2 PostgreSQL Data Base Management System (formerly known as Postgres, then
5 Copyright (c) 1994-7 Regents of the University of California
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose, without fee, and without a written agreement
9 is hereby granted, provided that the above copyright notice and this
10 paragraph and the following two paragraphs appear in all copies.
12 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
13 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
16 POSSIBILITY OF SUCH DAMAGE.
18 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
22 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 /*-------------------------------------------------------------------------
28 * Functions for the built-in type "dt".
30 * Copyright (c) 1994, Regents of the University of California
33 *-------------------------------------------------------------------------
40 #include <sys/types.h>
42 #include <sys/timeb.h>
46 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
47 static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
48 static int DecodeNumber(int flen, char *field,
49 int fmask, int *tmask, struct tm * tm, double *fsec);
50 static int DecodeNumberField(int len, char *str,
51 int fmask, int *tmask, struct tm * tm, double *fsec);
52 static int DecodeSpecial(int field, char *lowtoken, int *val);
53 static int DecodeTime(char *str, int fmask, int *tmask,
54 struct tm * tm, double *fsec);
55 static int DecodeTimezone(char *str, int *tzp);
57 #define USE_DATE_CACHE 1
60 /* those two vars are useless, and not even initialized, so
61 * I'd rather remove them all (EPP)
66 #define UTIME_MINYEAR (1901)
67 #define UTIME_MINMONTH (12)
68 #define UTIME_MINDAY (14)
69 #define UTIME_MAXYEAR (2038)
70 #define UTIME_MAXMONTH (01)
71 #define UTIME_MAXDAY (18)
73 /* Assumes month in 1..12. Note tm_mon is 0..11 */
74 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
75 || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
76 || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
77 && ((y < UTIME_MAXYEAR) \
78 || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
79 || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
84 /*****************************************************************************
86 *****************************************************************************/
88 /* definitions for squeezing values into "value" */
89 #define ABS_SIGNBIT (char) 0200
90 #define VALMASK (char) 0177
91 #define NEG(n) ((n)|ABS_SIGNBIT)
92 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
93 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
94 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
97 * to keep this table reasonably small, we divide the lexval for TZ and DTZ
98 * entries by 10 and truncate the text field at MAXTOKLEN characters.
99 * the text field is not guaranteed to be NULL-terminated.
101 static datetkn datetktbl[] = {
102 /* text token lexval */
103 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
104 {"acsst", DTZ, 63}, /* Cent. Australia */
105 {"acst", TZ, 57}, /* Cent. Australia */
106 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
107 {"abstime", IGNOREFIELD, 0}, /* "abstime" for pre-v6.1 "Invalid
109 {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
110 {"aesst", DTZ, 66}, /* E. Australia */
111 {"aest", TZ, 60}, /* Australia Eastern Std Time */
112 {"ahst", TZ, 60}, /* Alaska-Hawaii Std Time */
113 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
117 {"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */
118 {"at", IGNOREFIELD, 0}, /* "at" (throwaway) */
120 {"august", MONTH, 8},
121 {"awsst", DTZ, 54}, /* W. Australia */
122 {"awst", TZ, 48}, /* W. Australia */
123 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
124 {"bst", TZ, 6}, /* British Summer Time */
125 {"bt", TZ, 18}, /* Baghdad Time */
126 {"cadt", DTZ, 63}, /* Central Australian DST */
127 {"cast", TZ, 57}, /* Central Australian ST */
128 {"cat", TZ, NEG(60)}, /* Central Alaska Time */
129 {"cct", TZ, 48}, /* China Coast */
130 {"cdt", DTZ, NEG(30)}, /* Central Daylight Time */
131 {"cet", TZ, 6}, /* Central European Time */
132 {"cetdst", DTZ, 12}, /* Central European Dayl.Time */
133 {"cst", TZ, NEG(36)}, /* Central Standard Time */
134 {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
136 {"december", MONTH, 12},
137 {"dnt", TZ, 6}, /* Dansk Normal Tid */
138 {"dow", RESERV, DTK_DOW}, /* day of week */
139 {"doy", RESERV, DTK_DOY}, /* day of year */
141 {"east", TZ, NEG(60)}, /* East Australian Std Time */
142 {"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */
143 {"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */
144 {"eetdst", DTZ, 18}, /* Eastern Europe */
145 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
146 #if USE_AUSTRALIAN_RULES
147 {"est", TZ, 60}, /* Australia Eastern Std Time */
149 {"est", TZ, NEG(30)}, /* Eastern Standard Time */
152 {"february", MONTH, 2},
155 {"fst", TZ, 6}, /* French Summer Time */
156 {"fwt", DTZ, 12}, /* French Winter Time */
157 {"gmt", TZ, 0}, /* Greenwish Mean Time */
158 {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
159 {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
160 {"hmt", DTZ, 18}, /* Hellas ? ? */
161 {"hst", TZ, NEG(60)}, /* Hawaii Std Time */
162 {"idle", TZ, 72}, /* Intl. Date Line, East */
163 {"idlw", TZ, NEG(72)}, /* Intl. Date Line,, est */
164 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
165 {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for invalid
167 {"ist", TZ, 12}, /* Israel */
168 {"it", TZ, 22}, /* Iran Time */
170 {"january", MONTH, 1},
171 {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
172 {"jt", TZ, 45}, /* Java Time */
177 {"kst", TZ, 54}, /* Korea Standard Time */
178 {"ligt", TZ, 60}, /* From Melbourne, Australia */
182 {"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */
183 {"mest", DTZ, 12}, /* Middle Europe Summer Time */
184 {"met", TZ, 6}, /* Middle Europe Time */
185 {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */
186 {"mewt", TZ, 6}, /* Middle Europe Winter Time */
187 {"mez", TZ, 6}, /* Middle Europe Zone */
190 {"mst", TZ, NEG(42)}, /* Mountain Standard Time */
191 {"mt", TZ, 51}, /* Moluccas Time */
192 {"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */
193 {"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */
194 {"nor", TZ, 6}, /* Norway Standard Time */
196 {"november", MONTH, 11},
197 {NOW, RESERV, DTK_NOW}, /* current transaction time */
198 {"nst", TZ, NEG(21)}, /* Nfld. Standard Time */
199 {"nt", TZ, NEG(66)}, /* Nome Time */
200 {"nzdt", DTZ, 78}, /* New Zealand Daylight Time */
201 {"nzst", TZ, 72}, /* New Zealand Standard Time */
202 {"nzt", TZ, 72}, /* New Zealand Time */
204 {"october", MONTH, 10},
205 {"on", IGNOREFIELD, 0}, /* "on" (throwaway) */
206 {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
208 {"pst", TZ, NEG(48)}, /* Pacific Standard Time */
209 {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
210 {"sast", TZ, 57}, /* South Australian Std Time */
212 {"saturday", DOW, 6},
215 {"september", MONTH, 9},
216 {"set", TZ, NEG(6)}, /* Seychelles Time ?? */
217 {"sst", DTZ, 12}, /* Swedish Summer Time */
220 {"swt", TZ, 6}, /* Swedish Winter Time */
224 {"thursday", DOW, 4},
225 {TODAY, RESERV, DTK_TODAY}, /* midnight */
226 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
230 {"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
234 {"wadt", DTZ, 48}, /* West Australian DST */
235 {"wast", TZ, 42}, /* West Australian Std Time */
236 {"wat", TZ, NEG(6)}, /* West Africa Time */
237 {"wdt", DTZ, 54}, /* West Australian DST */
239 {"wednesday", DOW, 3},
241 {"wet", TZ, 0}, /* Western Europe */
242 {"wetdst", DTZ, 6}, /* Western Europe */
243 {"wst", TZ, 48}, /* West Australian Std Time */
244 {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
245 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
246 {"yst", TZ, NEG(54)}, /* Yukon Standard Time */
247 {"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
248 {"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
249 {"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
250 {"z", RESERV, DTK_ZULU}, /* 00:00:00 */
251 {ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
254 static unsigned int szdatetktbl = sizeof(datetktbl) / sizeof(datetktbl[0]);
259 datetkn *datecache[MAXDATEFIELDS] = {NULL};
261 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
267 * Calendar time to Julian date conversions.
268 * Julian date is commonly used in astronomical applications,
269 * since it is numerically accurate and computationally simple.
270 * The algorithms here will accurately convert between Julian day
271 * and calendar date for all non-negative Julian days
272 * (i.e. from Nov 23, -4713 on).
274 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
275 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
277 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
278 * now at Aerospace Corp. (hi, Henry!)
280 * These routines will be used by other date/time packages - tgl 97/02/25
283 /* Set the minimum year to one greater than the year of the first valid day
284 * to avoid having to check year and day both. - tgl 97/05/08
287 #define JULIAN_MINYEAR (-4713)
288 #define JULIAN_MINMONTH (11)
289 #define JULIAN_MINDAY (23)
291 #define IS_VALID_JULIAN(y,m,d) ((y > JULIAN_MINYEAR) \
292 || ((y == JULIAN_MINYEAR) && ((m > JULIAN_MINMONTH) \
293 || ((m == JULIAN_MINMONTH) && (d >= JULIAN_MINDAY)))))
296 date2j(int y, int m, int d)
298 int m12 = (m - 14) / 12;
300 return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
301 - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
305 j2date(int jd, int *year, int *month, int *day)
317 n = (4 * l) / 146097;
318 l -= (146097 * n + 3) / 4;
319 i = (4000 * (l + 1)) / 1461001;
320 l += 31 - (1461 * i) / 4;
322 d = l - (2447 * j) / 80;
324 m = (j + 2) - (12 * l);
325 y = 100 * (n - 49) + i + l;
337 * parse and convert date in timestr (the normal interface)
339 * Returns the number of seconds since epoch (J2000)
343 * Break string into tokens based on a date/time context.
346 ParseDateTime(char *timestr, char *lowstr,
347 char **field, int *ftype, int maxfields, int *numfields)
354 printf("ParseDateTime- input string is %s\n", timestr);
356 /* outer loop through fields */
361 /* leading digit? then date or time */
362 if (isdigit(*cp) || (*cp == '.'))
370 ftype[nf] = DTK_TIME;
371 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
375 /* date field? allow embedded text month */
376 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
378 ftype[nf] = DTK_DATE;
379 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
380 *lp++ = tolower(*cp++);
385 * otherwise, number only and will determine year, month, or
389 ftype[nf] = DTK_NUMBER;
394 * text? then date string, month, day of week, special, or
397 else if (isalpha(*cp))
399 ftype[nf] = DTK_STRING;
400 *lp++ = tolower(*cp++);
402 *lp++ = tolower(*cp++);
404 /* full date string with leading text month? */
405 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
407 ftype[nf] = DTK_DATE;
408 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
409 *lp++ = tolower(*cp++);
412 /* skip leading spaces */
414 else if (isspace(*cp))
419 /* sign? then special or numeric timezone */
421 else if ((*cp == '+') || (*cp == '-'))
424 /* soak up leading whitespace */
427 /* numeric timezone? */
432 while (isdigit(*cp) || (*cp == ':'))
437 else if (isalpha(*cp))
439 ftype[nf] = DTK_SPECIAL;
440 *lp++ = tolower(*cp++);
442 *lp++ = tolower(*cp++);
444 /* otherwise something wrong... */
449 /* ignore punctuation but use as delimiter */
451 else if (ispunct(*cp))
460 /* force in a delimiter */
463 if (nf > MAXDATEFIELDS)
466 printf("ParseDateTime- set field[%d] to %s type %d\n", (nf - 1), field[nf - 1], ftype[nf - 1]);
473 } /* ParseDateTime() */
477 * Interpret previously parsed fields for general date and time.
478 * Return 0 if full date, 1 if only time, and -1 if problems.
479 * External format(s):
480 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
481 * "Fri Feb-7-1997 15:23:27"
482 * "Feb-7-1997 15:23:27"
483 * "2-7-1997 15:23:27"
484 * "1997-2-7 15:23:27"
485 * "1997.038 15:23:27" (day of year 1-366)
486 * Also supports input in compact time:
490 * Use the system-provided functions to get the current time zone
491 * if not specified in the input string.
492 * If the date is outside the time_t system-supported time range,
493 * then assume GMT time zone. - tgl 97/05/27
496 DecodeDateTime(char **field, int *ftype, int nf,
497 int *dtype, struct tm * tm, double *fsec, int *tzp)
513 tm->tm_isdst = -1; /* don't know daylight savings time status
518 for (i = 0; i < nf; i++)
521 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
526 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
531 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
535 * check upper limit on hours; other limits checked in
538 if (tm->tm_hour > 23)
545 if (DecodeTimezone(field[i], tzp) != 0)
551 flen = strlen(field[i]);
555 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
561 if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
568 type = DecodeSpecial(i, field[i], &val);
570 printf("DecodeDateTime- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
572 if (type == IGNOREFIELD)
580 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
593 printf("DecodeDateTime- month field %s value is %d\n", field[i], val);
595 /* tm_mon is 0->11, so need to subtract one from value in table */
600 * daylight savings time modifier (solves "MET
614 * set mask for TZ here _or_ check for DTZ later
615 * when getting default timezone
656 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
657 i, field[i], fmask, tmask, val);
665 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
667 tm->tm_year = -(tm->tm_year - 1);
669 if ((mer != HR24) && (tm->tm_hour > 12))
671 if ((mer == AM) && (tm->tm_hour == 12))
673 else if ((mer == PM) && (tm->tm_hour != 12))
676 /* If parsing a time string into a date, all date parts are unset.
677 Win2k defaults these to 30 dec, 1899 so: */
678 if (tm->tm_year == 0 && tm->tm_mon == 0 && tm->tm_mday == 0 && fmask == DTK_TIME_M) {
680 tm->tm_mon = 11; /* December, as tm_mon is 0..11 */
686 printf("DecodeDateTime- mask %08x (%08x)", fmask, DTK_DATE_M);
687 printf(" set y%04d m%02d d%02d", tm->tm_year, (tm->tm_mon+1), tm->tm_mday);
688 printf(" %02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
691 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
692 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
694 /* timezone not specified? then find local timezone if possible */
695 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) == DTK_DATE_M)
696 && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
700 * daylight savings time modifier but no standard timezone? then
703 if (fmask & DTK_M(DTZMOD))
706 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon+1, tm->tm_mday))
708 /* FIXME: The code below is not correct */
709 #if 0 /* defined(USE_POSIX_TIME) */
717 #if 0 /* defined(HAVE_INT_TIMEZONE) */
718 *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
720 #else /* !HAVE_INT_TIMEZONE */
721 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
724 #else /* !USE_POSIX_TIME */
736 } /* DecodeDateTime() */
740 * Interpret parsed string as time fields only.
743 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
757 tm->tm_isdst = -1; /* don't know daylight savings time status
763 for (i = 0; i < nf; i++)
766 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
771 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
776 flen = strlen(field[i]);
778 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
784 type = DecodeSpecial(i, field[i], &val);
786 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
788 if (type == IGNOREFIELD)
796 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
828 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
833 printf("DecodeTimeOnly- mask %08x (%08x)", fmask, DTK_TIME_M);
834 printf(" %02d:%02d:%02d (%f)\n", tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec);
837 if ((mer != HR24) && (tm->tm_hour > 12))
839 if ((mer == AM) && (tm->tm_hour == 12))
841 else if ((mer == PM) && (tm->tm_hour != 12))
844 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
848 } /* DecodeTimeOnly() */
852 * Decode date string which includes delimiters.
853 * Insist on a complete set of fields.
856 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
866 char *field[MAXDATEFIELDS];
868 /* parse this string... */
869 while ((*str != '\0') && (nf < MAXDATEFIELDS))
871 /* skip field separators */
872 while (!isalnum(*str))
878 while (isdigit(*str))
881 else if (isalpha(*str))
883 while (isalpha(*str))
892 /* don't allow too many fields */
898 /* look first for text fields, since that will be unambiguous month */
899 for (i = 0; i < nf; i++)
901 if (isalpha(*field[i]))
903 type = DecodeSpecial(i, field[i], &val);
904 if (type == IGNOREFIELD)
912 printf("DecodeDate- month field %s value is %d\n", field[i], val);
914 /* tm_mon is 0->11, so need to subtract one from value in table */
920 printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
930 /* mark this field as being completed */
935 /* now pick up remaining numeric fields */
936 for (i = 0; i < nf; i++)
938 if (field[i] == NULL)
941 if ((len = strlen(field[i])) <= 0)
944 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
959 * Decode time string which includes delimiters.
960 * Only check the lower limit on hours, since this same code
961 * can be used to represent time spans.
964 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
970 tm->tm_hour = strtol(str, &cp, 10);
974 tm->tm_min = strtol(str, &cp, 10);
989 tm->tm_sec = strtol(str, &cp, 10);
995 *fsec = strtod(str, &cp);
1003 /* do a sanity check */
1004 if ((tm->tm_hour < 0)
1005 || (tm->tm_min < 0) || (tm->tm_min > 59)
1006 || (tm->tm_sec < 0) || (tm->tm_sec > 59))
1010 } /* DecodeTime() */
1014 * Interpret numeric field as a date value in context.
1017 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1024 val = strtol(str, &cp, 10);
1029 *fsec = strtod(cp, &cp);
1035 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1038 /* enough digits to be unequivocal year? */
1042 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1044 *tmask = DTK_M(YEAR);
1046 /* already have a year? then see if we can substitute... */
1047 if (fmask & DTK_M(YEAR))
1049 if ((!(fmask & DTK_M(DAY)))
1050 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1053 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm->tm_mday);
1055 tm->tm_mday = tm->tm_year;
1056 *tmask = DTK_M(DAY);
1062 /* special case day of year? */
1064 else if ((flen == 3) && (fmask & DTK_M(YEAR))
1065 && ((val >= 1) && (val <= 366)))
1067 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1069 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
1070 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1072 /* already have year? then could be month */
1074 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1075 && ((val >= 1) && (val <= 12)))
1078 printf("DecodeNumber- match %d (%s) as month\n", val, str);
1080 *tmask = DTK_M(MONTH);
1081 /* tm_mon is 0..11 */
1084 /* no year and EuroDates enabled? then could be day */
1086 else if ((EuroDates || (fmask & DTK_M(MONTH)))
1087 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1088 && ((val >= 1) && (val <= 31)))
1091 printf("DecodeNumber- match %d (%s) as day\n", val, str);
1093 *tmask = DTK_M(DAY);
1097 else if ((!(fmask & DTK_M(MONTH)))
1098 && ((val >= 1) && (val <= 12)))
1101 printf("DecodeNumber- (2) match %d (%s) as month\n", val, str);
1103 *tmask = DTK_M(MONTH);
1104 /* tm_mon is 0..11 */
1108 else if ((!(fmask & DTK_M(DAY)))
1109 && ((val >= 1) && (val <= 31)))
1112 printf("DecodeNumber- (2) match %d (%s) as day\n", val, str);
1114 *tmask = DTK_M(DAY);
1118 else if (!(fmask & DTK_M(YEAR)))
1121 printf("DecodeNumber- (2) match %d (%s) as year\n", val, str);
1123 *tmask = DTK_M(YEAR);
1125 if (tm->tm_year < 70)
1126 tm->tm_year += 2000;
1127 else if (tm->tm_year < 100)
1128 tm->tm_year += 1900;
1135 } /* DecodeNumber() */
1138 /* DecodeNumberField()
1139 * Interpret numeric string as a concatenated date field.
1142 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1150 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1153 *tmask = DTK_DATE_M;
1155 tm->tm_mday = atoi(str + 6);
1157 tm->tm_mon = atoi(str + 4) - 1; /* tm_mon is 0..11 */
1159 tm->tm_year = atoi(str + 0);
1161 /* yymmdd or hhmmss? */
1166 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1168 if (fmask & DTK_DATE_M)
1171 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1173 *tmask = DTK_TIME_M;
1174 tm->tm_sec = atoi(str + 4);
1176 tm->tm_min = atoi(str + 2);
1178 tm->tm_hour = atoi(str + 0);
1184 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1186 *tmask = DTK_DATE_M;
1187 tm->tm_mday = atoi(str + 4);
1189 tm->tm_mon = atoi(str + 2) - 1; /* tm_mon is 0..11 */
1191 tm->tm_year = atoi(str + 0);
1195 else if (strchr(str, '.') != NULL)
1198 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1200 *tmask = DTK_TIME_M;
1201 tm->tm_sec = strtod((str + 4), &cp);
1202 if (cp == (str + 4))
1205 *fsec = strtod(cp, NULL);
1207 tm->tm_min = strtod((str + 2), &cp);
1209 tm->tm_hour = strtod((str + 0), &cp);
1216 } /* DecodeNumberField() */
1220 * Interpret string as a numeric timezone.
1223 DecodeTimezone(char *str, int *tzp)
1231 /* assume leading character is "+" or "-" */
1232 hr = strtol((str + 1), &cp, 10);
1234 /* explicit delimiter? */
1237 min = strtol((cp + 1), &cp, 10);
1239 /* otherwise, might have run things together... */
1241 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1243 min = strtol((str + len - 2), &cp, 10);
1244 *(str + len - 2) = '\0';
1245 hr = strtol((str + 1), &cp, 10);
1251 tz = (hr * 60 + min) * 60;
1257 } /* DecodeTimezone() */
1261 * Decode text string using lookup table.
1262 * Implement a cache lookup since it is likely that dates
1263 * will be related in format.
1266 DecodeSpecial(int field, char *lowtoken, int *val)
1272 if ((datecache[field] != NULL)
1273 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1274 tp = datecache[field];
1278 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1281 datecache[field] = tp;
1306 } /* DecodeSpecial() */
1311 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1312 * is WAY faster than the generic bsearch().
1315 datebsearch(char *key, datetkn *base, unsigned int nel)
1317 datetkn *last = base + nel - 1,
1321 while (last >= base)
1323 position = base + ((last - base) >> 1);
1324 result = key[0] - position->token[0];
1327 result = strncmp(key, position->token, TOKMAXLEN);
1332 last = position - 1;
1334 base = position + 1;