From 7778ddc7a2d5b006edbfa69cdb44b8d8c24ec1ff Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 16 Oct 2013 13:22:55 -0400 Subject: [PATCH] Allow 5+ digit years for non-ISO timestamp/date strings, where appropriate Report from Haribabu Kommi --- src/backend/utils/adt/datetime.c | 49 ++++++++++++----------- src/test/regress/expected/timestamptz.out | 22 ++++++++++ src/test/regress/sql/timestamptz.sql | 14 +++++++ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index f39353f361..48bf3db9b2 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -1161,7 +1161,17 @@ DecodeDateTime(char **field, int *ftype, int nf, if (dterr < 0) return dterr; } - else if (flen > 4) + /* + * Is this a YMD or HMS specification, or a year number? + * YMD and HMS are required to be six digits or more, so + * if it is 5 digits, it is a year. If it is six or more + * more digits, we assume it is YMD or HMS unless no date + * and no time values have been specified. This forces + * 6+ digit years to be at the end of the string, or to use + * the ISO date specification. + */ + else if (flen >= 6 && (!(fmask & DTK_DATE_M) || + !(fmask & DTK_TIME_M))) { dterr = DecodeNumberField(flen, field[i], fmask, &tmask, tm, @@ -2647,29 +2657,20 @@ DecodeNumberField(int len, char *str, int fmask, /* No decimal point and no complete date yet? */ else if ((fmask & DTK_DATE_M) != DTK_DATE_M) { - /* yyyymmdd? */ - if (len == 8) + if (len >= 6) { *tmask = DTK_DATE_M; - - tm->tm_mday = atoi(str + 6); - *(str + 6) = '\0'; - tm->tm_mon = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_year = atoi(str + 0); - - return DTK_DATE; - } - /* yymmdd? */ - else if (len == 6) - { - *tmask = DTK_DATE_M; - tm->tm_mday = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_mon = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_year = atoi(str + 0); - *is2digits = TRUE; + /* + * Start from end and consider first 2 as Day, next 2 as Month, + * and the rest as Year. + */ + tm->tm_mday = atoi(str + (len - 2)); + *(str + (len - 2)) = '\0'; + tm->tm_mon = atoi(str + (len - 4)); + *(str + (len - 4)) = '\0'; + tm->tm_year = atoi(str); + if ((len - 4) == 2) + *is2digits = TRUE; return DTK_DATE; } @@ -2686,7 +2687,7 @@ DecodeNumberField(int len, char *str, int fmask, *(str + 4) = '\0'; tm->tm_min = atoi(str + 2); *(str + 2) = '\0'; - tm->tm_hour = atoi(str + 0); + tm->tm_hour = atoi(str); return DTK_TIME; } @@ -2697,7 +2698,7 @@ DecodeNumberField(int len, char *str, int fmask, tm->tm_sec = 0; tm->tm_min = atoi(str + 2); *(str + 2) = '\0'; - tm->tm_hour = atoi(str + 0); + tm->tm_hour = atoi(str); return DTK_TIME; } diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 6581b5e6a9..9f4f7a495a 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -1675,3 +1675,25 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') | 2001 1 1 1 1 1 1 (66 rows) +CREATE TABLE TIMESTAMPTZ_TST (a int , b timestamptz); +-- Test year field value with len > 4 +INSERT INTO TIMESTAMPTZ_TST VALUES(1, 'Sat Mar 12 23:58:48 1000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(2, 'Sat Mar 12 23:58:48 10000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(3, 'Sat Mar 12 23:58:48 100000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(3, '10000 Mar 12 23:58:48 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(4, '100000312 23:58:48 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(4, '1000000312 23:58:48 IST'); +--Verify data +SELECT * FROM TIMESTAMPTZ_TST ORDER BY a; + a | b +---+-------------------------------- + 1 | Wed Mar 12 13:58:48 1000 PST + 2 | Sun Mar 12 14:58:48 10000 PDT + 3 | Sun Mar 12 14:58:48 100000 PDT + 3 | Sun Mar 12 14:58:48 10000 PDT + 4 | Sun Mar 12 14:58:48 10000 PDT + 4 | Sun Mar 12 14:58:48 100000 PDT +(6 rows) + +--Cleanup +DROP TABLE TIMESTAMPTZ_TST; diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index 863b2865cb..4eef62e99a 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -240,3 +240,17 @@ SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') FROM TIMESTAMPTZ_TBL; + +CREATE TABLE TIMESTAMPTZ_TST (a int , b timestamptz); + +-- Test year field value with len > 4 +INSERT INTO TIMESTAMPTZ_TST VALUES(1, 'Sat Mar 12 23:58:48 1000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(2, 'Sat Mar 12 23:58:48 10000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(3, 'Sat Mar 12 23:58:48 100000 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(3, '10000 Mar 12 23:58:48 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(4, '100000312 23:58:48 IST'); +INSERT INTO TIMESTAMPTZ_TST VALUES(4, '1000000312 23:58:48 IST'); +--Verify data +SELECT * FROM TIMESTAMPTZ_TST ORDER BY a; +--Cleanup +DROP TABLE TIMESTAMPTZ_TST;