Skip to content

Commit 1a908a0

Browse files
committed
Fix datetime input parsing to accept YYYY-MONTHNAME-DD and related syntaxes,
which had been unintentionally broken by recent changes to tighten up the DateStyle rules for all-numeric date input. Add documentation and regression tests for this, too.
1 parent 9ad53b0 commit 1a908a0

File tree

4 files changed

+928
-20
lines changed

4 files changed

+928
-20
lines changed

doc/src/sgml/datatype.sgml

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.130 2003/11/06 22:21:47 tgl Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.131 2003/11/16 20:29:16 tgl Exp $
33
-->
44

55
<chapter id="datatype">
@@ -1464,7 +1464,7 @@ SELECT b, char_length(b) FROM test2;
14641464
</row>
14651465
<row>
14661466
<entry>1999-01-08</entry>
1467-
<entry>ISO 8601, January 8 in any mode
1467+
<entry>ISO 8601; January 8 in any mode
14681468
(recommended format)</entry>
14691469
</row>
14701470
<row>
@@ -1484,6 +1484,30 @@ SELECT b, char_length(b) FROM test2;
14841484
February 3, 2001 in <literal>YMD</> mode
14851485
</entry>
14861486
</row>
1487+
<row>
1488+
<entry>1999-Jan-08</entry>
1489+
<entry>January 8 in any mode</entry>
1490+
</row>
1491+
<row>
1492+
<entry>Jan-08-1999</entry>
1493+
<entry>January 8 in any mode</entry>
1494+
</row>
1495+
<row>
1496+
<entry>08-Jan-1999</entry>
1497+
<entry>January 8 in any mode</entry>
1498+
</row>
1499+
<row>
1500+
<entry>99-Jan-08</entry>
1501+
<entry>January 8 in <literal>YMD</> mode, else error</entry>
1502+
</row>
1503+
<row>
1504+
<entry>08-Jan-99</entry>
1505+
<entry>January 8, except error in <literal>YMD</> mode</entry>
1506+
</row>
1507+
<row>
1508+
<entry>Jan-08-99</entry>
1509+
<entry>January 8, except error in <literal>YMD</> mode</entry>
1510+
</row>
14871511
<row>
14881512
<entry>19990108</entry>
14891513
<entry>ISO 8601; January 8, 1999 in any mode</entry>
@@ -1625,7 +1649,7 @@ SELECT b, char_length(b) FROM test2;
16251649
</row>
16261650
<row>
16271651
<entry><literal>zulu</literal></entry>
1628-
<entry>Military abbreviation for GMT</entry>
1652+
<entry>Military abbreviation for UTC</entry>
16291653
</row>
16301654
<row>
16311655
<entry><literal>z</literal></entry>

src/backend/utils/adt/datetime.c

+64-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.118 2003/09/25 06:58:03 petere Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.119 2003/11/16 20:29:16 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -25,7 +25,7 @@
2525
#include "utils/guc.h"
2626

2727

