diff options
Diffstat (limited to 'src/backend/utils/adt/datetime.c')
-rw-r--r-- | src/backend/utils/adt/datetime.c | 210 |
1 files changed, 168 insertions, 42 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 7095f24de73..28ca77b64ea 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.66 2001/07/10 01:41:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.67 2001/09/28 08:09:10 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -29,13 +29,13 @@ #define ROUND_ALL 1 static int DecodeNumber(int flen, char *field, - int fmask, int *tmask, - struct tm * tm, double *fsec, int *is2digits); + int fmask, int *tmask, + struct tm * tm, double *fsec, int *is2digits); static int DecodeNumberField(int len, char *str, - int fmask, int *tmask, - struct tm * tm, double *fsec, int *is2digits); + int fmask, int *tmask, + struct tm * tm, double *fsec, int *is2digits); static int DecodeTime(char *str, int fmask, int *tmask, - struct tm * tm, double *fsec); + struct tm * tm, double *fsec); static int DecodeTimezone(char *str, int *tzp); static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm); @@ -47,10 +47,10 @@ int day_tab[2][13] = { {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}}; char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", -"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", -"Thursday", "Friday", "Saturday", NULL}; + "Thursday", "Friday", "Saturday", NULL}; /***************************************************************************** @@ -71,7 +71,7 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", * the text field is not guaranteed to be NULL-terminated. */ static datetkn datetktbl[] = { -/* text token lexval */ +/* text, token, lexval */ {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ {"acsst", DTZ, 63}, /* Cent. Australia */ {"acst", TZ, 57}, /* Cent. Australia */ @@ -104,6 +104,7 @@ static datetkn datetktbl[] = { {"cetdst", DTZ, 12}, /* Central European Dayl.Time */ {"cst", TZ, NEG(36)}, /* Central Standard Time */ {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ + {"d", UNITS, DAY}, /* "day of month" for ISO input */ {"dec", MONTH, 12}, {"december", MONTH, 12}, {"dnt", TZ, 6}, /* Dansk Normal Tid */ @@ -124,6 +125,7 @@ static datetkn datetktbl[] = { {"fwt", DTZ, 12}, /* French Winter Time */ {"gmt", TZ, 0}, /* Greenwish Mean Time */ {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */ + {"h", UNITS, HOUR}, /* "hour" */ {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */ {"hmt", DTZ, 18}, /* Hellas ? ? */ {"hst", TZ, NEG(60)}, /* Hawaii Std Time */ @@ -134,16 +136,19 @@ static datetkn datetktbl[] = { /* "invalid" reserved for invalid time */ {"ist", TZ, 12}, /* Israel */ {"it", TZ, 21}, /* Iran Time */ + {"j", UNITS, JULIAN}, {"jan", MONTH, 1}, {"january", MONTH, 1}, + {"jd", UNITS, JULIAN}, {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ {"jt", TZ, 45}, /* Java Time */ {"jul", MONTH, 7}, - {"july", MONTH, 7}, + {"julian", UNITS, JULIAN}, {"jun", MONTH, 6}, {"june", MONTH, 6}, {"kst", TZ, 54}, /* Korea Standard Time */ {"ligt", TZ, 60}, /* From Melbourne, Australia */ + {"m", UNITS, MONTH}, /* "month" for ISO input */ {"mar", MONTH, 3}, {"march", MONTH, 3}, {"may", MONTH, 5}, @@ -153,6 +158,7 @@ static datetkn datetktbl[] = { {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */ {"mewt", TZ, 6}, /* Middle Europe Winter Time */ {"mez", TZ, 6}, /* Middle Europe Zone */ + {"mm", UNITS, MINUTE}, /* "minute" for ISO input */ {"mon", DOW, 1}, {"monday", DOW, 1}, {"mst", TZ, NEG(42)}, /* Mountain Standard Time */ @@ -174,6 +180,7 @@ static datetkn datetktbl[] = { {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */ {"pm", AMPM, PM}, {"pst", TZ, NEG(48)}, /* Pacific Standard Time */ + {"s", UNITS, SECOND}, /* "seconds" for ISO input */ {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */ {"sast", TZ, 57}, /* South Australian Std Time */ {"sat", DOW, 6}, @@ -186,6 +193,7 @@ static datetkn datetktbl[] = { {"sun", DOW, 0}, {"sunday", DOW, 0}, {"swt", TZ, 6}, /* Swedish Winter Time */ + {"t", DTK_ISO_TIME, 0}, /* Filler for ISO time fields */ {"thu", DOW, 4}, {"thur", DOW, 4}, {"thurs", DOW, 4}, @@ -208,6 +216,7 @@ static datetkn datetktbl[] = { {"wet", TZ, 0}, /* Western Europe */ {"wetdst", DTZ, 6}, /* Western Europe */ {"wst", TZ, 48}, /* West Australian Std Time */ + {"y", UNITS, YEAR}, /* "year" for ISO input */ {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */ {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */ {"yst", TZ, NEG(54)}, /* Yukon Standard Time */ @@ -222,7 +231,7 @@ static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; /* Used for SET australian_timezones to override North American ones */ static datetkn australian_datetktbl[] = { - {"cst", TZ, 63}, /* Australia Eastern Std Time */ + {"cst", TZ, 63}, /* Australia Central Std Time */ {"est", TZ, 60}, /* Australia Eastern Std Time */ {"sat", TZ, 57}, }; @@ -231,7 +240,7 @@ static unsigned int australian_szdatetktbl = sizeof australian_datetktbl / sizeof australian_datetktbl[0]; static datetkn deltatktbl[] = { -/* text token lexval */ +/* text, token, lexval */ {"@", IGNORE, 0}, /* postgres relative time prefix */ {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ {"c", UNITS, DTK_CENTURY}, /* "century" relative time units */ @@ -329,7 +338,8 @@ datetkn *deltacache[MAXDATEFIELDS] = {NULL}; * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague * now at Aerospace Corp. (hi, Henry!) * - * These routines will be used by other date/time packages - tgl 97/02/25 + * These routines will be used by other date/time packages + * - thomas 97/02/25 */ int @@ -413,6 +423,7 @@ ParseDateTime(char *timestr, char *lowstr, if (*cp == ':') { ftype[nf] = DTK_TIME; + *lp++ = *cp++; while (isdigit((unsigned char) *cp) || (*cp == ':') || (*cp == '.')) *lp++ = *cp++; @@ -422,10 +433,20 @@ ParseDateTime(char *timestr, char *lowstr, else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) { ftype[nf] = DTK_DATE; - while (isalnum((unsigned char) *cp) || (*cp == '-') || - (*cp == '/') || (*cp == '.')) - *lp++ = tolower((unsigned char) *cp++); - + *lp++ = *cp++; + /* second field is all digits? then no embedded text month */ + if (isdigit((unsigned char) *cp)) + { + while (isdigit((unsigned char) *cp) || (*cp == '-') || + (*cp == '/') || (*cp == '.')) + *lp++ = *cp++; + } + else + { + while (isalnum((unsigned char) *cp) || (*cp == '-') || + (*cp == '/') || (*cp == '.')) + *lp++ = tolower((unsigned char) *cp++); + } } /* @@ -539,7 +560,7 @@ ParseDateTime(char *timestr, char *lowstr, * Use the system-provided functions to get the current time zone * if not specified in the input string. * If the date is outside the time_t system-supported time range, - * then assume GMT time zone. - tgl 97/05/27 + * then assume GMT time zone. - thomas 1997/05/27 */ int DecodeDateTime(char **field, int *ftype, int nf, @@ -548,6 +569,7 @@ DecodeDateTime(char **field, int *ftype, int nf, int fmask = 0, tmask, type; + int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */ int i; int flen, val; @@ -556,13 +578,16 @@ DecodeDateTime(char **field, int *ftype, int nf, int is2digits = FALSE; int bc = FALSE; + /* We'll insist on at least all of the date fields, + * but initialize the remaining fields in case they are not + * set later... + */ *dtype = DTK_DATE; tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; *fsec = 0; - tm->tm_isdst = -1; /* don't know daylight savings time status - * apriori */ + tm->tm_isdst = -1; /* don't know daylight savings time status apriori */ if (tzp != NULL) *tzp = 0; @@ -571,13 +596,32 @@ DecodeDateTime(char **field, int *ftype, int nf, switch (ftype[i]) { case DTK_DATE: + /* Previous field was a label for "julian date"? + * then this should be a julian date with fractional day... + */ + if (ptype == JULIAN) + { + char *cp; + double dt, date, time; - /* - * Already have a date? Then this might be a POSIX time - * zone with an embedded dash (e.g. "PST-3" == "EST") - - * thomas 2000-03-15 + dt = strtod(field[i], &cp); + if (*cp != '\0') + return -1; + + time = dt * 86400; + TMODULO(time, date, 86400e0); + j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + dt2time(time, &tm->tm_hour, &tm->tm_min, fsec); + + tmask = DTK_DATE_M | DTK_TIME_M; + *dtype = DTK_DATE; + } + + /* Already have a date? Then this might be a POSIX time + * zone with an embedded dash (e.g. "PST-3" == "EST") + * - thomas 2000-03-15 */ - if ((fmask & DTK_DATE_M) == DTK_DATE_M) + else if ((fmask & DTK_DATE_M) == DTK_DATE_M) { if ((tzp == NULL) || (DecodePosixTimezone(field[i], tzp) != 0)) @@ -587,15 +631,16 @@ DecodeDateTime(char **field, int *ftype, int nf, tmask = DTK_M(TZ); } else if (DecodeDate(field[i], fmask, &tmask, tm) != 0) + { return -1; + } break; case DTK_TIME: if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) return -1; - /* - * check upper limit on hours; other limits checked in + /* Check upper limit on hours; other limits checked in * DecodeTime() */ if (tm->tm_hour > 23) @@ -618,7 +663,8 @@ DecodeDateTime(char **field, int *ftype, int nf, * PST) */ if ((i > 0) && ((fmask & DTK_M(TZ)) != 0) - && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1]))) + && (ftype[i - 1] == DTK_TZ) + && (isalpha((unsigned char) *field[i - 1]))) { *tzp -= tz; tmask = 0; @@ -634,21 +680,81 @@ DecodeDateTime(char **field, int *ftype, int nf, case DTK_NUMBER: flen = strlen(field[i]); + /* Was this an "ISO date" with embedded field labels? + * An example is "y2001m02d04" - thomas 2001-02-04 + */ + if (ptype != 0) + { + char *cp; + int val; + + val = strtol(field[i], &cp, 10); + if (*cp != '\0') + return -1; + + switch (ptype) { + case YEAR: + tm->tm_year = val; + tmask = DTK_M(ptype); + break; + + case MONTH: + tm->tm_mon = val; + tmask = DTK_M(ptype); + break; + + case DAY: + tm->tm_mday = val; + tmask = DTK_M(ptype); + break; + + case HOUR: + tm->tm_hour = val; + tmask = DTK_M(ptype); + break; + + case MINUTE: + tm->tm_min = val; + tmask = DTK_M(ptype); + break; + + case SECOND: + tm->tm_sec = val; + tmask = DTK_M(ptype); + break; + + case JULIAN: + /* previous field was a label for "julian date"? + * then this is a julian day with no fractional part + * (see DTK_DATE for cases involving fractional parts) + */ + j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + + tmask = DTK_DATE_M; + break; + + default: + return -1; + break; + } + + ptype = 0; + *dtype = DTK_DATE; + } /* * long numeric string and either no date or no time read * yet? then interpret as a concatenated date or time... */ - if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) + else if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) { if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) return -1; } /* otherwise it is a single date/time field... */ - else + else if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) { - if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) - return -1; + return -1; } break; @@ -664,10 +770,15 @@ DecodeDateTime(char **field, int *ftype, int nf, case RESERV: switch (val) { + case DTK_CURRENT: case DTK_NOW: tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ)); *dtype = DTK_DATE; +#if NOT_USED GetCurrentTime(tm); +#else + GetCurrentTimeUsec(tm, fsec); +#endif if (tzp != NULL) *tzp = CTimeZone; break; @@ -786,6 +897,18 @@ DecodeDateTime(char **field, int *ftype, int nf, tm->tm_wday = val; break; + case UNITS: + ptype = val; + tmask = 0; + break; + + case DTK_ISO_TIME: + if ((i < 1) || (i >= (nf-1)) + || (ftype[i-1] != DTK_DATE) + || (ftype[i+1] != DTK_TIME)) + return -1; + break; + default: return -1; } @@ -1182,6 +1305,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm) str++; } + /* Just get rid of any non-digit, non-alpha characters... */ if (*str != '\0') *str++ = '\0'; nf++; @@ -1362,8 +1486,9 @@ DecodeNumber(int flen, char *str, int fmask, /* * Enough digits to be unequivocal year? Used to test for 4 digits or * more, but we now test first for a three-digit doy so anything - * bigger than two digits had better be an explicit year. - thomas - * 1999-01-09 Back to requiring a 4 digit year. We accept a two digit + * bigger than two digits had better be an explicit year. + * - thomas 1999-01-09 + * Back to requiring a 4 digit year. We accept a two digit * year farther down. - thomas 2000-03-28 */ else if (flen >= 4) @@ -1613,7 +1738,7 @@ DecodeSpecial(int field, char *lowtoken, int *val) datecache[field] = tp; if (tp == NULL) { - type = IGNORE; + type = UNKNOWN_FIELD; *val = 0; } else @@ -1747,10 +1872,11 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do case DTK_NUMBER: val = strtol(field[i], &cp, 10); + if (type == IGNORE) + type = DTK_SECOND; + if (*cp == '.') { - if (type == IGNORE) - type = DTK_SECOND; fval = strtod(cp, &cp); if (*cp != '\0') return -1; @@ -1928,7 +2054,7 @@ DecodeUnits(int field, char *lowtoken, int *val) deltacache[field] = tp; if (tp == NULL) { - type = IGNORE; + type = UNKNOWN_FIELD; *val = 0; } else @@ -1985,8 +2111,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) switch (style) { - /* compatible with ISO date formats */ case USE_ISO_DATES: + /* compatible with ISO date formats */ if (tm->tm_year > 0) sprintf(str, "%04d-%02d-%02d", tm->tm_year, tm->tm_mon, tm->tm_mday); @@ -1995,8 +2121,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC"); break; - /* compatible with Oracle/Ingres date formats */ case USE_SQL_DATES: + /* compatible with Oracle/Ingres date formats */ if (EuroDates) sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon); else @@ -2007,8 +2133,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC"); break; - /* German-style date format */ case USE_GERMAN_DATES: + /* German-style date format */ sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon); if (tm->tm_year > 0) sprintf((str + 5), ".%04d", tm->tm_year); @@ -2016,9 +2142,9 @@ EncodeDateOnly(struct tm * tm, int style, char *str) sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC"); break; - /* traditional date-only style for Postgres */ case USE_POSTGRES_DATES: default: + /* traditional date-only style for Postgres */ if (EuroDates) sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon); else |