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