Protect against multiple includes.
[wine.git] / ole / parsedt.c
blob963588ff342592d1c5ff450040ec915d3b177ee0
1 /*
2 PostgreSQL Data Base Management System (formerly known as Postgres, then
3 as Postgres95).
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 /*-------------------------------------------------------------------------
27 * dt.c--
28 * Functions for the built-in type "dt".
30 * Copyright (c) 1994, Regents of the University of California
33 * IDENTIFICATION
34 * $Header$
36 *-------------------------------------------------------------------------
38 #include <time.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <math.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <errno.h>
45 #include <float.h>
46 #include <limits.h>
47 #include <sys/timeb.h>
49 #include "parsedt.h"
51 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
52 static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
53 static int DecodeNumber(int flen, char *field,
54 int fmask, int *tmask, struct tm * tm, double *fsec);
55 static int DecodeNumberField(int len, char *str,
56 int fmask, int *tmask, struct tm * tm, double *fsec);
57 static int DecodeSpecial(int field, char *lowtoken, int *val);
58 static int DecodeTime(char *str, int fmask, int *tmask,
59 struct tm * tm, double *fsec);
60 static int DecodeTimezone(char *str, int *tzp);
62 #define USE_DATE_CACHE 1
63 #define ROUND_ALL 0
65 int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
67 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
70 char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
71 "Thursday", "Friday", "Saturday", NULL};
74 #define UTIME_MINYEAR (1901)
75 #define UTIME_MINMONTH (12)
76 #define UTIME_MINDAY (14)
77 #define UTIME_MAXYEAR (2038)
78 #define UTIME_MAXMONTH (01)
79 #define UTIME_MAXDAY (18)
81 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
82 || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
83 || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
84 && ((y < UTIME_MAXYEAR) \
85 || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
86 || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
91 /*****************************************************************************
92 * PRIVATE ROUTINES *
93 *****************************************************************************/
95 /* definitions for squeezing values into "value" */
96 #define ABS_SIGNBIT (char) 0200
97 #define VALMASK (char) 0177
98 #define NEG(n) ((n)|ABS_SIGNBIT)
99 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
100 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
101 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
104 * to keep this table reasonably small, we divide the lexval for TZ and DTZ
105 * entries by 10 and truncate the text field at MAXTOKLEN characters.
106 * the text field is not guaranteed to be NULL-terminated.
108 static datetkn datetktbl[] = {
109 /* text token lexval */
110 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
111 {"acsst", DTZ, 63}, /* Cent. Australia */
112 {"acst", TZ, 57}, /* Cent. Australia */
113 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
114 {"abstime", IGNOREFIELD, 0}, /* "abstime" for pre-v6.1 "Invalid
115 * Abstime" */
116 {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
117 {"aesst", DTZ, 66}, /* E. Australia */
118 {"aest", TZ, 60}, /* Australia Eastern Std Time */
119 {"ahst", TZ, 60}, /* Alaska-Hawaii Std Time */
120 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
121 {"am", AMPM, AM},
122 {"apr", MONTH, 4},
123 {"april", MONTH, 4},
124 {"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */
125 {"at", IGNOREFIELD, 0}, /* "at" (throwaway) */
126 {"aug", MONTH, 8},
127 {"august", MONTH, 8},
128 {"awsst", DTZ, 54}, /* W. Australia */
129 {"awst", TZ, 48}, /* W. Australia */
130 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
131 {"bst", TZ, 6}, /* British Summer Time */
132 {"bt", TZ, 18}, /* Baghdad Time */
133 {"cadt", DTZ, 63}, /* Central Australian DST */
134 {"cast", TZ, 57}, /* Central Australian ST */
135 {"cat", TZ, NEG(60)}, /* Central Alaska Time */
136 {"cct", TZ, 48}, /* China Coast */
137 {"cdt", DTZ, NEG(30)}, /* Central Daylight Time */
138 {"cet", TZ, 6}, /* Central European Time */
139 {"cetdst", DTZ, 12}, /* Central European Dayl.Time */
140 {"cst", TZ, NEG(36)}, /* Central Standard Time */
141 {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
142 {"dec", MONTH, 12},
143 {"december", MONTH, 12},
144 {"dnt", TZ, 6}, /* Dansk Normal Tid */
145 {"dow", RESERV, DTK_DOW}, /* day of week */
146 {"doy", RESERV, DTK_DOY}, /* day of year */
147 {"dst", DTZMOD, 6},
148 {"east", TZ, NEG(60)}, /* East Australian Std Time */
149 {"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */
150 {"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */
151 {"eetdst", DTZ, 18}, /* Eastern Europe */
152 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
153 #if USE_AUSTRALIAN_RULES
154 {"est", TZ, 60}, /* Australia Eastern Std Time */
155 #else
156 {"est", TZ, NEG(30)}, /* Eastern Standard Time */
157 #endif
158 {"feb", MONTH, 2},
159 {"february", MONTH, 2},
160 {"fri", DOW, 5},
161 {"friday", DOW, 5},
162 {"fst", TZ, 6}, /* French Summer Time */
163 {"fwt", DTZ, 12}, /* French Winter Time */
164 {"gmt", TZ, 0}, /* Greenwish Mean Time */
165 {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
166 {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
167 {"hmt", DTZ, 18}, /* Hellas ? ? */
168 {"hst", TZ, NEG(60)}, /* Hawaii Std Time */
169 {"idle", TZ, 72}, /* Intl. Date Line, East */
170 {"idlw", TZ, NEG(72)}, /* Intl. Date Line,, est */
171 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
172 {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for invalid
173 * time */
174 {"ist", TZ, 12}, /* Israel */
175 {"it", TZ, 22}, /* Iran Time */
176 {"jan", MONTH, 1},
177 {"january", MONTH, 1},
178 {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
179 {"jt", TZ, 45}, /* Java Time */
180 {"jul", MONTH, 7},
181 {"july", MONTH, 7},
182 {"jun", MONTH, 6},
183 {"june", MONTH, 6},
184 {"kst", TZ, 54}, /* Korea Standard Time */
185 {"ligt", TZ, 60}, /* From Melbourne, Australia */
186 {"mar", MONTH, 3},
187 {"march", MONTH, 3},
188 {"may", MONTH, 5},
189 {"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */
190 {"mest", DTZ, 12}, /* Middle Europe Summer Time */
191 {"met", TZ, 6}, /* Middle Europe Time */
192 {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */
193 {"mewt", TZ, 6}, /* Middle Europe Winter Time */
194 {"mez", TZ, 6}, /* Middle Europe Zone */
195 {"mon", DOW, 1},
196 {"monday", DOW, 1},
197 {"mst", TZ, NEG(42)}, /* Mountain Standard Time */
198 {"mt", TZ, 51}, /* Moluccas Time */
199 {"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */
200 {"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */
201 {"nor", TZ, 6}, /* Norway Standard Time */
202 {"nov", MONTH, 11},
203 {"november", MONTH, 11},
204 {NOW, RESERV, DTK_NOW}, /* current transaction time */
205 {"nst", TZ, NEG(21)}, /* Nfld. Standard Time */
206 {"nt", TZ, NEG(66)}, /* Nome Time */
207 {"nzdt", DTZ, 78}, /* New Zealand Daylight Time */
208 {"nzst", TZ, 72}, /* New Zealand Standard Time */
209 {"nzt", TZ, 72}, /* New Zealand Time */
210 {"oct", MONTH, 10},
211 {"october", MONTH, 10},
212 {"on", IGNOREFIELD, 0}, /* "on" (throwaway) */
213 {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
214 {"pm", AMPM, PM},
215 {"pst", TZ, NEG(48)}, /* Pacific Standard Time */
216 {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
217 {"sast", TZ, 57}, /* South Australian Std Time */
218 {"sat", DOW, 6},
219 {"saturday", DOW, 6},
220 {"sep", MONTH, 9},
221 {"sept", MONTH, 9},
222 {"september", MONTH, 9},
223 {"set", TZ, NEG(6)}, /* Seychelles Time ?? */
224 {"sst", DTZ, 12}, /* Swedish Summer Time */
225 {"sun", DOW, 0},
226 {"sunday", DOW, 0},
227 {"swt", TZ, 6}, /* Swedish Winter Time */
228 {"thu", DOW, 4},
229 {"thur", DOW, 4},
230 {"thurs", DOW, 4},
231 {"thursday", DOW, 4},
232 {TODAY, RESERV, DTK_TODAY}, /* midnight */
233 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
234 {"tue", DOW, 2},
235 {"tues", DOW, 2},
236 {"tuesday", DOW, 2},
237 {"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
238 * time */
239 {"ut", TZ, 0},
240 {"utc", TZ, 0},
241 {"wadt", DTZ, 48}, /* West Australian DST */
242 {"wast", TZ, 42}, /* West Australian Std Time */
243 {"wat", TZ, NEG(6)}, /* West Africa Time */
244 {"wdt", DTZ, 54}, /* West Australian DST */
245 {"wed", DOW, 3},
246 {"wednesday", DOW, 3},
247 {"weds", DOW, 3},
248 {"wet", TZ, 0}, /* Western Europe */
249 {"wetdst", DTZ, 6}, /* Western Europe */
250 {"wst", TZ, 48}, /* West Australian Std Time */
251 {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
252 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
253 {"yst", TZ, NEG(54)}, /* Yukon Standard Time */
254 {"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
255 {"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
256 {"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
257 {"z", RESERV, DTK_ZULU}, /* 00:00:00 */
258 {ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
261 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
265 #if USE_DATE_CACHE
266 datetkn *datecache[MAXDATEFIELDS] = {NULL};
268 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
270 #endif
274 * Calendar time to Julian date conversions.
275 * Julian date is commonly used in astronomical applications,
276 * since it is numerically accurate and computationally simple.
277 * The algorithms here will accurately convert between Julian day
278 * and calendar date for all non-negative Julian days
279 * (i.e. from Nov 23, -4713 on).
281 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
282 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
284 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
285 * now at Aerospace Corp. (hi, Henry!)
287 * These routines will be used by other date/time packages - tgl 97/02/25
290 /* Set the minimum year to one greater than the year of the first valid day
291 * to avoid having to check year and day both. - tgl 97/05/08
294 #define JULIAN_MINYEAR (-4713)
295 #define JULIAN_MINMONTH (11)
296 #define JULIAN_MINDAY (23)
298 #define IS_VALID_JULIAN(y,m,d) ((y > JULIAN_MINYEAR) \
299 || ((y == JULIAN_MINYEAR) && ((m > JULIAN_MINMONTH) \
300 || ((m == JULIAN_MINMONTH) && (d >= JULIAN_MINDAY)))))
303 date2j(int y, int m, int d)
305 int m12 = (m - 14) / 12;
307 return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
308 - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
309 } /* date2j() */
311 void
312 j2date(int jd, int *year, int *month, int *day)
314 int j,
319 int i,
323 l = jd + 68569;
324 n = (4 * l) / 146097;
325 l -= (146097 * n + 3) / 4;
326 i = (4000 * (l + 1)) / 1461001;
327 l += 31 - (1461 * i) / 4;
328 j = (80 * l) / 2447;
329 d = l - (2447 * j) / 80;
330 l = j / 11;
331 m = (j + 2) - (12 * l);
332 y = 100 * (n - 49) + i + l;
334 *year = y;
335 *month = m;
336 *day = d;
337 return;
338 } /* j2date() */
344 * parse and convert date in timestr (the normal interface)
346 * Returns the number of seconds since epoch (J2000)
349 /* ParseDateTime()
350 * Break string into tokens based on a date/time context.
353 ParseDateTime(char *timestr, char *lowstr,
354 char **field, int *ftype, int maxfields, int *numfields)
356 int nf = 0;
357 char *cp = timestr;
358 char *lp = lowstr;
360 #ifdef DATEDEBUG
361 printf("ParseDateTime- input string is %s\n", timestr);
362 #endif
363 /* outer loop through fields */
364 while (*cp != '\0')
366 field[nf] = lp;
368 /* leading digit? then date or time */
369 if (isdigit(*cp) || (*cp == '.'))
371 *lp++ = *cp++;
372 while (isdigit(*cp))
373 *lp++ = *cp++;
374 /* time field? */
375 if (*cp == ':')
377 ftype[nf] = DTK_TIME;
378 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
379 *lp++ = *cp++;
382 /* date field? allow embedded text month */
383 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
385 ftype[nf] = DTK_DATE;
386 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
387 *lp++ = tolower(*cp++);
392 * otherwise, number only and will determine year, month, or
393 * day later
395 else
396 ftype[nf] = DTK_NUMBER;
401 * text? then date string, month, day of week, special, or
402 * timezone
404 else if (isalpha(*cp))
406 ftype[nf] = DTK_STRING;
407 *lp++ = tolower(*cp++);
408 while (isalpha(*cp))
409 *lp++ = tolower(*cp++);
411 /* full date string with leading text month? */
412 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
414 ftype[nf] = DTK_DATE;
415 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
416 *lp++ = tolower(*cp++);
419 /* skip leading spaces */
421 else if (isspace(*cp))
423 cp++;
424 continue;
426 /* sign? then special or numeric timezone */
428 else if ((*cp == '+') || (*cp == '-'))
430 *lp++ = *cp++;
431 /* soak up leading whitespace */
432 while (isspace(*cp))
433 cp++;
434 /* numeric timezone? */
435 if (isdigit(*cp))
437 ftype[nf] = DTK_TZ;
438 *lp++ = *cp++;
439 while (isdigit(*cp) || (*cp == ':'))
440 *lp++ = *cp++;
442 /* special? */
444 else if (isalpha(*cp))
446 ftype[nf] = DTK_SPECIAL;
447 *lp++ = tolower(*cp++);
448 while (isalpha(*cp))
449 *lp++ = tolower(*cp++);
451 /* otherwise something wrong... */
453 else
454 return -1;
456 /* ignore punctuation but use as delimiter */
458 else if (ispunct(*cp))
460 cp++;
461 continue;
464 else
465 return -1;
467 /* force in a delimiter */
468 *lp++ = '\0';
469 nf++;
470 if (nf > MAXDATEFIELDS)
471 return -1;
472 #ifdef DATEDEBUG
473 printf("ParseDateTime- set field[%d] to %s type %d\n", (nf - 1), field[nf - 1], ftype[nf - 1]);
474 #endif
477 *numfields = nf;
479 return 0;
480 } /* ParseDateTime() */
483 /* DecodeDateTime()
484 * Interpret previously parsed fields for general date and time.
485 * Return 0 if full date, 1 if only time, and -1 if problems.
486 * External format(s):
487 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
488 * "Fri Feb-7-1997 15:23:27"
489 * "Feb-7-1997 15:23:27"
490 * "2-7-1997 15:23:27"
491 * "1997-2-7 15:23:27"
492 * "1997.038 15:23:27" (day of year 1-366)
493 * Also supports input in compact time:
494 * "970207 152327"
495 * "97038 152327"
497 * Use the system-provided functions to get the current time zone
498 * if not specified in the input string.
499 * If the date is outside the time_t system-supported time range,
500 * then assume GMT time zone. - tgl 97/05/27
503 DecodeDateTime(char **field, int *ftype, int nf,
504 int *dtype, struct tm * tm, double *fsec, int *tzp)
506 int fmask = 0,
507 tmask,
508 type;
509 int i;
510 int flen,
511 val;
512 int mer = HR24;
513 int bc = FALSE;
515 *dtype = DTK_DATE;
516 tm->tm_hour = 0;
517 tm->tm_min = 0;
518 tm->tm_sec = 0;
519 *fsec = 0;
520 tm->tm_isdst = -1; /* don't know daylight savings time status
521 * apriori */
522 if (tzp != NULL)
523 *tzp = 0;
525 for (i = 0; i < nf; i++)
527 #ifdef DATEDEBUG
528 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
529 #endif
530 switch (ftype[i])
532 case DTK_DATE:
533 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
534 return -1;
535 break;
537 case DTK_TIME:
538 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
539 return -1;
542 * check upper limit on hours; other limits checked in
543 * DecodeTime()
545 if (tm->tm_hour > 23)
546 return -1;
547 break;
549 case DTK_TZ:
550 if (tzp == NULL)
551 return -1;
552 if (DecodeTimezone(field[i], tzp) != 0)
553 return -1;
554 tmask = DTK_M(TZ);
555 break;
557 case DTK_NUMBER:
558 flen = strlen(field[i]);
560 if (flen > 4)
562 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
563 return -1;
566 else
568 if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
569 return -1;
571 break;
573 case DTK_STRING:
574 case DTK_SPECIAL:
575 type = DecodeSpecial(i, field[i], &val);
576 #ifdef DATEDEBUG
577 printf("DecodeDateTime- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
578 #endif
579 if (type == IGNOREFIELD)
580 continue;
582 tmask = DTK_M(type);
583 switch (type)
585 case RESERV:
586 #ifdef DATEDEBUG
587 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
588 #endif
589 switch (val)
592 default:
593 *dtype = val;
596 break;
598 case MONTH:
599 #ifdef DATEDEBUG
600 printf("DecodeDateTime- month field %s value is %d\n", field[i], val);
601 #endif
602 tm->tm_mon = val;
603 break;
606 * daylight savings time modifier (solves "MET
607 * DST" syntax)
609 case DTZMOD:
610 tmask |= DTK_M(DTZ);
611 tm->tm_isdst = 1;
612 if (tzp == NULL)
613 return -1;
614 *tzp += val * 60;
615 break;
617 case DTZ:
620 * set mask for TZ here _or_ check for DTZ later
621 * when getting default timezone
623 tmask |= DTK_M(TZ);
624 tm->tm_isdst = 1;
625 if (tzp == NULL)
626 return -1;
627 *tzp = val * 60;
628 break;
630 case TZ:
631 tm->tm_isdst = 0;
632 if (tzp == NULL)
633 return -1;
634 *tzp = val * 60;
635 break;
637 case IGNOREFIELD:
638 break;
640 case AMPM:
641 mer = val;
642 break;
644 case ADBC:
645 bc = (val == BC);
646 break;
648 case DOW:
649 tm->tm_wday = val;
650 break;
652 default:
653 return -1;
655 break;
657 default:
658 return -1;
661 #ifdef DATEDEBUG
662 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
663 i, field[i], fmask, tmask, val);
664 #endif
666 if (tmask & fmask)
667 return -1;
668 fmask |= tmask;
671 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
672 if (bc)
673 tm->tm_year = -(tm->tm_year - 1);
675 if ((mer != HR24) && (tm->tm_hour > 12))
676 return -1;
677 if ((mer == AM) && (tm->tm_hour == 12))
678 tm->tm_hour = 0;
679 else if ((mer == PM) && (tm->tm_hour != 12))
680 tm->tm_hour += 12;
682 #ifdef DATEDEBUG
683 printf("DecodeDateTime- mask %08x (%08x)", fmask, DTK_DATE_M);
684 printf(" set y%04d m%02d d%02d", tm->tm_year, tm->tm_mon, tm->tm_mday);
685 printf(" %02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
686 #endif
688 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
689 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
691 /* timezone not specified? then find local timezone if possible */
692 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) == DTK_DATE_M)
693 && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
697 * daylight savings time modifier but no standard timezone? then
698 * error
700 if (fmask & DTK_M(DTZMOD))
701 return -1;
703 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
705 #ifdef USE_POSIX_TIME
706 tm->tm_year -= 1900;
707 tm->tm_mon -= 1;
708 tm->tm_isdst = -1;
709 mktime(tm);
710 tm->tm_year += 1900;
711 tm->tm_mon += 1;
713 #ifdef HAVE_INT_TIMEZONE
714 *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
716 #else /* !HAVE_INT_TIMEZONE */
717 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
718 #endif
720 #else /* !USE_POSIX_TIME */
721 *tzp = CTimeZone;
722 #endif
724 else
726 tm->tm_isdst = 0;
727 *tzp = 0;
731 return 0;
732 } /* DecodeDateTime() */
735 /* DecodeTimeOnly()
736 * Interpret parsed string as time fields only.
739 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
741 int fmask,
742 tmask,
743 type;
744 int i;
745 int flen,
746 val;
747 int mer = HR24;
749 *dtype = DTK_TIME;
750 tm->tm_hour = 0;
751 tm->tm_min = 0;
752 tm->tm_sec = 0;
753 tm->tm_isdst = -1; /* don't know daylight savings time status
754 * apriori */
755 *fsec = 0;
757 fmask = DTK_DATE_M;
759 for (i = 0; i < nf; i++)
761 #ifdef DATEDEBUG
762 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
763 #endif
764 switch (ftype[i])
766 case DTK_TIME:
767 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
768 return -1;
769 break;
771 case DTK_NUMBER:
772 flen = strlen(field[i]);
774 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
775 return -1;
776 break;
778 case DTK_STRING:
779 case DTK_SPECIAL:
780 type = DecodeSpecial(i, field[i], &val);
781 #ifdef DATEDEBUG
782 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
783 #endif
784 if (type == IGNOREFIELD)
785 continue;
787 tmask = DTK_M(type);
788 switch (type)
790 case RESERV:
791 #ifdef DATEDEBUG
792 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
793 #endif
794 switch (val)
797 default:
798 return -1;
801 break;
803 case IGNOREFIELD:
804 break;
806 case AMPM:
807 mer = val;
808 break;
810 default:
811 return -1;
813 break;
815 default:
816 return -1;
819 if (tmask & fmask)
820 return -1;
821 fmask |= tmask;
823 #ifdef DATEDEBUG
824 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
825 #endif
828 #ifdef DATEDEBUG
829 printf("DecodeTimeOnly- mask %08x (%08x)", fmask, DTK_TIME_M);
830 printf(" %02d:%02d:%02d (%f)\n", tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec);
831 #endif
833 if ((mer != HR24) && (tm->tm_hour > 12))
834 return -1;
835 if ((mer == AM) && (tm->tm_hour == 12))
836 tm->tm_hour = 0;
837 else if ((mer == PM) && (tm->tm_hour != 12))
838 tm->tm_hour += 12;
840 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
841 return -1;
843 return 0;
844 } /* DecodeTimeOnly() */
847 /* DecodeDate()
848 * Decode date string which includes delimiters.
849 * Insist on a complete set of fields.
851 static int
852 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
854 double fsec;
856 int nf = 0;
857 int i,
858 len;
859 int type,
860 val,
861 dmask = 0;
862 char *field[MAXDATEFIELDS];
864 /* parse this string... */
865 while ((*str != '\0') && (nf < MAXDATEFIELDS))
867 /* skip field separators */
868 while (!isalnum(*str))
869 str++;
871 field[nf] = str;
872 if (isdigit(*str))
874 while (isdigit(*str))
875 str++;
877 else if (isalpha(*str))
879 while (isalpha(*str))
880 str++;
883 if (*str != '\0')
884 *str++ = '\0';
885 nf++;
888 /* don't allow too many fields */
889 if (nf > 3)
890 return -1;
892 *tmask = 0;
894 /* look first for text fields, since that will be unambiguous month */
895 for (i = 0; i < nf; i++)
897 if (isalpha(*field[i]))
899 type = DecodeSpecial(i, field[i], &val);
900 if (type == IGNOREFIELD)
901 continue;
903 dmask = DTK_M(type);
904 switch (type)
906 case MONTH:
907 #ifdef DATEDEBUG
908 printf("DecodeDate- month field %s value is %d\n", field[i], val);
909 #endif
910 tm->tm_mon = val;
911 break;
913 default:
914 #ifdef DATEDEBUG
915 printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
916 #endif
917 return -1;
919 if (fmask & dmask)
920 return -1;
922 fmask |= dmask;
923 *tmask |= dmask;
925 /* mark this field as being completed */
926 field[i] = NULL;
930 /* now pick up remaining numeric fields */
931 for (i = 0; i < nf; i++)
933 if (field[i] == NULL)
934 continue;
936 if ((len = strlen(field[i])) <= 0)
937 return -1;
939 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
940 return -1;
942 if (fmask & dmask)
943 return -1;
945 fmask |= dmask;
946 *tmask |= dmask;
949 return 0;
950 } /* DecodeDate() */
953 /* DecodeTime()
954 * Decode time string which includes delimiters.
955 * Only check the lower limit on hours, since this same code
956 * can be used to represent time spans.
958 static int
959 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
961 char *cp;
963 *tmask = DTK_TIME_M;
965 tm->tm_hour = strtol(str, &cp, 10);
966 if (*cp != ':')
967 return -1;
968 str = cp + 1;
969 tm->tm_min = strtol(str, &cp, 10);
970 if (*cp == '\0')
972 tm->tm_sec = 0;
973 *fsec = 0;
976 else if (*cp != ':')
978 return -1;
981 else
983 str = cp + 1;
984 tm->tm_sec = strtol(str, &cp, 10);
985 if (*cp == '\0')
986 *fsec = 0;
987 else if (*cp == '.')
989 str = cp;
990 *fsec = strtod(str, &cp);
991 if (cp == str)
992 return -1;
994 else
995 return -1;
998 /* do a sanity check */
999 if ((tm->tm_hour < 0)
1000 || (tm->tm_min < 0) || (tm->tm_min > 59)
1001 || (tm->tm_sec < 0) || (tm->tm_sec > 59))
1002 return -1;
1004 return 0;
1005 } /* DecodeTime() */
1008 /* DecodeNumber()
1009 * Interpret numeric field as a date value in context.
1011 static int
1012 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1014 int val;
1015 char *cp;
1017 *tmask = 0;
1019 val = strtol(str, &cp, 10);
1020 if (cp == str)
1021 return -1;
1022 if (*cp == '.')
1024 *fsec = strtod(cp, &cp);
1025 if (*cp != '\0')
1026 return -1;
1029 #ifdef DATEDEBUG
1030 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1031 #endif
1033 /* enough digits to be unequivocal year? */
1034 if (flen == 4)
1036 #ifdef DATEDEBUG
1037 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1038 #endif
1039 *tmask = DTK_M(YEAR);
1041 /* already have a year? then see if we can substitute... */
1042 if (fmask & DTK_M(YEAR))
1044 if ((!(fmask & DTK_M(DAY)))
1045 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1047 #ifdef DATEDEBUG
1048 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm->tm_mday);
1049 #endif
1050 tm->tm_mday = tm->tm_year;
1051 *tmask = DTK_M(DAY);
1055 tm->tm_year = val;
1057 /* special case day of year? */
1059 else if ((flen == 3) && (fmask & DTK_M(YEAR))
1060 && ((val >= 1) && (val <= 366)))
1062 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1063 tm->tm_yday = val;
1064 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
1065 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1067 /* already have year? then could be month */
1069 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1070 && ((val >= 1) && (val <= 12)))
1072 #ifdef DATEDEBUG
1073 printf("DecodeNumber- match %d (%s) as month\n", val, str);
1074 #endif
1075 *tmask = DTK_M(MONTH);
1076 tm->tm_mon = val;
1078 /* no year and EuroDates enabled? then could be day */
1080 else if ((EuroDates || (fmask & DTK_M(MONTH)))
1081 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1082 && ((val >= 1) && (val <= 31)))
1084 #ifdef DATEDEBUG
1085 printf("DecodeNumber- match %d (%s) as day\n", val, str);
1086 #endif
1087 *tmask = DTK_M(DAY);
1088 tm->tm_mday = val;
1091 else if ((!(fmask & DTK_M(MONTH)))
1092 && ((val >= 1) && (val <= 12)))
1094 #ifdef DATEDEBUG
1095 printf("DecodeNumber- (2) match %d (%s) as month\n", val, str);
1096 #endif
1097 *tmask = DTK_M(MONTH);
1098 tm->tm_mon = val;
1101 else if ((!(fmask & DTK_M(DAY)))
1102 && ((val >= 1) && (val <= 31)))
1104 #ifdef DATEDEBUG
1105 printf("DecodeNumber- (2) match %d (%s) as day\n", val, str);
1106 #endif
1107 *tmask = DTK_M(DAY);
1108 tm->tm_mday = val;
1111 else if (!(fmask & DTK_M(YEAR)))
1113 #ifdef DATEDEBUG
1114 printf("DecodeNumber- (2) match %d (%s) as year\n", val, str);
1115 #endif
1116 *tmask = DTK_M(YEAR);
1117 tm->tm_year = val;
1118 if (tm->tm_year < 70)
1119 tm->tm_year += 2000;
1120 else if (tm->tm_year < 100)
1121 tm->tm_year += 1900;
1124 else
1125 return -1;
1127 return 0;
1128 } /* DecodeNumber() */
1131 /* DecodeNumberField()
1132 * Interpret numeric string as a concatenated date field.
1134 static int
1135 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1137 char *cp;
1139 /* yyyymmdd? */
1140 if (len == 8)
1142 #ifdef DATEDEBUG
1143 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1144 #endif
1146 *tmask = DTK_DATE_M;
1148 tm->tm_mday = atoi(str + 6);
1149 *(str + 6) = '\0';
1150 tm->tm_mon = atoi(str + 4);
1151 *(str + 4) = '\0';
1152 tm->tm_year = atoi(str + 0);
1154 /* yymmdd or hhmmss? */
1156 else if (len == 6)
1158 #ifdef DATEDEBUG
1159 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1160 #endif
1161 if (fmask & DTK_DATE_M)
1163 #ifdef DATEDEBUG
1164 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1165 #endif
1166 *tmask = DTK_TIME_M;
1167 tm->tm_sec = atoi(str + 4);
1168 *(str + 4) = '\0';
1169 tm->tm_min = atoi(str + 2);
1170 *(str + 2) = '\0';
1171 tm->tm_hour = atoi(str + 0);
1174 else
1176 #ifdef DATEDEBUG
1177 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1178 #endif
1179 *tmask = DTK_DATE_M;
1180 tm->tm_mday = atoi(str + 4);
1181 *(str + 4) = '\0';
1182 tm->tm_mon = atoi(str + 2);
1183 *(str + 2) = '\0';
1184 tm->tm_year = atoi(str + 0);
1188 else if (strchr(str, '.') != NULL)
1190 #ifdef DATEDEBUG
1191 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1192 #endif
1193 *tmask = DTK_TIME_M;
1194 tm->tm_sec = strtod((str + 4), &cp);
1195 if (cp == (str + 4))
1196 return -1;
1197 if (*cp == '.')
1198 *fsec = strtod(cp, NULL);
1199 *(str + 4) = '\0';
1200 tm->tm_min = strtod((str + 2), &cp);
1201 *(str + 2) = '\0';
1202 tm->tm_hour = strtod((str + 0), &cp);
1205 else
1206 return -1;
1208 return 0;
1209 } /* DecodeNumberField() */
1212 /* DecodeTimezone()
1213 * Interpret string as a numeric timezone.
1215 static int
1216 DecodeTimezone(char *str, int *tzp)
1218 int tz;
1219 int hr,
1220 min;
1221 char *cp;
1222 int len;
1224 /* assume leading character is "+" or "-" */
1225 hr = strtol((str + 1), &cp, 10);
1227 /* explicit delimiter? */
1228 if (*cp == ':')
1230 min = strtol((cp + 1), &cp, 10);
1232 /* otherwise, might have run things together... */
1234 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1236 min = strtol((str + len - 2), &cp, 10);
1237 *(str + len - 2) = '\0';
1238 hr = strtol((str + 1), &cp, 10);
1241 else
1242 min = 0;
1244 tz = (hr * 60 + min) * 60;
1245 if (*str == '-')
1246 tz = -tz;
1248 *tzp = -tz;
1249 return *cp != '\0';
1250 } /* DecodeTimezone() */
1253 /* DecodeSpecial()
1254 * Decode text string using lookup table.
1255 * Implement a cache lookup since it is likely that dates
1256 * will be related in format.
1258 static int
1259 DecodeSpecial(int field, char *lowtoken, int *val)
1261 int type;
1262 datetkn *tp;
1264 #if USE_DATE_CACHE
1265 if ((datecache[field] != NULL)
1266 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1267 tp = datecache[field];
1268 else
1270 #endif
1271 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1272 #if USE_DATE_CACHE
1274 datecache[field] = tp;
1275 #endif
1276 if (tp == NULL)
1278 type = IGNOREFIELD;
1279 *val = 0;
1281 else
1283 type = tp->type;
1284 switch (type)
1286 case TZ:
1287 case DTZ:
1288 case DTZMOD:
1289 *val = FROMVAL(tp);
1290 break;
1292 default:
1293 *val = tp->value;
1294 break;
1298 return type;
1299 } /* DecodeSpecial() */
1303 /* datebsearch()
1304 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1305 * is WAY faster than the generic bsearch().
1307 static datetkn *
1308 datebsearch(char *key, datetkn *base, unsigned int nel)
1310 datetkn *last = base + nel - 1,
1311 *position;
1312 int result;
1314 while (last >= base)
1316 position = base + ((last - base) >> 1);
1317 result = key[0] - position->token[0];
1318 if (result == 0)
1320 result = strncmp(key, position->token, TOKMAXLEN);
1321 if (result == 0)
1322 return position;
1324 if (result < 0)
1325 last = position - 1;
1326 else
1327 base = position + 1;
1329 return NULL;