28-
static int DecodeNumber(int flen, char *field,
28+
static int DecodeNumber(int flen, char *field, bool haveTextMonth,
2929
int fmask, int *tmask,
3030
struct tm * tm, fsec_t *fsec, int *is2digits);
3131
static int DecodeNumberField(int len, char *str,
@@ -924,7 +924,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
924924
int val;
925925
int dterr;
926926
int mer = HR24;
927-
int haveTextMonth = FALSE;
927+
bool haveTextMonth = FALSE;
928928
int is2digits = FALSE;
929929
int bc = FALSE;
930930

@@ -1281,7 +1281,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
12811281
/* otherwise it is a single date/time field... */
12821282
else
12831283
{
1284-
dterr = DecodeNumber(flen, field[i], fmask,
1284+
dterr = DecodeNumber(flen, field[i],
1285+
haveTextMonth, fmask,
12851286
&tmask, tm,
12861287
fsec, &is2digits);
12871288
if (dterr)
@@ -2032,6 +2033,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
20322033
else
20332034
{
20342035
dterr = DecodeNumber(flen, field[i],
2036+
FALSE,
20352037
(fmask | DTK_DATE_M),
20362038
&tmask, tm,
20372039
fsec, &is2digits);
@@ -2229,6 +2231,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
22292231
int i,
22302232
len;
22312233
int dterr;
2234+
bool haveTextMonth = FALSE;
22322235
int bc = FALSE;
22332236
int is2digits = FALSE;
22342237
int type,
@@ -2283,6 +2286,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
22832286
{
22842287
case MONTH:
22852288
tm->tm_mon = val;
2289+
haveTextMonth = TRUE;
22862290
break;
22872291

22882292
case ADBC:
@@ -2312,7 +2316,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
23122316
if ((len = strlen(field[i])) <= 0)
23132317
return DTERR_BAD_FORMAT;
23142318

2315-
dterr = DecodeNumber(len, field[i], fmask,
2319+
dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
23162320
&dmask, tm,
23172321
&fsec, &is2digits);
23182322
if (dterr)
@@ -2444,7 +2448,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
24442448
* Return 0 if okay, a DTERR code if not.
24452449
*/
24462450
static int
2447-
DecodeNumber(int flen, char *str, int fmask,
2451+
DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
24482452
int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
24492453
{
24502454
int val;
@@ -2534,10 +2538,59 @@ DecodeNumber(int flen, char *str, int fmask,
25342538
tm->tm_mon = val;
25352539
break;
25362540

2541+
case (DTK_M(MONTH)):
2542+
if (haveTextMonth)
2543+
{
2544+
/*
2545+
* We are at the first numeric field of a date that included
2546+
* a textual month name. We want to support the variants
2547+
* MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2548+
* inputs. We will also accept MON-DD-YY or DD-MON-YY in
2549+
* either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2550+
*/
2551+
if (flen >= 3 || DateOrder == DATEORDER_YMD)
2552+
{
2553+
*tmask = DTK_M(YEAR);
2554+
tm->tm_year = val;
2555+
}
2556+
else
2557+
{
2558+
*tmask = DTK_M(DAY);
2559+
tm->tm_mday = val;
2560+
}
2561+
}
2562+
else
2563+
{
2564+
/* Must be at second field of MM-DD-YY */
2565+
*tmask = DTK_M(DAY);
2566+
tm->tm_mday = val;
2567+
}
2568+
break;
2569+
25372570
case (DTK_M(YEAR) | DTK_M(MONTH)):
2538-
/* Must be at third field of YY-MM-DD */
2539-
*tmask = DTK_M(DAY);
2540-
tm->tm_mday = val;
2571+
if (haveTextMonth)
2572+
{
2573+
/* Need to accept DD-MON-YYYY even in YMD mode */
2574+
if (flen >= 3 && *is2digits)
2575+
{
2576+
/* Guess that first numeric field is day was wrong */
2577+
*tmask = DTK_M(DAY); /* YEAR is already set */
2578+
tm->tm_mday = tm->tm_year;
2579+
tm->tm_year = val;
2580+
*is2digits = FALSE;
2581+
}
2582+
else
2583+
{
2584+
*tmask = DTK_M(DAY);
2585+
tm->tm_mday = val;
2586+
}
2587+
}
2588+
else
2589+
{
2590+
/* Must be at third field of YY-MM-DD */
2591+
*tmask = DTK_M(DAY);
2592+
tm->tm_mday = val;
2593+
}
25412594
break;
25422595

25432596
case (DTK_M(DAY)):
@@ -2552,12 +2605,6 @@ DecodeNumber(int flen, char *str, int fmask,
25522605
tm->tm_year = val;
25532606
break;
25542607

2555-
case (DTK_M(MONTH)):
2556-
/* Must be at second field of MM-DD-YY */
2557-
*tmask = DTK_M(DAY);
2558-
tm->tm_mday = val;
2559-
break;
2560-
25612608
case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
25622609
/* we have all the date, so it must be a time field */
25632610
dterr = DecodeNumberField(flen, str, fmask,
@@ -2574,10 +2621,10 @@ DecodeNumber(int flen, char *str, int fmask,
25742621

25752622
/*
25762623
* When processing a year field, mark it for adjustment if it's
2577-
* exactly two digits.
2624+
* only one or two digits.
25782625
*/
25792626
if (*tmask == DTK_M(YEAR))
2580-
*is2digits = (flen == 2);
2627+
*is2digits = (flen <= 2);
25812628

25822629
return 0;
25832630
}

0 commit comments

Comments
 (0)