diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 0bfdd0ec2d6fdf945b1328236147a3c158359a51..d8fbe0a30771862ccf4b57b89b4087df5cdc1203 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.185 2008/02/17 02:09:28 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.186 2008/02/25 23:21:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,7 +40,10 @@ static int DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec); static int DecodeTimezone(char *str, int *tzp); static const datetkn *datebsearch(const char *key, const datetkn *base, int nel); -static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm); +static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, + struct pg_tm * tm); +static int ValidateDate(int fmask, bool is2digits, bool bc, + struct pg_tm * tm); static void TrimTrailingZeros(char *str); @@ -805,7 +808,8 @@ DecodeDateTime(char **field, int *ftype, int nf, } else { - dterr = DecodeDate(field[i], fmask, &tmask, tm); + dterr = DecodeDate(field[i], fmask, + &tmask, &is2digits, tm); if (dterr) return dterr; } @@ -1000,7 +1004,8 @@ DecodeDateTime(char **field, int *ftype, int nf, /* Embedded decimal and no date yet? */ if (cp != NULL && !(fmask & DTK_DATE_M)) { - dterr = DecodeDate(field[i], fmask, &tmask, tm); + dterr = DecodeDate(field[i], fmask, + &tmask, &is2digits, tm); if (dterr) return dterr; } @@ -1234,51 +1239,14 @@ DecodeDateTime(char **field, int *ftype, int nf, if (tmask & fmask) return DTERR_BAD_FORMAT; fmask |= tmask; - } - - if (fmask & DTK_M(YEAR)) - { - /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ - if (bc) - { - if (tm->tm_year > 0) - tm->tm_year = -(tm->tm_year - 1); - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_DATETIME_FORMAT), - errmsg("inconsistent use of year %04d and \"BC\"", - tm->tm_year))); - } - else if (is2digits) - { - if (tm->tm_year < 70) - tm->tm_year += 2000; - else if (tm->tm_year < 100) - tm->tm_year += 1900; - } - } - - /* now that we have correct year, decode DOY */ - if (fmask & DTK_M(DOY)) - { - j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1, - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - } - - /* check for valid month */ - if (fmask & DTK_M(MONTH)) - { - if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR) - return DTERR_MD_FIELD_OVERFLOW; - } + } /* end loop over fields */ - /* minimal check for valid day */ - if (fmask & DTK_M(DAY)) - { - if (tm->tm_mday < 1 || tm->tm_mday > 31) - return DTERR_MD_FIELD_OVERFLOW; - } + /* do final checking/adjustment of Y/M/D fields */ + dterr = ValidateDate(fmask, is2digits, bc, tm); + if (dterr) + return dterr; + /* handle AM/PM */ if (mer != HR24 && tm->tm_hour > 12) return DTERR_FIELD_OVERFLOW; if (mer == AM && tm->tm_hour == 12) @@ -1296,14 +1264,6 @@ DecodeDateTime(char **field, int *ftype, int nf, return DTERR_BAD_FORMAT; } - /* - * Check for valid day of month, now that we know for sure the month - * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems - * unlikely that "Feb 29" is a YMD-order error. - */ - if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) - return DTERR_FIELD_OVERFLOW; - /* * If we had a full timezone spec, compute the offset (we could not do * it before, because we need the date to resolve DST status). @@ -1486,6 +1446,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, int val; int dterr; bool is2digits = FALSE; + bool bc = FALSE; int mer = HR24; pg_tz *namedTz = NULL; @@ -1517,7 +1478,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf, if (i == 0 && nf >= 2 && (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME)) { - dterr = DecodeDate(field[i], fmask, &tmask, tm); + dterr = DecodeDate(field[i], fmask, + &tmask, &is2digits, tm); if (dterr) return dterr; } @@ -1783,7 +1745,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf, */ if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE) { - dterr = DecodeDate(field[i], fmask, &tmask, tm); + dterr = DecodeDate(field[i], fmask, + &tmask, &is2digits, tm); if (dterr) return dterr; } @@ -1912,6 +1875,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf, mer = val; break; + case ADBC: + bc = (val == BC); + break; + case UNITS: tmask = 0; ptype = val; @@ -1960,8 +1927,14 @@ DecodeTimeOnly(char **field, int *ftype, int nf, if (tmask & fmask) return DTERR_BAD_FORMAT; fmask |= tmask; - } + } /* end loop over fields */ + /* do final checking/adjustment of Y/M/D fields */ + dterr = ValidateDate(fmask, is2digits, bc, tm); + if (dterr) + return dterr; + + /* handle AM/PM */ if (mer != HR24 && tm->tm_hour > 12) return DTERR_FIELD_OVERFLOW; if (mer == AM && tm->tm_hour == 12) @@ -2047,10 +2020,15 @@ DecodeTimeOnly(char **field, int *ftype, int nf, * Decode date string which includes delimiters. * Return 0 if okay, a DTERR code if not. * - * Insist on a complete set of fields. + * str: field to be parsed + * fmask: bitmask for field types already seen + * *tmask: receives bitmask for fields found here + * *is2digits: set to TRUE if we find 2-digit year + * *tm: field values are stored into appropriate members of this struct */ static int -DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) +DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, + struct pg_tm * tm) { fsec_t fsec; int nf = 0; @@ -2058,13 +2036,13 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) len; int dterr; bool haveTextMonth = FALSE; - bool bc = FALSE; - bool is2digits = FALSE; int type, val, dmask = 0; char *field[MAXDATEFIELDS]; + *tmask = 0; + /* parse this string... */ while (*str != '\0' && nf < MAXDATEFIELDS) { @@ -2090,14 +2068,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) nf++; } -#if 0 - /* don't allow too many fields */ - if (nf > 3) - return DTERR_BAD_FORMAT; -#endif - - *tmask = 0; - /* look first for text fields, since that will be unambiguous month */ for (i = 0; i < nf; i++) { @@ -2115,10 +2085,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) haveTextMonth = TRUE; break; - case ADBC: - bc = (val == BC); - break; - default: return DTERR_BAD_FORMAT; } @@ -2144,7 +2110,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) dterr = DecodeNumber(len, field[i], haveTextMonth, fmask, &dmask, tm, - &fsec, &is2digits); + &fsec, is2digits); if (dterr) return dterr; @@ -2158,23 +2124,38 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M) return DTERR_BAD_FORMAT; - /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ - if (bc) - { - if (tm->tm_year > 0) - tm->tm_year = -(tm->tm_year - 1); - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_DATETIME_FORMAT), - errmsg("inconsistent use of year %04d and \"BC\"", - tm->tm_year))); - } - else if (is2digits) + /* validation of the field values must wait until ValidateDate() */ + + return 0; +} + +/* ValidateDate() + * Check valid year/month/day values, handle BC and DOY cases + * Return 0 if okay, a DTERR code if not. + */ +static int +ValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm) +{ + if (fmask & DTK_M(YEAR)) { - if (tm->tm_year < 70) - tm->tm_year += 2000; - else if (tm->tm_year < 100) - tm->tm_year += 1900; + /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ + if (bc) + { + if (tm->tm_year > 0) + tm->tm_year = -(tm->tm_year - 1); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_DATETIME_FORMAT), + errmsg("inconsistent use of year %04d and \"BC\"", + tm->tm_year))); + } + else if (is2digits) + { + if (tm->tm_year < 70) + tm->tm_year += 2000; + else if (tm->tm_year < 100) + tm->tm_year += 1900; + } } /* now that we have correct year, decode DOY */ @@ -2185,16 +2166,29 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) } /* check for valid month */ - if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR) - return DTERR_MD_FIELD_OVERFLOW; + if (fmask & DTK_M(MONTH)) + { + if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR) + return DTERR_MD_FIELD_OVERFLOW; + } - /* check for valid day */ - if (tm->tm_mday < 1 || tm->tm_mday > 31) - return DTERR_MD_FIELD_OVERFLOW; + /* minimal check for valid day */ + if (fmask & DTK_M(DAY)) + { + if (tm->tm_mday < 1 || tm->tm_mday > 31) + return DTERR_MD_FIELD_OVERFLOW; + } - /* We don't want to hint about DateStyle for Feb 29 */ - if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) - return DTERR_FIELD_OVERFLOW; + if ((fmask & DTK_DATE_M) == DTK_DATE_M) + { + /* + * Check for valid day of month, now that we know for sure the month + * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems + * unlikely that "Feb 29" is a YMD-order error. + */ + if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) + return DTERR_FIELD_OVERFLOW; + } return 0; }