diff --git a/src/common/backend/utils/adt/timestamp.cpp b/src/common/backend/utils/adt/timestamp.cpp index 40c895064054e363fd984ddcde80a96b94b90c96..2af07fe37c9feb55595931c6452197462a37f56e 100644 --- a/src/common/backend/utils/adt/timestamp.cpp +++ b/src/common/backend/utils/adt/timestamp.cpp @@ -5089,6 +5089,7 @@ struct pg_tm* GetDateDetail(const char* dateString) strLength = strlen(dateString); + int decimalPointIndex = -1; for (i = 0; i < strLength; i++) { if (' ' == dateString[i]) { if (spaceCount >= 5) { @@ -5097,12 +5098,24 @@ struct pg_tm* GetDateDetail(const char* dateString) } spacePosition[spaceCount] = i; spaceCount++; + } else if ('.' == dateString[i]) { + if (decimalPointIndex != -1) { + pfree(tm); + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("the format is not correct"))); + } + decimalPointIndex = i; } } /* there is no space in the date-string*/ if (0 == spaceCount) { - /* assume that the string is this kind of fommat like "19900304123045"*/ - if (DATE_WITHOUT_SPC_LEN == strLength) { + /* + * assume that the string is this kind of fommat like "19900304123045", + * or like "19900304123045.345" with decimal format of second + */ +#define DECIMAL_POINT_INDEX_IN_NO_SPACE_INPUT 14 + if ((DATE_WITHOUT_SPC_LEN == strLength && decimalPointIndex == -1) + /* in condition like '19900304123045.345', make sure decimal point in position behind 'yyyymmddhhminss' */ + || (DATE_WITHOUT_SPC_LEN < strLength && decimalPointIndex == DECIMAL_POINT_INDEX_IN_NO_SPACE_INPUT)) { SplitWholeStrWithoutSeparator(dateString, tm); } /* there is only specific date in the string but no time information in the string*/ @@ -5119,7 +5132,8 @@ struct pg_tm* GetDateDetail(const char* dateString) errno_t rc = strncpy_s(dateStr, DATESTR_LEN, dateString, spacePosition[0]); securec_check(rc, "\0", "\0"); /* get the specific time*/ - rc = strncpy_s(timeStr, TIMESTR_LEN, dateString + spacePosition[0] + 1, strLength - spacePosition[0]); + int count = strLength - spacePosition[0] <= TIMESTR_LEN ? strLength - spacePosition[0] : TIMESTR_LEN - 1; + rc = strncpy_s(timeStr, TIMESTR_LEN, dateString + spacePosition[0] + 1, count); securec_check(rc, "\0", "\0"); AnalyseDate(dateStr, tm); @@ -5159,7 +5173,8 @@ void SplitWholeStrWithoutSeparator(const char* dateString, struct pg_tm* tm) strLength = strlen(dateString); for (i = 0; i < strLength; i++) { - if (dateString[i] < '0' || dateString[i] > '9') { + /* '.' will not count as nonDigit since we are now compatible with input format "19900304123045.345" */ + if ((dateString[i] < '0' || dateString[i] > '9') && dateString[i] != '.') { nonDigitCount++; } } @@ -5180,7 +5195,9 @@ void SplitWholeStrWithoutSeparator(const char* dateString, struct pg_tm* tm) rc = strncpy_s(minute, UNIT_LEN, dateString + FOUR_DIGIT_LEN + TWO_DIGIT_LEN * 3, TWO_DIGIT_LEN); securec_check(rc, "\0", "\0"); /* get second*/ - rc = strncpy_s(second, UNIT_LEN, dateString + FOUR_DIGIT_LEN + TWO_DIGIT_LEN * 4, TWO_DIGIT_LEN); + int secondLength = (int)strlen(dateString + FOUR_DIGIT_LEN + TWO_DIGIT_LEN * 4); + int count = UNIT_LEN <= secondLength ? UNIT_LEN - 1 : secondLength; + rc = strncpy_s(second, UNIT_LEN, dateString + FOUR_DIGIT_LEN + TWO_DIGIT_LEN * 4, count); securec_check(rc, "\0", "\0"); tm->tm_year = atoi(year); @@ -5188,7 +5205,7 @@ void SplitWholeStrWithoutSeparator(const char* dateString, struct pg_tm* tm) tm->tm_mday = atoi(day); tm->tm_hour = atoi(hour); tm->tm_min = atoi(minute); - tm->tm_sec = atoi(second); + tm->tm_sec = (int)round(atof(second)); } else { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the format is not correct"))); } @@ -5314,6 +5331,7 @@ void AnalyseTime(const char* timeString, struct pg_tm* tm_time) int i; int timeSeparatorCount = 0; + int decimalPointCount = 0; int timeSeparatorPosition[5] = {0}; if (NULL == timeString || NULL == tm_time) { @@ -5322,7 +5340,12 @@ void AnalyseTime(const char* timeString, struct pg_tm* tm_time) } strLength = strlen(timeString); for (i = 0; i < strLength; i++) { - if (timeString[i] < '0' || timeString[i] > '9') { + if (timeString[i] == '.') { + decimalPointCount++; + if (decimalPointCount > 1) { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("the format is not correct"))); + } + } else if (timeString[i] < '0' || timeString[i] > '9') { timeSeparatorPosition[timeSeparatorCount] = i; timeSeparatorCount++; if (timeSeparatorCount > 5) { @@ -5386,13 +5409,9 @@ void SplitTimestrBySeparator(const char* timeString, int strLength, const int* s ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the format is not correct"))); return; } - if (MINLEN_TIME > strLength || MAXLEN_TIME < strLength) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the time is not correct!"))); - } /* check the position of the oparator .1 and 2 are the posiible position of the first separator*/ - else if (1 != separatorPosition[0] && 2 != separatorPosition[0]) { + if (1 != separatorPosition[0] && 2 != separatorPosition[0]) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the hour is invalid!"))); - } /* 3 ,4 and 5 are the posiible position of the second separator*/ else if (3 != separatorPosition[1] && 4 != separatorPosition[1] && 5 != separatorPosition[1]) { @@ -5410,13 +5429,13 @@ void SplitTimestrBySeparator(const char* timeString, int strLength, const int* s errorno = strncpy_s(second, strLength - separatorPosition[1], timeString + separatorPosition[1] + 1, - strLength - separatorPosition[1] - 1); + strLength - separatorPosition[1] - 1 < UNIT_LEN ? strLength - separatorPosition[1] - 1 : UNIT_LEN); securec_check(errorno, "\0", "\0"); /* transfer char to int*/ tm_time->tm_hour = atoi(hour); tm_time->tm_min = atoi(minute); - tm_time->tm_sec = atoi(second); + tm_time->tm_sec = (int)round(atof(second)); } } diff --git a/src/test/regress/expected/hw_to_timestamp.out b/src/test/regress/expected/hw_to_timestamp.out index f8f9b7602b9b233c4d8a4b59a9a1570e4adff624..19f79cb0e4d2630a6e6a6d58290272123a5c94d5 100644 --- a/src/test/regress/expected/hw_to_timestamp.out +++ b/src/test/regress/expected/hw_to_timestamp.out @@ -1038,3 +1038,51 @@ CONTEXT: referenced column: to_timestamp SELECT to_timestamp(-999888762478); ERROR: timestamp out of range: "-9.99889e+11" CONTEXT: referenced column: to_timestamp +SELECT to_date('1992-01-01 12:34:56'); + to_date +--------------------- + 1992-01-01 12:34:56 +(1 row) + +SELECT to_date('1992-01-01 12:34:56.545612164556'); + to_date +--------------------- + 1992-01-01 12:34:57 +(1 row) + +SELECT to_date('1992-01-01 12:34:56.499999999999'); + to_date +--------------------- + 1992-01-01 12:34:56 +(1 row) + +SELECT to_date('1992-01-01 12:34:56.500000000000'); + to_date +--------------------- + 1992-01-01 12:34:57 +(1 row) + +SELECT to_date('19920101123456'); + to_date +--------------------- + 1992-01-01 12:34:56 +(1 row) + +SELECT to_date('19920101123456.5848954380543'); + to_date +--------------------- + 1992-01-01 12:34:57 +(1 row) + +SELECT to_date('19920101123456.5000000000000'); + to_date +--------------------- + 1992-01-01 12:34:57 +(1 row) + +SELECT to_date('19920101123456.4999999999999'); + to_date +--------------------- + 1992-01-01 12:34:56 +(1 row) + diff --git a/src/test/regress/sql/hw_to_timestamp.sql b/src/test/regress/sql/hw_to_timestamp.sql index 5b34f398a6f8631d0d2640ce0d555ef76097ca8b..e557ac6a0e3c32fb3a4b8b67929c4127f1c88d49 100644 --- a/src/test/regress/sql/hw_to_timestamp.sql +++ b/src/test/regress/sql/hw_to_timestamp.sql @@ -270,3 +270,12 @@ SELECT to_timestamp(' Infinity'::float); SELECT to_timestamp('-Infinity'::float); SELECT to_timestamp('NaN'::float); SELECT to_timestamp(-999888762478); + +SELECT to_date('1992-01-01 12:34:56'); +SELECT to_date('1992-01-01 12:34:56.545612164556'); +SELECT to_date('1992-01-01 12:34:56.499999999999'); +SELECT to_date('1992-01-01 12:34:56.500000000000'); +SELECT to_date('19920101123456'); +SELECT to_date('19920101123456.5848954380543'); +SELECT to_date('19920101123456.5000000000000'); +SELECT to_date('19920101123456.4999999999999'); \ No newline at end of file