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