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 static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
62 static const char * const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
65 static const char * const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
66 "Thursday", "Friday", "Saturday", NULL};
68 /* those three vars are useless, and not even initialized, so
69 * I'd rather remove them all (EPP)
75 #define UTIME_MINYEAR (1901)
76 #define UTIME_MINMONTH (12)
77 #define UTIME_MINDAY (14)
78 #define UTIME_MAXYEAR (2038)
79 #define UTIME_MAXMONTH (01)
80 #define UTIME_MAXDAY (18)
82 /* Assumes month in 1..12. Note tm_mon is 0..11 */
83 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
84 || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
85 || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
86 && ((y < UTIME_MAXYEAR) \
87 || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
88 || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
93 /*****************************************************************************
95 *****************************************************************************/
97 /* definitions for squeezing values into "value" */
98 #define ABS_SIGNBIT (char) 0200
99 #define VALMASK (char) 0177
100 #define NEG(n) ((n)|ABS_SIGNBIT)
101 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
102 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
103 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
106 * to keep this table reasonably small, we divide the lexval for TZ and DTZ
107 * entries by 10 and truncate the text field at MAXTOKLEN characters.
108 * the text field is not guaranteed to be NULL-terminated.
110 static datetkn datetktbl[] = {
111 /* text token lexval */
112 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
113 {"acsst", DTZ, 63}, /* Cent. Australia */
114 {"acst", TZ, 57}, /* Cent. Australia */
115 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
116 {"abstime", IGNOREFIELD, 0}, /* "abstime" for pre-v6.1 "Invalid
118 {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
119 {"aesst", DTZ, 66}, /* E. Australia */
120 {"aest", TZ, 60}, /* Australia Eastern Std Time */
121 {"ahst", TZ, 60}, /* Alaska-Hawaii Std Time */
122 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
126 {"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */
127 {"at", IGNOREFIELD, 0}, /* "at" (throwaway) */
129 {"august", MONTH, 8},
130 {"awsst", DTZ, 54}, /* W. Australia */
131 {"awst", TZ, 48}, /* W. Australia */
132 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
133 {"bst", TZ, 6}, /* British Summer Time */
134 {"bt", TZ, 18}, /* Baghdad Time */
135 {"cadt", DTZ, 63}, /* Central Australian DST */
136 {"cast", TZ, 57}, /* Central Australian ST */
137 {"cat", TZ, NEG(60)}, /* Central Alaska Time */
138 {"cct", TZ, 48}, /* China Coast */
139 {"cdt", DTZ, NEG(30)}, /* Central Daylight Time */
140 {"cet", TZ, 6}, /* Central European Time */
141 {"cetdst", DTZ, 12}, /* Central European Dayl.Time */
142 {"cst", TZ, NEG(36)}, /* Central Standard Time */
143 {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
145 {"december", MONTH, 12},
146 {"dnt", TZ, 6}, /* Dansk Normal Tid */
147 {"dow", RESERV, DTK_DOW}, /* day of week */
148 {"doy", RESERV, DTK_DOY}, /* day of year */
150 {"east", TZ, NEG(60)}, /* East Australian Std Time */
151 {"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */
152 {"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */
153 {"eetdst", DTZ, 18}, /* Eastern Europe */
154 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
155 #if USE_AUSTRALIAN_RULES
156 {"est", TZ, 60}, /* Australia Eastern Std Time */
158 {"est", TZ, NEG(30)}, /* Eastern Standard Time */
161 {"february", MONTH, 2},
164 {"fst", TZ, 6}, /* French Summer Time */
165 {"fwt", DTZ, 12}, /* French Winter Time */
166 {"gmt", TZ, 0}, /* Greenwish Mean Time */
167 {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
168 {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
169 {"hmt", DTZ, 18}, /* Hellas ? ? */
170 {"hst", TZ, NEG(60)}, /* Hawaii Std Time */
171 {"idle", TZ, 72}, /* Intl. Date Line, East */
172 {"idlw", TZ, NEG(72)}, /* Intl. Date Line,, est */
173 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
174 {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for invalid
176 {"ist", TZ, 12}, /* Israel */
177 {"it", TZ, 22}, /* Iran Time */
179 {"january", MONTH, 1},
180 {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
181 {"jt", TZ, 45}, /* Java Time */
186 {"kst", TZ, 54}, /* Korea Standard Time */
187 {"ligt", TZ, 60}, /* From Melbourne, Australia */
191 {"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */
192 {"mest", DTZ, 12}, /* Middle Europe Summer Time */
193 {"met", TZ, 6}, /* Middle Europe Time */
194 {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */
195 {"mewt", TZ, 6}, /* Middle Europe Winter Time */
196 {"mez", TZ, 6}, /* Middle Europe Zone */
199 {"mst", TZ, NEG(42)}, /* Mountain Standard Time */
200 {"mt", TZ, 51}, /* Moluccas Time */
201 {"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */
202 {"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */
203 {"nor", TZ, 6}, /* Norway Standard Time */
205 {"november", MONTH, 11},
206 {NOW, RESERV, DTK_NOW}, /* current transaction time */
207 {"nst", TZ, NEG(21)}, /* Nfld. Standard Time */
208 {"nt", TZ, NEG(66)}, /* Nome Time */
209 {"nzdt", DTZ, 78}, /* New Zealand Daylight Time */
210 {"nzst", TZ, 72}, /* New Zealand Standard Time */
211 {"nzt", TZ, 72}, /* New Zealand Time */
213 {"october", MONTH, 10},
214 {"on", IGNOREFIELD, 0}, /* "on" (throwaway) */
215 {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
217 {"pst", TZ, NEG(48)}, /* Pacific Standard Time */
218 {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
219 {"sast", TZ, 57}, /* South Australian Std Time */
221 {"saturday", DOW, 6},
224 {"september", MONTH, 9},
225 {"set", TZ, NEG(6)}, /* Seychelles Time ?? */
226 {"sst", DTZ, 12}, /* Swedish Summer Time */
229 {"swt", TZ, 6}, /* Swedish Winter Time */
233 {"thursday", DOW, 4},
234 {TODAY, RESERV, DTK_TODAY}, /* midnight */
235 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
239 {"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
243 {"wadt", DTZ, 48}, /* West Australian DST */
244 {"wast", TZ, 42}, /* West Australian Std Time */
245 {"wat", TZ, NEG(6)}, /* West Africa Time */
246 {"wdt", DTZ, 54}, /* West Australian DST */
248 {"wednesday", DOW, 3},
250 {"wet", TZ, 0}, /* Western Europe */
251 {"wetdst", DTZ, 6}, /* Western Europe */
252 {"wst", TZ, 48}, /* West Australian Std Time */
253 {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
254 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
255 {"yst", TZ, NEG(54)}, /* Yukon Standard Time */
256 {"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
257 {"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
258 {"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
259 {"z", RESERV, DTK_ZULU}, /* 00:00:00 */
260 {ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
263 static unsigned int szdatetktbl = sizeof(datetktbl) / sizeof(datetktbl[0]);
268 datetkn *datecache[MAXDATEFIELDS] = {NULL};
270 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
276 * Calendar time to Julian date conversions.
277 * Julian date is commonly used in astronomical applications,
278 * since it is numerically accurate and computationally simple.
279 * The algorithms here will accurately convert between Julian day
280 * and calendar date for all non-negative Julian days
281 * (i.e. from Nov 23, -4713 on).
283 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
284 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
286 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
287 * now at Aerospace Corp. (hi, Henry!)
289 * These routines will be used by other date/time packages - tgl 97/02/25
292 /* Set the minimum year to one greater than the year of the first valid day
293 * to avoid having to check year and day both. - tgl 97/05/08
296 #define JULIAN_MINYEAR (-4713)
297 #define JULIAN_MINMONTH (11)
298 #define JULIAN_MINDAY (23)
300 #define IS_VALID_JULIAN(y,m,d) ((y > JULIAN_MINYEAR) \
301 || ((y == JULIAN_MINYEAR) && ((m > JULIAN_MINMONTH) \
302 || ((m == JULIAN_MINMONTH) && (d >= JULIAN_MINDAY)))))
305 date2j(int y, int m, int d)
307 int m12 = (m - 14) / 12;
309 return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
310 - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
314 j2date(int jd, int *year, int *month, int *day)
326 n = (4 * l) / 146097;
327 l -= (146097 * n + 3) / 4;
328 i = (4000 * (l + 1)) / 1461001;
329 l += 31 - (1461 * i) / 4;
331 d = l - (2447 * j) / 80;
333 m = (j + 2) - (12 * l);
334 y = 100 * (n - 49) + i + l;
346 * parse and convert date in timestr (the normal interface)
348 * Returns the number of seconds since epoch (J2000)
352 * Break string into tokens based on a date/time context.
355 ParseDateTime(char *timestr, char *lowstr,
356 char **field, int *ftype, int maxfields, int *numfields)
363 printf("ParseDateTime- input string is %s\n", timestr);
365 /* outer loop through fields */
370 /* leading digit? then date or time */
371 if (isdigit(*cp) || (*cp == '.'))
379 ftype[nf] = DTK_TIME;
380 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
384 /* date field? allow embedded text month */
385 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
387 ftype[nf] = DTK_DATE;
388 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
389 *lp++ = tolower(*cp++);
394 * otherwise, number only and will determine year, month, or
398 ftype[nf] = DTK_NUMBER;
403 * text? then date string, month, day of week, special, or
406 else if (isalpha(*cp))
408 ftype[nf] = DTK_STRING;
409 *lp++ = tolower(*cp++);
411 *lp++ = tolower(*cp++);
413 /* full date string with leading text month? */
414 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
416 ftype[nf] = DTK_DATE;
417 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
418 *lp++ = tolower(*cp++);
421 /* skip leading spaces */
423 else if (isspace(*cp))
428 /* sign? then special or numeric timezone */
430 else if ((*cp == '+') || (*cp == '-'))
433 /* soak up leading whitespace */
436 /* numeric timezone? */
441 while (isdigit(*cp) || (*cp == ':'))
446 else if (isalpha(*cp))
448 ftype[nf] = DTK_SPECIAL;
449 *lp++ = tolower(*cp++);
451 *lp++ = tolower(*cp++);
453 /* otherwise something wrong... */
458 /* ignore punctuation but use as delimiter */
460 else if (ispunct(*cp))
469 /* force in a delimiter */
472 if (nf > MAXDATEFIELDS)
475 printf("ParseDateTime- set field[%d] to %s type %d\n", (nf - 1), field[nf - 1], ftype[nf - 1]);
482 } /* ParseDateTime() */
486 * Interpret previously parsed fields for general date and time.
487 * Return 0 if full date, 1 if only time, and -1 if problems.
488 * External format(s):
489 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
490 * "Fri Feb-7-1997 15:23:27"
491 * "Feb-7-1997 15:23:27"
492 * "2-7-1997 15:23:27"
493 * "1997-2-7 15:23:27"
494 * "1997.038 15:23:27" (day of year 1-366)
495 * Also supports input in compact time:
499 * Use the system-provided functions to get the current time zone
500 * if not specified in the input string.
501 * If the date is outside the time_t system-supported time range,
502 * then assume GMT time zone. - tgl 97/05/27
505 DecodeDateTime(char **field, int *ftype, int nf,
506 int *dtype, struct tm * tm, double *fsec, int *tzp)
522 tm->tm_isdst = -1; /* don't know daylight savings time status
527 for (i = 0; i < nf; i++)
530 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
535 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
540 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
544 * check upper limit on hours; other limits checked in
547 if (tm->tm_hour > 23)
554 if (DecodeTimezone(field[i], tzp) != 0)
560 flen = strlen(field[i]);
564 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
570 if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
577 type = DecodeSpecial(i, field[i], &val);
579 printf("DecodeDateTime- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
581 if (type == IGNOREFIELD)
589 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
602 printf("DecodeDateTime- month field %s value is %d\n", field[i], val);
604 /* tm_mon is 0->11, so need to subtract one from value in table */
609 * daylight savings time modifier (solves "MET
623 * set mask for TZ here _or_ check for DTZ later
624 * when getting default timezone
665 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
666 i, field[i], fmask, tmask, val);
674 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
676 tm->tm_year = -(tm->tm_year - 1);
678 if ((mer != HR24) && (tm->tm_hour > 12))
680 if ((mer == AM) && (tm->tm_hour == 12))
682 else if ((mer == PM) && (tm->tm_hour != 12))
685 /* If parsing a time string into a date, all date parts are unset.
686 Win2k defaults these to 30 dec, 1899 so: */
687 if (tm->tm_year == 0 && tm->tm_mon == 0 && tm->tm_mday == 0 && fmask == DTK_TIME_M) {
689 tm->tm_mon = 11; /* December, as tm_mon is 0..11 */
695 printf("DecodeDateTime- mask %08x (%08x)", fmask, DTK_DATE_M);
696 printf(" set y%04d m%02d d%02d", tm->tm_year, (tm->tm_mon+1), tm->tm_mday);
697 printf(" %02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
700 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
701 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
703 /* timezone not specified? then find local timezone if possible */
704 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) == DTK_DATE_M)
705 && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
709 * daylight savings time modifier but no standard timezone? then
712 if (fmask & DTK_M(DTZMOD))
715 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon+1, tm->tm_mday))
717 /* FIXME: The code below is not correct */
718 #if 0 /* defined(USE_POSIX_TIME) */
726 #if 0 /* defined(HAVE_INT_TIMEZONE) */
727 *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
729 #else /* !HAVE_INT_TIMEZONE */
730 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
733 #else /* !USE_POSIX_TIME */
745 } /* DecodeDateTime() */
749 * Interpret parsed string as time fields only.
752 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
766 tm->tm_isdst = -1; /* don't know daylight savings time status
772 for (i = 0; i < nf; i++)
775 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
780 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
785 flen = strlen(field[i]);
787 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
793 type = DecodeSpecial(i, field[i], &val);
795 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
797 if (type == IGNOREFIELD)
805 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
837 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
842 printf("DecodeTimeOnly- mask %08x (%08x)", fmask, DTK_TIME_M);
843 printf(" %02d:%02d:%02d (%f)\n", tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec);
846 if ((mer != HR24) && (tm->tm_hour > 12))
848 if ((mer == AM) && (tm->tm_hour == 12))
850 else if ((mer == PM) && (tm->tm_hour != 12))
853 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
857 } /* DecodeTimeOnly() */
861 * Decode date string which includes delimiters.
862 * Insist on a complete set of fields.
865 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
875 char *field[MAXDATEFIELDS];
877 /* parse this string... */
878 while ((*str != '\0') && (nf < MAXDATEFIELDS))
880 /* skip field separators */
881 while (!isalnum(*str))
887 while (isdigit(*str))
890 else if (isalpha(*str))
892 while (isalpha(*str))
901 /* don't allow too many fields */
907 /* look first for text fields, since that will be unambiguous month */
908 for (i = 0; i < nf; i++)
910 if (isalpha(*field[i]))
912 type = DecodeSpecial(i, field[i], &val);
913 if (type == IGNOREFIELD)
921 printf("DecodeDate- month field %s value is %d\n", field[i], val);
923 /* tm_mon is 0->11, so need to subtract one from value in table */
929 printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
939 /* mark this field as being completed */
944 /* now pick up remaining numeric fields */
945 for (i = 0; i < nf; i++)
947 if (field[i] == NULL)
950 if ((len = strlen(field[i])) <= 0)
953 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
968 * Decode time string which includes delimiters.
969 * Only check the lower limit on hours, since this same code
970 * can be used to represent time spans.
973 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
979 tm->tm_hour = strtol(str, &cp, 10);
983 tm->tm_min = strtol(str, &cp, 10);
998 tm->tm_sec = strtol(str, &cp, 10);
1001 else if (*cp == '.')
1004 *fsec = strtod(str, &cp);
1012 /* do a sanity check */
1013 if ((tm->tm_hour < 0)
1014 || (tm->tm_min < 0) || (tm->tm_min > 59)
1015 || (tm->tm_sec < 0) || (tm->tm_sec > 59))
1019 } /* DecodeTime() */
1023 * Interpret numeric field as a date value in context.
1026 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1033 val = strtol(str, &cp, 10);
1038 *fsec = strtod(cp, &cp);
1044 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1047 /* enough digits to be unequivocal year? */
1051 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1053 *tmask = DTK_M(YEAR);
1055 /* already have a year? then see if we can substitute... */
1056 if (fmask & DTK_M(YEAR))
1058 if ((!(fmask & DTK_M(DAY)))
1059 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1062 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm->tm_mday);
1064 tm->tm_mday = tm->tm_year;
1065 *tmask = DTK_M(DAY);
1071 /* special case day of year? */
1073 else if ((flen == 3) && (fmask & DTK_M(YEAR))
1074 && ((val >= 1) && (val <= 366)))
1076 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1078 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
1079 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1081 /* already have year? then could be month */
1083 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1084 && ((val >= 1) && (val <= 12)))
1087 printf("DecodeNumber- match %d (%s) as month\n", val, str);
1089 *tmask = DTK_M(MONTH);
1090 /* tm_mon is 0..11 */
1093 /* no year and EuroDates enabled? then could be day */
1095 else if ((EuroDates || (fmask & DTK_M(MONTH)))
1096 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1097 && ((val >= 1) && (val <= 31)))
1100 printf("DecodeNumber- match %d (%s) as day\n", val, str);
1102 *tmask = DTK_M(DAY);
1106 else if ((!(fmask & DTK_M(MONTH)))
1107 && ((val >= 1) && (val <= 12)))
1110 printf("DecodeNumber- (2) match %d (%s) as month\n", val, str);
1112 *tmask = DTK_M(MONTH);
1113 /* tm_mon is 0..11 */
1117 else if ((!(fmask & DTK_M(DAY)))
1118 && ((val >= 1) && (val <= 31)))
1121 printf("DecodeNumber- (2) match %d (%s) as day\n", val, str);
1123 *tmask = DTK_M(DAY);
1127 else if (!(fmask & DTK_M(YEAR)))
1130 printf("DecodeNumber- (2) match %d (%s) as year\n", val, str);
1132 *tmask = DTK_M(YEAR);
1134 if (tm->tm_year < 70)
1135 tm->tm_year += 2000;
1136 else if (tm->tm_year < 100)
1137 tm->tm_year += 1900;
1144 } /* DecodeNumber() */
1147 /* DecodeNumberField()
1148 * Interpret numeric string as a concatenated date field.
1151 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1159 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1162 *tmask = DTK_DATE_M;
1164 tm->tm_mday = atoi(str + 6);
1166 tm->tm_mon = atoi(str + 4) - 1; /* tm_mon is 0..11 */
1168 tm->tm_year = atoi(str + 0);
1170 /* yymmdd or hhmmss? */
1175 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1177 if (fmask & DTK_DATE_M)
1180 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1182 *tmask = DTK_TIME_M;
1183 tm->tm_sec = atoi(str + 4);
1185 tm->tm_min = atoi(str + 2);
1187 tm->tm_hour = atoi(str + 0);
1193 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1195 *tmask = DTK_DATE_M;
1196 tm->tm_mday = atoi(str + 4);
1198 tm->tm_mon = atoi(str + 2) - 1; /* tm_mon is 0..11 */
1200 tm->tm_year = atoi(str + 0);
1204 else if (strchr(str, '.') != NULL)
1207 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1209 *tmask = DTK_TIME_M;
1210 tm->tm_sec = strtod((str + 4), &cp);
1211 if (cp == (str + 4))
1214 *fsec = strtod(cp, NULL);
1216 tm->tm_min = strtod((str + 2), &cp);
1218 tm->tm_hour = strtod((str + 0), &cp);
1225 } /* DecodeNumberField() */
1229 * Interpret string as a numeric timezone.
1232 DecodeTimezone(char *str, int *tzp)
1240 /* assume leading character is "+" or "-" */
1241 hr = strtol((str + 1), &cp, 10);
1243 /* explicit delimiter? */
1246 min = strtol((cp + 1), &cp, 10);
1248 /* otherwise, might have run things together... */
1250 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1252 min = strtol((str + len - 2), &cp, 10);
1253 *(str + len - 2) = '\0';
1254 hr = strtol((str + 1), &cp, 10);
1260 tz = (hr * 60 + min) * 60;
1266 } /* DecodeTimezone() */
1270 * Decode text string using lookup table.
1271 * Implement a cache lookup since it is likely that dates
1272 * will be related in format.
1275 DecodeSpecial(int field, char *lowtoken, int *val)
1281 if ((datecache[field] != NULL)
1282 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1283 tp = datecache[field];
1287 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1290 datecache[field] = tp;
1315 } /* DecodeSpecial() */
1320 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1321 * is WAY faster than the generic bsearch().
1324 datebsearch(char *key, datetkn *base, unsigned int nel)
1326 datetkn *last = base + nel - 1,
1330 while (last >= base)
1332 position = base + ((last - base) >> 1);
1333 result = key[0] - position->token[0];
1336 result = strncmp(key, position->token, TOKMAXLEN);
1341 last = position - 1;
1343 base = position + 1;