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