WIN_FindWindow: use GetExeptr(pWnd->hInstance) for CLASS_FindClassByAtom.
[wine.git] / ole / parsedt.c
blob3d4228733f5f0a3a6dc239aada6305cf1ff7ab7b
1 /*
2 PostgreSQL Data Base Management System (formerly known as Postgres, then
3 as Postgres95).
5 Copyright (c) 1994-7 Regents of the University of California
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose, without fee, and without a written agreement
9 is hereby granted, provided that the above copyright notice and this
10 paragraph and the following two paragraphs appear in all copies.
12 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
13 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
16 POSSIBILITY OF SUCH DAMAGE.
18 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
22 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 /*-------------------------------------------------------------------------
27 * dt.c--
28 * Functions for the built-in type "dt".
30 * Copyright (c) 1994, Regents of the University of California
33 * IDENTIFICATION
34 * $Header$
36 *-------------------------------------------------------------------------
38 #include <time.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <math.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <sys/timeb.h>
48 #include "parsedt.h"
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);
61 #define USE_DATE_CACHE 1
62 #define ROUND_ALL 0
64 int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
66 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
67 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
69 char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
70 "Thursday", "Friday", "Saturday", NULL};
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)
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))))))
90 /*****************************************************************************
91 * PRIVATE ROUTINES *
92 *****************************************************************************/
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))
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.
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 */
260 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
264 #if USE_DATE_CACHE
265 datetkn *datecache[MAXDATEFIELDS] = {NULL};
267 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
269 #endif
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).
280 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
281 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
283 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
284 * now at Aerospace Corp. (hi, Henry!)
286 * These routines will be used by other date/time packages - tgl 97/02/25
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
293 #define JULIAN_MINYEAR (-4713)
294 #define JULIAN_MINMONTH (11)
295 #define JULIAN_MINDAY (23)
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)))))
302 date2j(int y, int m, int d)
304 int m12 = (m - 14) / 12;
306 return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
307 - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
308 } /* date2j() */
310 void
311 j2date(int jd, int *year, int *month, int *day)
313 int j,
318 int i,
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;
333 *year = y;
334 *month = m;
335 *day = d;
336 return;
337 } /* j2date() */
343 * parse and convert date in timestr (the normal interface)
345 * Returns the number of seconds since epoch (J2000)
348 /* ParseDateTime()
349 * Break string into tokens based on a date/time context.
352 ParseDateTime(char *timestr, char *lowstr,
353 char **field, int *ftype, int maxfields, int *numfields)
355 int nf = 0;
356 char *cp = timestr;
357 char *lp = lowstr;
359 #ifdef DATEDEBUG
360 printf("ParseDateTime- input string is %s\n", timestr);
361 #endif
362 /* outer loop through fields */
363 while (*cp != '\0')
365 field[nf] = lp;
367 /* leading digit? then date or time */
368 if (isdigit(*cp) || (*cp == '.'))
370 *lp++ = *cp++;
371 while (isdigit(*cp))
372 *lp++ = *cp++;
373 /* time field? */
374 if (*cp == ':')
376 ftype[nf] = DTK_TIME;
377 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
378 *lp++ = *cp++;
381 /* date field? allow embedded text month */
382 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
384 ftype[nf] = DTK_DATE;
385 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
386 *lp++ = tolower(*cp++);
391 * otherwise, number only and will determine year, month, or
392 * day later
394 else
395 ftype[nf] = DTK_NUMBER;
400 * text? then date string, month, day of week, special, or
401 * timezone
403 else if (isalpha(*cp))
405 ftype[nf] = DTK_STRING;
406 *lp++ = tolower(*cp++);
407 while (isalpha(*cp))
408 *lp++ = tolower(*cp++);
410 /* full date string with leading text month? */
411 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
413 ftype[nf] = DTK_DATE;
414 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
415 *lp++ = tolower(*cp++);
418 /* skip leading spaces */
420 else if (isspace(*cp))
422 cp++;
423 continue;
425 /* sign? then special or numeric timezone */
427 else if ((*cp == '+') || (*cp == '-'))
429 *lp++ = *cp++;
430 /* soak up leading whitespace */
431 while (isspace(*cp))
432 cp++;
433 /* numeric timezone? */
434 if (isdigit(*cp))
436 ftype[nf] = DTK_TZ;
437 *lp++ = *cp++;
438 while (isdigit(*cp) || (*cp == ':'))
439 *lp++ = *cp++;
441 /* special? */
443 else if (isalpha(*cp))
445 ftype[nf] = DTK_SPECIAL;
446 *lp++ = tolower(*cp++);
447 while (isalpha(*cp))
448 *lp++ = tolower(*cp++);
450 /* otherwise something wrong... */
452 else
453 return -1;
455 /* ignore punctuation but use as delimiter */
457 else if (ispunct(*cp))
459 cp++;
460 continue;
463 else
464 return -1;
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
476 *numfields = nf;
478 return 0;
479 } /* ParseDateTime() */
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"
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
502 DecodeDateTime(char **field, int *ftype, int nf,
503 int *dtype, struct tm * tm, double *fsec, int *tzp)
505 int fmask = 0,
506 tmask,
507 type;
508 int i;
509 int flen,
510 val;
511 int mer = HR24;
512 int bc = FALSE;
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;
524 for (i = 0; i < nf; i++)
526 #ifdef DATEDEBUG
527 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
528 #endif
529 switch (ftype[i])
531 case DTK_DATE:
532 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
533 return -1;
534 break;
536 case DTK_TIME:
537 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
538 return -1;
541 * check upper limit on hours; other limits checked in
542 * DecodeTime()
544 if (tm->tm_hour > 23)
545 return -1;
546 break;
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;
556 case DTK_NUMBER:
557 flen = strlen(field[i]);
559 if (flen > 4)
561 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
562 return -1;
565 else
567 if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
568 return -1;
570 break;
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;
581 tmask = DTK_M(type);
582 switch (type)
584 case RESERV:
585 #ifdef DATEDEBUG
586 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
587 #endif
588 switch (val)
591 default:
592 *dtype = val;
595 break;
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;
605 * daylight savings time modifier (solves "MET
606 * DST" syntax)
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;
616 case DTZ:
619 * set mask for TZ here _or_ check for DTZ later
620 * when getting default timezone
622 tmask |= DTK_M(TZ);
623 tm->tm_isdst = 1;
624 if (tzp == NULL)
625 return -1;
626 *tzp = val * 60;
627 break;
629 case TZ:
630 tm->tm_isdst = 0;
631 if (tzp == NULL)
632 return -1;
633 *tzp = val * 60;
634 break;
636 case IGNOREFIELD:
637 break;
639 case AMPM:
640 mer = val;
641 break;
643 case ADBC:
644 bc = (val == BC);
645 break;
647 case DOW:
648 tm->tm_wday = val;
649 break;
651 default:
652 return -1;
654 break;
656 default:
657 return -1;
660 #ifdef DATEDEBUG
661 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
662 i, field[i], fmask, tmask, val);
663 #endif
665 if (tmask & fmask)
666 return -1;
667 fmask |= tmask;
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);
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;
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
687 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
688 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
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))))
696 * daylight savings time modifier but no standard timezone? then
697 * error
699 if (fmask & DTK_M(DTZMOD))
700 return -1;
702 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
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;
712 #ifdef HAVE_INT_TIMEZONE
713 *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
715 #else /* !HAVE_INT_TIMEZONE */
716 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
717 #endif
719 #else /* !USE_POSIX_TIME */
720 *tzp = CTimeZone;
721 #endif
723 else
725 tm->tm_isdst = 0;
726 *tzp = 0;
730 return 0;
731 } /* DecodeDateTime() */
734 /* DecodeTimeOnly()
735 * Interpret parsed string as time fields only.
738 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
740 int fmask,
741 tmask,
742 type;
743 int i;
744 int flen,
745 val;
746 int mer = HR24;
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;
756 fmask = DTK_DATE_M;
758 for (i = 0; i < nf; i++)
760 #ifdef DATEDEBUG
761 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
762 #endif
763 switch (ftype[i])
765 case DTK_TIME:
766 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
767 return -1;
768 break;
770 case DTK_NUMBER:
771 flen = strlen(field[i]);
773 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
774 return -1;
775 break;
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;
786 tmask = DTK_M(type);
787 switch (type)
789 case RESERV:
790 #ifdef DATEDEBUG
791 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
792 #endif
793 switch (val)
796 default:
797 return -1;
800 break;
802 case IGNOREFIELD:
803 break;
805 case AMPM:
806 mer = val;
807 break;
809 default:
810 return -1;
812 break;
814 default:
815 return -1;
818 if (tmask & fmask)
819 return -1;
820 fmask |= tmask;
822 #ifdef DATEDEBUG
823 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
824 #endif
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
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;
839 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
840 return -1;
842 return 0;
843 } /* DecodeTimeOnly() */
846 /* DecodeDate()
847 * Decode date string which includes delimiters.
848 * Insist on a complete set of fields.
850 static int
851 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
853 double fsec;
855 int nf = 0;
856 int i,
857 len;
858 int type,
859 val,
860 dmask = 0;
861 char *field[MAXDATEFIELDS];
863 /* parse this string... */
864 while ((*str != '\0') && (nf < MAXDATEFIELDS))
866 /* skip field separators */
867 while (!isalnum(*str))
868 str++;
870 field[nf] = str;
871 if (isdigit(*str))
873 while (isdigit(*str))
874 str++;
876 else if (isalpha(*str))
878 while (isalpha(*str))
879 str++;
882 if (*str != '\0')
883 *str++ = '\0';
884 nf++;
887 /* don't allow too many fields */
888 if (nf > 3)
889 return -1;
891 *tmask = 0;
893 /* look first for text fields, since that will be unambiguous month */
894 for (i = 0; i < nf; i++)
896 if (isalpha(*field[i]))
898 type = DecodeSpecial(i, field[i], &val);
899 if (type == IGNOREFIELD)
900 continue;
902 dmask = DTK_M(type);
903 switch (type)
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;
912 default:
913 #ifdef DATEDEBUG
914 printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
915 #endif
916 return -1;
918 if (fmask & dmask)
919 return -1;
921 fmask |= dmask;
922 *tmask |= dmask;
924 /* mark this field as being completed */
925 field[i] = NULL;
929 /* now pick up remaining numeric fields */
930 for (i = 0; i < nf; i++)
932 if (field[i] == NULL)
933 continue;
935 if ((len = strlen(field[i])) <= 0)
936 return -1;
938 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
939 return -1;
941 if (fmask & dmask)
942 return -1;
944 fmask |= dmask;
945 *tmask |= dmask;
948 return 0;
949 } /* DecodeDate() */
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.
957 static int
958 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
960 char *cp;
962 *tmask = DTK_TIME_M;
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')
971 tm->tm_sec = 0;
972 *fsec = 0;
975 else if (*cp != ':')
977 return -1;
980 else
982 str = cp + 1;
983 tm->tm_sec = strtol(str, &cp, 10);
984 if (*cp == '\0')
985 *fsec = 0;
986 else if (*cp == '.')
988 str = cp;
989 *fsec = strtod(str, &cp);
990 if (cp == str)
991 return -1;
993 else
994 return -1;
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;
1003 return 0;
1004 } /* DecodeTime() */
1007 /* DecodeNumber()
1008 * Interpret numeric field as a date value in context.
1010 static int
1011 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1013 int val;
1014 char *cp;
1016 *tmask = 0;
1018 val = strtol(str, &cp, 10);
1019 if (cp == str)
1020 return -1;
1021 if (*cp == '.')
1023 *fsec = strtod(cp, &cp);
1024 if (*cp != '\0')
1025 return -1;
1028 #ifdef DATEDEBUG
1029 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1030 #endif
1032 /* enough digits to be unequivocal year? */
1033 if (flen == 4)
1035 #ifdef DATEDEBUG
1036 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1037 #endif
1038 *tmask = DTK_M(YEAR);
1040 /* already have a year? then see if we can substitute... */
1041 if (fmask & DTK_M(YEAR))
1043 if ((!(fmask & DTK_M(DAY)))
1044 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
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);
1054 tm->tm_year = val;
1056 /* special case day of year? */
1058 else if ((flen == 3) && (fmask & DTK_M(YEAR))
1059 && ((val >= 1) && (val <= 366)))
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);
1066 /* already have year? then could be month */
1068 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1069 && ((val >= 1) && (val <= 12)))
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;
1077 /* no year and EuroDates enabled? then could be day */
1079 else if ((EuroDates || (fmask & DTK_M(MONTH)))
1080 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1081 && ((val >= 1) && (val <= 31)))
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;
1090 else if ((!(fmask & DTK_M(MONTH)))
1091 && ((val >= 1) && (val <= 12)))
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;
1100 else if ((!(fmask & DTK_M(DAY)))
1101 && ((val >= 1) && (val <= 31)))
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;
1110 else if (!(fmask & DTK_M(YEAR)))
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;
1123 else
1124 return -1;
1126 return 0;
1127 } /* DecodeNumber() */
1130 /* DecodeNumberField()
1131 * Interpret numeric string as a concatenated date field.
1133 static int
1134 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1136 char *cp;
1138 /* yyyymmdd? */
1139 if (len == 8)
1141 #ifdef DATEDEBUG
1142 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1143 #endif
1145 *tmask = DTK_DATE_M;
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);
1153 /* yymmdd or hhmmss? */
1155 else if (len == 6)
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)
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);
1173 else
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);
1187 else if (strchr(str, '.') != NULL)
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);
1204 else
1205 return -1;
1207 return 0;
1208 } /* DecodeNumberField() */
1211 /* DecodeTimezone()
1212 * Interpret string as a numeric timezone.
1214 static int
1215 DecodeTimezone(char *str, int *tzp)
1217 int tz;
1218 int hr,
1219 min;
1220 char *cp;
1221 int len;
1223 /* assume leading character is "+" or "-" */
1224 hr = strtol((str + 1), &cp, 10);
1226 /* explicit delimiter? */
1227 if (*cp == ':')
1229 min = strtol((cp + 1), &cp, 10);
1231 /* otherwise, might have run things together... */
1233 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1235 min = strtol((str + len - 2), &cp, 10);
1236 *(str + len - 2) = '\0';
1237 hr = strtol((str + 1), &cp, 10);
1240 else
1241 min = 0;
1243 tz = (hr * 60 + min) * 60;
1244 if (*str == '-')
1245 tz = -tz;
1247 *tzp = -tz;
1248 return *cp != '\0';
1249 } /* DecodeTimezone() */
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.
1257 static int
1258 DecodeSpecial(int field, char *lowtoken, int *val)
1260 int type;
1261 datetkn *tp;
1263 #if USE_DATE_CACHE
1264 if ((datecache[field] != NULL)
1265 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1266 tp = datecache[field];
1267 else
1269 #endif
1270 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1271 #if USE_DATE_CACHE
1273 datecache[field] = tp;
1274 #endif
1275 if (tp == NULL)
1277 type = IGNOREFIELD;
1278 *val = 0;
1280 else
1282 type = tp->type;
1283 switch (type)
1285 case TZ:
1286 case DTZ:
1287 case DTZMOD:
1288 *val = FROMVAL(tp);
1289 break;
1291 default:
1292 *val = tp->value;
1293 break;
1297 return type;
1298 } /* DecodeSpecial() */
1302 /* datebsearch()
1303 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1304 * is WAY faster than the generic bsearch().
1306 static datetkn *
1307 datebsearch(char *key, datetkn *base, unsigned int nel)
1309 datetkn *last = base + nel - 1,
1310 *position;
1311 int result;
1313 while (last >= base)
1315 position = base + ((last - base) >> 1);
1316 result = key[0] - position->token[0];
1317 if (result == 0)
1319 result = strncmp(key, position->token, TOKMAXLEN);
1320 if (result == 0)
1321 return position;
1323 if (result < 0)
1324 last = position - 1;
1325 else
1326 base = position + 1;
1328 return NULL;