Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b73c0c2

Browse files
committed
Clean up a couple of weird corner cases in interval parsing: make -yyyy-mm be
interpreted as expected (the sign should affect months too), and get rid of hard-wired assumption that unmarked signed values must be hours (if integers) or seconds (if floats). The former was just a bug in my previous patch, while the latter may have made sense at one time but seems illogical now that we support determination of the units from typmod information. Ron Mayer and myself.
1 parent 30df79a commit b73c0c2

File tree

3 files changed

+13
-38
lines changed

3 files changed

+13
-38
lines changed

src/backend/utils/adt/datetime.c

+13-31
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.192 2008/09/11 15:27:30 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.193 2008/09/16 22:31:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -448,7 +448,7 @@ TrimTrailingZeros(char *str)
448448
* DTK_TIME - digits, colon delimiters, and possibly a decimal point
449449
* DTK_STRING - text (no digits or punctuation)
450450
* DTK_SPECIAL - leading "+" or "-" followed by text
451-
* DTK_TZ - leading "+" or "-" followed by digits (also eats ':' or '.')
451+
* DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
452452
*
453453
* Note that some field types can hold unexpected items:
454454
* DTK_NUMBER can hold date fields (yy.ddd)
@@ -610,12 +610,13 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
610610
while (isspace((unsigned char) *cp))
611611
cp++;
612612
/* numeric timezone? */
613+
/* note that "DTK_TZ" could also be a signed float or yyyy-mm */
613614
if (isdigit((unsigned char) *cp))
614615
{
615616
ftype[nf] = DTK_TZ;
616617
APPEND_CHAR(bufp, bufend, *cp++);
617618
while (isdigit((unsigned char) *cp) ||
618-
*cp == ':' || *cp == '.')
619+
*cp == ':' || *cp == '.' || *cp == '-')
619620
APPEND_CHAR(bufp, bufend, *cp++);
620621
}
621622
/* special? */
@@ -2774,19 +2775,17 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
27742775

27752776
/*
27762777
* Timezone is a token with a leading sign character and
2777-
* otherwise the same as a non-signed time field
2778+
* at least one digit; there could be ':', '.', '-'
2779+
* embedded in it as well.
27782780
*/
27792781
Assert(*field[i] == '-' || *field[i] == '+');
27802782

27812783
/*
2782-
* A single signed number ends up here, but will be rejected
2783-
* by DecodeTime(). So, work this out to drop through to
2784-
* DTK_NUMBER, which *can* tolerate this.
2784+
* Try for hh:mm or hh:mm:ss. If not, fall through to
2785+
* DTK_NUMBER case, which can handle signed float numbers
2786+
* and signed year-month values.
27852787
*/
2786-
cp = field[i] + 1;
2787-
while (*cp != '\0' && *cp != ':' && *cp != '.')
2788-
cp++;
2789-
if (*cp == ':' &&
2788+
if (strchr(field[i] + 1, ':') != NULL &&
27902789
DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
27912790
&tmask, tm, fsec) == 0)
27922791
{
@@ -2808,32 +2807,13 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
28082807
tmask = DTK_M(TZ);
28092808
break;
28102809
}
2811-
else if (type == IGNORE_DTF)
2812-
{
2813-
if (*cp == '.')
2814-
{
2815-
/*
2816-
* Got a decimal point? Then assume some sort of
2817-
* seconds specification
2818-
*/
2819-
type = DTK_SECOND;
2820-
}
2821-
else if (*cp == '\0')
2822-
{
2823-
/*
2824-
* Only a signed integer? Then must assume a
2825-
* timezone-like usage
2826-
*/
2827-
type = DTK_HOUR;
2828-
}
2829-
}
28302810
/* FALL THROUGH */
28312811

28322812
case DTK_DATE:
28332813
case DTK_NUMBER:
28342814
if (type == IGNORE_DTF)
28352815
{
2836-
/* use typmod to decide what rightmost integer field is */
2816+
/* use typmod to decide what rightmost field is */
28372817
switch (range)
28382818
{
28392819
case INTERVAL_MASK(YEAR):
@@ -2883,6 +2863,8 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
28832863
if (*cp != '\0')
28842864
return DTERR_BAD_FORMAT;
28852865
type = DTK_MONTH;
2866+
if (val < 0)
2867+
val2 = -val2;
28862868
val = val * MONTHS_PER_YEAR + val2;
28872869
fval = 0;
28882870
}

src/test/regress/expected/interval.out

-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ SELECT INTERVAL '-08:00' AS "Eight hours";
2121
-08:00:00
2222
(1 row)
2323

24-
SELECT INTERVAL '-05' AS "Five hours";
25-
Five hours
26-
------------
27-
-05:00:00
28-
(1 row)
29-
3024
SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
3125
22 hours ago...
3226
-------------------

src/test/regress/sql/interval.sql

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ SET DATESTYLE = 'ISO';
88
SELECT INTERVAL '01:00' AS "One hour";
99
SELECT INTERVAL '+02:00' AS "Two hours";
1010
SELECT INTERVAL '-08:00' AS "Eight hours";
11-
SELECT INTERVAL '-05' AS "Five hours";
1211
SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
1312
SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
1413
SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours";

0 commit comments

Comments
 (0)