Added #ifdef HAVE_WCTYPE_H.
[wine] / ole / 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  * IDENTIFICATION
34  *        $Header$
35  *
36  *-------------------------------------------------------------------------
37  */
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>
48
49 #include "parsedt.h"
50
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);
61
62 #define USE_DATE_CACHE 1
63 #define ROUND_ALL 0
64
65 int                     mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
66
67 char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
69
70 char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
71 "Thursday", "Friday", "Saturday", NULL};
72
73
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)
80
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))))))
87
88
89
90
91 /*****************************************************************************
92  *       PRIVATE ROUTINES                                                                                                                *
93  *****************************************************************************/
94
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))
102
103 /*
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.
107  */
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 */
259 };
260
261 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
262
263
264
265 #if USE_DATE_CACHE
266 datetkn    *datecache[MAXDATEFIELDS] = {NULL};
267
268 datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
269
270 #endif
271
272
273 /*
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).
280  *
281  * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
282  *      University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
283  *
284  * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
285  *      now at Aerospace Corp. (hi, Henry!)
286  *
287  * These routines will be used by other date/time packages - tgl 97/02/25
288  */
289
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
292  */
293
294 #define JULIAN_MINYEAR (-4713)
295 #define JULIAN_MINMONTH (11)
296 #define JULIAN_MINDAY (23)
297
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)))))
301
302 int
303 date2j(int y, int m, int d)
304 {
305         int                     m12 = (m - 14) / 12;
306
307         return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
308                         - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
309 }       /* date2j() */
310
311 void
312 j2date(int jd, int *year, int *month, int *day)
313 {
314         int                     j,
315                                 y,
316                                 m,
317                                 d;
318
319         int                     i,
320                                 l,
321                                 n;
322
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;
333
334         *year = y;
335         *month = m;
336         *day = d;
337         return;
338 }       /* j2date() */
339
340
341
342
343 /*
344  * parse and convert date in timestr (the normal interface)
345  *
346  * Returns the number of seconds since epoch (J2000)
347  */
348
349 /* ParseDateTime()
350  * Break string into tokens based on a date/time context.
351  */
352 int
353 ParseDateTime(char *timestr, char *lowstr,
354                           char **field, int *ftype, int maxfields, int *numfields)
355 {
356         int                     nf = 0;
357         char       *cp = timestr;
358         char       *lp = lowstr;
359
360 #ifdef DATEDEBUG
361         printf("ParseDateTime- input string is %s\n", timestr);
362 #endif
363         /* outer loop through fields */
364         while (*cp != '\0')
365         {
366                 field[nf] = lp;
367
368                 /* leading digit? then date or time */
369                 if (isdigit(*cp) || (*cp == '.'))
370                 {
371                         *lp++ = *cp++;
372                         while (isdigit(*cp))
373                                 *lp++ = *cp++;
374                         /* time field? */
375                         if (*cp == ':')
376                         {
377                                 ftype[nf] = DTK_TIME;
378                                 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
379                                         *lp++ = *cp++;
380
381                         }
382                         /* date field? allow embedded text month */
383                         else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
384                         {
385                                 ftype[nf] = DTK_DATE;
386                                 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
387                                         *lp++ = tolower(*cp++);
388
389                         }
390
391                         /*
392                          * otherwise, number only and will determine year, month, or
393                          * day later
394                          */
395                         else
396                                 ftype[nf] = DTK_NUMBER;
397
398                 }
399
400                 /*
401                  * text? then date string, month, day of week, special, or
402                  * timezone
403                  */
404                 else if (isalpha(*cp))
405                 {
406                         ftype[nf] = DTK_STRING;
407                         *lp++ = tolower(*cp++);
408                         while (isalpha(*cp))
409                                 *lp++ = tolower(*cp++);
410
411                         /* full date string with leading text month? */
412                         if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
413                         {
414                                 ftype[nf] = DTK_DATE;
415                                 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
416                                         *lp++ = tolower(*cp++);
417                         }
418
419                         /* skip leading spaces */
420                 }
421                 else if (isspace(*cp))
422                 {
423                         cp++;
424                         continue;
425
426                         /* sign? then special or numeric timezone */
427                 }
428                 else if ((*cp == '+') || (*cp == '-'))
429                 {
430                         *lp++ = *cp++;
431                         /* soak up leading whitespace */
432                         while (isspace(*cp))
433                                 cp++;
434                         /* numeric timezone? */
435                         if (isdigit(*cp))
436                         {
437                                 ftype[nf] = DTK_TZ;
438                                 *lp++ = *cp++;
439                                 while (isdigit(*cp) || (*cp == ':'))
440                                         *lp++ = *cp++;
441
442                                 /* special? */
443                         }
444                         else if (isalpha(*cp))
445                         {
446                                 ftype[nf] = DTK_SPECIAL;
447                                 *lp++ = tolower(*cp++);
448                                 while (isalpha(*cp))
449                                         *lp++ = tolower(*cp++);
450
451                                 /* otherwise something wrong... */
452                         }
453                         else
454                                 return -1;
455
456                         /* ignore punctuation but use as delimiter */
457                 }
458                 else if (ispunct(*cp))
459                 {
460                         cp++;
461                         continue;
462
463                 }
464                 else
465                         return -1;
466
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
475         }
476
477         *numfields = nf;
478
479         return 0;
480 }       /* ParseDateTime() */
481
482
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"
496  *
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
501  */
502 int
503 DecodeDateTime(char **field, int *ftype, int nf,
504                            int *dtype, struct tm * tm, double *fsec, int *tzp)
505 {
506         int                     fmask = 0,
507                                 tmask,
508                                 type;
509         int                     i;
510         int                     flen,
511                                 val;
512         int                     mer = HR24;
513         int                     bc = FALSE;
514
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;
524
525         for (i = 0; i < nf; i++)
526         {
527 #ifdef DATEDEBUG
528                 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
529 #endif
530                 switch (ftype[i])
531                 {
532                         case DTK_DATE:
533                                 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
534                                         return -1;
535                                 break;
536
537                         case DTK_TIME:
538                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
539                                         return -1;
540
541                                 /*
542                                  * check upper limit on hours; other limits checked in
543                                  * DecodeTime()
544                                  */
545                                 if (tm->tm_hour > 23)
546                                         return -1;
547                                 break;
548
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;
556
557                         case DTK_NUMBER:
558                                 flen = strlen(field[i]);
559
560                                 if (flen > 4)
561                                 {
562                                         if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
563                                                 return -1;
564
565                                 }
566                                 else
567                                 {
568                                         if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
569                                                 return -1;
570                                 }
571                                 break;
572
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;
581
582                                 tmask = DTK_M(type);
583                                 switch (type)
584                                 {
585                                         case RESERV:
586 #ifdef DATEDEBUG
587                                                 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
588 #endif
589                                                 switch (val)
590                                                 {
591
592                                                         default:
593                                                                 *dtype = val;
594                                                 }
595
596                                                 break;
597
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;
604
605                                                 /*
606                                                  * daylight savings time modifier (solves "MET
607                                                  * DST" syntax)
608                                                  */
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;
616
617                                         case DTZ:
618
619                                                 /*
620                                                  * set mask for TZ here _or_ check for DTZ later
621                                                  * when getting default timezone
622                                                  */
623                                                 tmask |= DTK_M(TZ);
624                                                 tm->tm_isdst = 1;
625                                                 if (tzp == NULL)
626                                                         return -1;
627                                                 *tzp = val * 60;
628                                                 break;
629
630                                         case TZ:
631                                                 tm->tm_isdst = 0;
632                                                 if (tzp == NULL)
633                                                         return -1;
634                                                 *tzp = val * 60;
635                                                 break;
636
637                                         case IGNOREFIELD:
638                                                 break;
639
640                                         case AMPM:
641                                                 mer = val;
642                                                 break;
643
644                                         case ADBC:
645                                                 bc = (val == BC);
646                                                 break;
647
648                                         case DOW:
649                                                 tm->tm_wday = val;
650                                                 break;
651
652                                         default:
653                                                 return -1;
654                                 }
655                                 break;
656
657                         default:
658                                 return -1;
659                 }
660
661 #ifdef DATEDEBUG
662                 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
663                            i, field[i], fmask, tmask, val);
664 #endif
665
666                 if (tmask & fmask)
667                         return -1;
668                 fmask |= tmask;
669         }
670
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);
674
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;
681
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
687
688         if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
689                 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
690
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))))
694         {
695
696                 /*
697                  * daylight savings time modifier but no standard timezone? then
698                  * error
699                  */
700                 if (fmask & DTK_M(DTZMOD))
701                         return -1;
702
703                 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
704                 {
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;
712
713 #ifdef HAVE_INT_TIMEZONE
714                         *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
715
716 #else                                                   /* !HAVE_INT_TIMEZONE */
717                         *tzp = -(tm->tm_gmtoff);        /* tm_gmtoff is Sun/DEC-ism */
718 #endif
719
720 #else                                                   /* !USE_POSIX_TIME */
721                         *tzp = CTimeZone;
722 #endif
723                 }
724                 else
725                 {
726                         tm->tm_isdst = 0;
727                         *tzp = 0;
728                 }
729         }
730
731         return 0;
732 }       /* DecodeDateTime() */
733
734
735 /* DecodeTimeOnly()
736  * Interpret parsed string as time fields only.
737  */
738 int
739 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
740 {
741         int                     fmask,
742                                 tmask,
743                                 type;
744         int                     i;
745         int                     flen,
746                                 val;
747         int                     mer = HR24;
748
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;
756
757         fmask = DTK_DATE_M;
758
759         for (i = 0; i < nf; i++)
760         {
761 #ifdef DATEDEBUG
762                 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
763 #endif
764                 switch (ftype[i])
765                 {
766                         case DTK_TIME:
767                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
768                                         return -1;
769                                 break;
770
771                         case DTK_NUMBER:
772                                 flen = strlen(field[i]);
773
774                                 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
775                                         return -1;
776                                 break;
777
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;
786
787                                 tmask = DTK_M(type);
788                                 switch (type)
789                                 {
790                                         case RESERV:
791 #ifdef DATEDEBUG
792                                                 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
793 #endif
794                                                 switch (val)
795                                                 {
796
797                                                         default:
798                                                                 return -1;
799                                                 }
800
801                                                 break;
802
803                                         case IGNOREFIELD:
804                                                 break;
805
806                                         case AMPM:
807                                                 mer = val;
808                                                 break;
809
810                                         default:
811                                                 return -1;
812                                 }
813                                 break;
814
815                         default:
816                                 return -1;
817                 }
818
819                 if (tmask & fmask)
820                         return -1;
821                 fmask |= tmask;
822
823 #ifdef DATEDEBUG
824                 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
825 #endif
826         }
827
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
832
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;
839
840         if ((fmask & DTK_TIME_M) != DTK_TIME_M)
841                 return -1;
842
843         return 0;
844 }       /* DecodeTimeOnly() */
845
846
847 /* DecodeDate()
848  * Decode date string which includes delimiters.
849  * Insist on a complete set of fields.
850  */
851 static int
852 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
853 {
854         double          fsec;
855
856         int                     nf = 0;
857         int                     i,
858                                 len;
859         int                     type,
860                                 val,
861                                 dmask = 0;
862         char       *field[MAXDATEFIELDS];
863
864         /* parse this string... */
865         while ((*str != '\0') && (nf < MAXDATEFIELDS))
866         {
867                 /* skip field separators */
868                 while (!isalnum(*str))
869                         str++;
870
871                 field[nf] = str;
872                 if (isdigit(*str))
873                 {
874                         while (isdigit(*str))
875                                 str++;
876                 }
877                 else if (isalpha(*str))
878                 {
879                         while (isalpha(*str))
880                                 str++;
881                 }
882
883                 if (*str != '\0')
884                         *str++ = '\0';
885                 nf++;
886         }
887
888         /* don't allow too many fields */
889         if (nf > 3)
890                 return -1;
891
892         *tmask = 0;
893
894         /* look first for text fields, since that will be unambiguous month */
895         for (i = 0; i < nf; i++)
896         {
897                 if (isalpha(*field[i]))
898                 {
899                         type = DecodeSpecial(i, field[i], &val);
900                         if (type == IGNOREFIELD)
901                                 continue;
902
903                         dmask = DTK_M(type);
904                         switch (type)
905                         {
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;
912
913                                 default:
914 #ifdef DATEDEBUG
915                                         printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
916 #endif
917                                         return -1;
918                         }
919                         if (fmask & dmask)
920                                 return -1;
921
922                         fmask |= dmask;
923                         *tmask |= dmask;
924
925                         /* mark this field as being completed */
926                         field[i] = NULL;
927                 }
928         }
929
930         /* now pick up remaining numeric fields */
931         for (i = 0; i < nf; i++)
932         {
933                 if (field[i] == NULL)
934                         continue;
935
936                 if ((len = strlen(field[i])) <= 0)
937                         return -1;
938
939                 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
940                         return -1;
941
942                 if (fmask & dmask)
943                         return -1;
944
945                 fmask |= dmask;
946                 *tmask |= dmask;
947         }
948
949         return 0;
950 }       /* DecodeDate() */
951
952
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.
957  */
958 static int
959 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
960 {
961         char       *cp;
962
963         *tmask = DTK_TIME_M;
964
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')
971         {
972                 tm->tm_sec = 0;
973                 *fsec = 0;
974
975         }
976         else if (*cp != ':')
977         {
978                 return -1;
979
980         }
981         else
982         {
983                 str = cp + 1;
984                 tm->tm_sec = strtol(str, &cp, 10);
985                 if (*cp == '\0')
986                         *fsec = 0;
987                 else if (*cp == '.')
988                 {
989                         str = cp;
990                         *fsec = strtod(str, &cp);
991                         if (cp == str)
992                                 return -1;
993                 }
994                 else
995                         return -1;
996         }
997
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;
1003
1004         return 0;
1005 }       /* DecodeTime() */
1006
1007
1008 /* DecodeNumber()
1009  * Interpret numeric field as a date value in context.
1010  */
1011 static int
1012 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1013 {
1014         int                     val;
1015         char       *cp;
1016
1017         *tmask = 0;
1018
1019         val = strtol(str, &cp, 10);
1020         if (cp == str)
1021                 return -1;
1022         if (*cp == '.')
1023         {
1024                 *fsec = strtod(cp, &cp);
1025                 if (*cp != '\0')
1026                         return -1;
1027         }
1028
1029 #ifdef DATEDEBUG
1030         printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1031 #endif
1032
1033         /* enough digits to be unequivocal year? */
1034         if (flen == 4)
1035         {
1036 #ifdef DATEDEBUG
1037                 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1038 #endif
1039                 *tmask = DTK_M(YEAR);
1040
1041                 /* already have a year? then see if we can substitute... */
1042                 if (fmask & DTK_M(YEAR))
1043                 {
1044                         if ((!(fmask & DTK_M(DAY)))
1045                                 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1046                         {
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);
1052                         }
1053                 }
1054
1055                 tm->tm_year = val;
1056
1057                 /* special case day of year? */
1058         }
1059         else if ((flen == 3) && (fmask & DTK_M(YEAR))
1060                          && ((val >= 1) && (val <= 366)))
1061         {
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);
1066
1067                 /* already have year? then could be month */
1068         }
1069         else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1070                          && ((val >= 1) && (val <= 12)))
1071         {
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;
1077
1078                 /* no year and EuroDates enabled? then could be day */
1079         }
1080         else if ((EuroDates || (fmask & DTK_M(MONTH)))
1081                          && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1082                          && ((val >= 1) && (val <= 31)))
1083         {
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;
1089
1090         }
1091         else if ((!(fmask & DTK_M(MONTH)))
1092                          && ((val >= 1) && (val <= 12)))
1093         {
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;
1099
1100         }
1101         else if ((!(fmask & DTK_M(DAY)))
1102                          && ((val >= 1) && (val <= 31)))
1103         {
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;
1109
1110         }
1111         else if (!(fmask & DTK_M(YEAR)))
1112         {
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;
1122
1123         }
1124         else
1125                 return -1;
1126
1127         return 0;
1128 }       /* DecodeNumber() */
1129
1130
1131 /* DecodeNumberField()
1132  * Interpret numeric string as a concatenated date field.
1133  */
1134 static int
1135 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1136 {
1137         char       *cp;
1138
1139         /* yyyymmdd? */
1140         if (len == 8)
1141         {
1142 #ifdef DATEDEBUG
1143                 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1144 #endif
1145
1146                 *tmask = DTK_DATE_M;
1147
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);
1153
1154                 /* yymmdd or hhmmss? */
1155         }
1156         else if (len == 6)
1157         {
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)
1162                 {
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);
1172
1173                 }
1174                 else
1175                 {
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);
1185                 }
1186
1187         }
1188         else if (strchr(str, '.') != NULL)
1189         {
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);
1203
1204         }
1205         else
1206                 return -1;
1207
1208         return 0;
1209 }       /* DecodeNumberField() */
1210
1211
1212 /* DecodeTimezone()
1213  * Interpret string as a numeric timezone.
1214  */
1215 static int
1216 DecodeTimezone(char *str, int *tzp)
1217 {
1218         int                     tz;
1219         int                     hr,
1220                                 min;
1221         char       *cp;
1222         int                     len;
1223
1224         /* assume leading character is "+" or "-" */
1225         hr = strtol((str + 1), &cp, 10);
1226
1227         /* explicit delimiter? */
1228         if (*cp == ':')
1229         {
1230                 min = strtol((cp + 1), &cp, 10);
1231
1232                 /* otherwise, might have run things together... */
1233         }
1234         else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1235         {
1236                 min = strtol((str + len - 2), &cp, 10);
1237                 *(str + len - 2) = '\0';
1238                 hr = strtol((str + 1), &cp, 10);
1239
1240         }
1241         else
1242                 min = 0;
1243
1244         tz = (hr * 60 + min) * 60;
1245         if (*str == '-')
1246                 tz = -tz;
1247
1248         *tzp = -tz;
1249         return *cp != '\0';
1250 }       /* DecodeTimezone() */
1251
1252
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.
1257  */
1258 static int
1259 DecodeSpecial(int field, char *lowtoken, int *val)
1260 {
1261         int                     type;
1262         datetkn    *tp;
1263
1264 #if USE_DATE_CACHE
1265         if ((datecache[field] != NULL)
1266                 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1267                 tp = datecache[field];
1268         else
1269         {
1270 #endif
1271                 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1272 #if USE_DATE_CACHE
1273         }
1274         datecache[field] = tp;
1275 #endif
1276         if (tp == NULL)
1277         {
1278                 type = IGNOREFIELD;
1279                 *val = 0;
1280         }
1281         else
1282         {
1283                 type = tp->type;
1284                 switch (type)
1285                 {
1286                         case TZ:
1287                         case DTZ:
1288                         case DTZMOD:
1289                                 *val = FROMVAL(tp);
1290                                 break;
1291
1292                         default:
1293                                 *val = tp->value;
1294                                 break;
1295                 }
1296         }
1297
1298         return type;
1299 }       /* DecodeSpecial() */
1300
1301
1302
1303 /* datebsearch()
1304  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
1305  * is WAY faster than the generic bsearch().
1306  */
1307 static datetkn *
1308 datebsearch(char *key, datetkn *base, unsigned int nel)
1309 {
1310         datetkn    *last = base + nel - 1,
1311                            *position;
1312         int                     result;
1313
1314         while (last >= base)
1315         {
1316                 position = base + ((last - base) >> 1);
1317                 result = key[0] - position->token[0];
1318                 if (result == 0)
1319                 {
1320                         result = strncmp(key, position->token, TOKMAXLEN);
1321                         if (result == 0)
1322                                 return position;
1323                 }
1324                 if (result < 0)
1325                         last = position - 1;
1326                 else
1327                         base = position + 1;
1328         }
1329         return NULL;
1330 }
1331
1332
1333