diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/utils/adt/datetime.c | 192 |
1 files changed, 93 insertions, 99 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index f8b82bac8a3..4440a3c477a 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.160.2.4 2008/01/02 22:05:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.160.2.5 2008/02/25 23:21:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,7 +37,10 @@ static int DecodeTime(char *str, int fmask, int *tmask, static int DecodeTimezone(char *str, int *tzp); static int DecodePosixTimezone(char *str, int *tzp); static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); -static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm); +static int DecodeDate(char *str, int fmask, int *tmask, int *is2digits, + struct pg_tm * tm); +static int ValidateDate(int fmask, int is2digits, int bc, + struct pg_tm * tm); static void TrimTrailingZeros(char *str); @@ -1099,7 +1102,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; } @@ -1311,7 +1315,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; } @@ -1536,51 +1541,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) @@ -1598,14 +1566,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; - /* timezone not specified? then find local timezone if possible */ if (tzp != NULL && !(fmask & DTK_M(TZ))) { @@ -1775,6 +1735,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, int val; int dterr; int is2digits = FALSE; + int bc = FALSE; int mer = HR24; *dtype = DTK_TIME; @@ -1805,7 +1766,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; } @@ -2076,7 +2038,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; } @@ -2205,6 +2168,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf, mer = val; break; + case ADBC: + bc = (val == BC); + break; + case UNITS: tmask = 0; ptype = val; @@ -2240,8 +2207,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) @@ -2300,10 +2273,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, int *is2digits, + struct pg_tm * tm) { fsec_t fsec; int nf = 0; @@ -2311,13 +2289,13 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm) len; int dterr; bool haveTextMonth = FALSE; - int bc = FALSE; - int is2digits = FALSE; int type, val, dmask = 0; char *field[MAXDATEFIELDS]; + *tmask = 0; + /* parse this string... */ while (*str != '\0' && nf < MAXDATEFIELDS) { @@ -2343,14 +2321,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++) { @@ -2368,10 +2338,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; } @@ -2397,7 +2363,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; @@ -2411,23 +2377,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, int is2digits, int 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 */ @@ -2438,16 +2419,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; } |