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

Lines changed: 27 additions & 3 deletions
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

Lines changed: 64 additions & 17 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy