diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index a1cf2b4ed5..9640684cb1 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.192 2008/09/11 15:27:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.193 2008/09/16 22:31:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -448,7 +448,7 @@ TrimTrailingZeros(char *str) * DTK_TIME - digits, colon delimiters, and possibly a decimal point * DTK_STRING - text (no digits or punctuation) * DTK_SPECIAL - leading "+" or "-" followed by text - * DTK_TZ - leading "+" or "-" followed by digits (also eats ':' or '.') + * DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-') * * Note that some field types can hold unexpected items: * DTK_NUMBER can hold date fields (yy.ddd) @@ -610,12 +610,13 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen, while (isspace((unsigned char) *cp)) cp++; /* numeric timezone? */ + /* note that "DTK_TZ" could also be a signed float or yyyy-mm */ if (isdigit((unsigned char) *cp)) { ftype[nf] = DTK_TZ; APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsigned char) *cp) || - *cp == ':' || *cp == '.') + *cp == ':' || *cp == '.' || *cp == '-') APPEND_CHAR(bufp, bufend, *cp++); } /* special? */ @@ -2774,19 +2775,17 @@ DecodeInterval(char **field, int *ftype, int nf, int range, /* * Timezone is a token with a leading sign character and - * otherwise the same as a non-signed time field + * at least one digit; there could be ':', '.', '-' + * embedded in it as well. */ Assert(*field[i] == '-' || *field[i] == '+'); /* - * A single signed number ends up here, but will be rejected - * by DecodeTime(). So, work this out to drop through to - * DTK_NUMBER, which *can* tolerate this. + * Try for hh:mm or hh:mm:ss. If not, fall through to + * DTK_NUMBER case, which can handle signed float numbers + * and signed year-month values. */ - cp = field[i] + 1; - while (*cp != '\0' && *cp != ':' && *cp != '.') - cp++; - if (*cp == ':' && + if (strchr(field[i] + 1, ':') != NULL && DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE, &tmask, tm, fsec) == 0) { @@ -2808,32 +2807,13 @@ DecodeInterval(char **field, int *ftype, int nf, int range, tmask = DTK_M(TZ); break; } - else if (type == IGNORE_DTF) - { - if (*cp == '.') - { - /* - * Got a decimal point? Then assume some sort of - * seconds specification - */ - type = DTK_SECOND; - } - else if (*cp == '\0') - { - /* - * Only a signed integer? Then must assume a - * timezone-like usage - */ - type = DTK_HOUR; - } - } /* FALL THROUGH */ case DTK_DATE: case DTK_NUMBER: if (type == IGNORE_DTF) { - /* use typmod to decide what rightmost integer field is */ + /* use typmod to decide what rightmost field is */ switch (range) { case INTERVAL_MASK(YEAR): @@ -2883,6 +2863,8 @@ DecodeInterval(char **field, int *ftype, int nf, int range, if (*cp != '\0') return DTERR_BAD_FORMAT; type = DTK_MONTH; + if (val < 0) + val2 = -val2; val = val * MONTHS_PER_YEAR + val2; fval = 0; } diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index a9b5766439..8270516388 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -21,12 +21,6 @@ SELECT INTERVAL '-08:00' AS "Eight hours"; -08:00:00 (1 row) -SELECT INTERVAL '-05' AS "Five hours"; - Five hours ------------- - -05:00:00 -(1 row) - SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; 22 hours ago... ------------------- diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 06e4705859..732ca026f9 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -8,7 +8,6 @@ SET DATESTYLE = 'ISO'; SELECT INTERVAL '01:00' AS "One hour"; SELECT INTERVAL '+02:00' AS "Two hours"; SELECT INTERVAL '-08:00' AS "Eight hours"; -SELECT INTERVAL '-05' AS "Five hours"; SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours";