For a radio button even if the initial style includes WS_TABSTOP the
[wine/wine64.git] / dlls / oleaut32 / parsedt.c
blob96762da45eb3b01bab6fe2629e2c4f35f258948f
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 *-------------------------------------------------------------------------
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>
44 #include "parsedt.h"
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);
57 #define USE_DATE_CACHE 1
58 #define ROUND_ALL 0
60 static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
62 static const char * const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
65 static const char * const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
66 "Thursday", "Friday", "Saturday", NULL};
68 /* those three vars are useless, and not even initialized, so
69 * I'd rather remove them all (EPP)
71 int DateStyle;
72 bool EuroDates;
73 int CTimeZone;
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)
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))))))
92 /*****************************************************************************
93 * PRIVATE ROUTINES *
94 *****************************************************************************/
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))
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.
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 */
262 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
266 #if USE_DATE_CACHE
267 datetkn *datecache[MAXDATEFIELDS] = {NULL};
269 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
271 #endif
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).
282 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
283 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
285 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
286 * now at Aerospace Corp. (hi, Henry!)
288 * These routines will be used by other date/time packages - tgl 97/02/25
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
295 #define JULIAN_MINYEAR (-4713)
296 #define JULIAN_MINMONTH (11)
297 #define JULIAN_MINDAY (23)
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)))))
304 date2j(int y, int m, int d)
306 int m12 = (m - 14) / 12;
308 return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
309 - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
310 } /* date2j() */
312 void
313 j2date(int jd, int *year, int *month, int *day)
315 int j,
320 int i,
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;
335 *year = y;
336 *month = m;
337 *day = d;
338 return;
339 } /* j2date() */
345 * parse and convert date in timestr (the normal interface)
347 * Returns the number of seconds since epoch (J2000)
350 /* ParseDateTime()
351 * Break string into tokens based on a date/time context.
354 ParseDateTime(char *timestr, char *lowstr,
355 char **field, int *ftype, int maxfields, int *numfields)
357 int nf = 0;
358 char *cp = timestr;
359 char *lp = lowstr;
361 #ifdef DATEDEBUG
362 printf("ParseDateTime- input string is %s\n", timestr);
363 #endif
364 /* outer loop through fields */
365 while (*cp != '\0')
367 field[nf] = lp;
369 /* leading digit? then date or time */
370 if (isdigit(*cp) || (*cp == '.'))
372 *lp++ = *cp++;
373 while (isdigit(*cp))
374 *lp++ = *cp++;
375 /* time field? */
376 if (*cp == ':')
378 ftype[nf] = DTK_TIME;
379 while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
380 *lp++ = *cp++;
383 /* date field? allow embedded text month */
384 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
386 ftype[nf] = DTK_DATE;
387 while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
388 *lp++ = tolower(*cp++);
393 * otherwise, number only and will determine year, month, or
394 * day later
396 else
397 ftype[nf] = DTK_NUMBER;
402 * text? then date string, month, day of week, special, or
403 * timezone
405 else if (isalpha(*cp))
407 ftype[nf] = DTK_STRING;
408 *lp++ = tolower(*cp++);
409 while (isalpha(*cp))
410 *lp++ = tolower(*cp++);
412 /* full date string with leading text month? */
413 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
415 ftype[nf] = DTK_DATE;
416 while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
417 *lp++ = tolower(*cp++);
420 /* skip leading spaces */
422 else if (isspace(*cp))
424 cp++;
425 continue;
427 /* sign? then special or numeric timezone */
429 else if ((*cp == '+') || (*cp == '-'))
431 *lp++ = *cp++;
432 /* soak up leading whitespace */
433 while (isspace(*cp))
434 cp++;
435 /* numeric timezone? */
436 if (isdigit(*cp))
438 ftype[nf] = DTK_TZ;
439 *lp++ = *cp++;
440 while (isdigit(*cp) || (*cp == ':'))
441 *lp++ = *cp++;
443 /* special? */
445 else if (isalpha(*cp))
447 ftype[nf] = DTK_SPECIAL;
448 *lp++ = tolower(*cp++);
449 while (isalpha(*cp))
450 *lp++ = tolower(*cp++);
452 /* otherwise something wrong... */
454 else
455 return -1;
457 /* ignore punctuation but use as delimiter */
459 else if (ispunct(*cp))
461 cp++;
462 continue;
465 else
466 return -1;
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
478 *numfields = nf;
480 return 0;
481 } /* ParseDateTime() */
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"
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
504 DecodeDateTime(char **field, int *ftype, int nf,
505 int *dtype, struct tm * tm, double *fsec, int *tzp)
507 int fmask = 0,
508 tmask,
509 type;
510 int i;
511 int flen,
512 val;
513 int mer = HR24;
514 int bc = FALSE;
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;
526 for (i = 0; i < nf; i++)
528 #ifdef DATEDEBUG
529 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
530 #endif
531 switch (ftype[i])
533 case DTK_DATE:
534 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
535 return -1;
536 break;
538 case DTK_TIME:
539 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
540 return -1;
543 * check upper limit on hours; other limits checked in
544 * DecodeTime()
546 if (tm->tm_hour > 23)
547 return -1;
548 break;
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;
558 case DTK_NUMBER:
559 flen = strlen(field[i]);
561 if (flen > 4)
563 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
564 return -1;
567 else
569 if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec) != 0)
570 return -1;
572 break;
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;
583 tmask = DTK_M(type);
584 switch (type)
586 case RESERV:
587 #ifdef DATEDEBUG
588 printf("DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
589 #endif
590 switch (val)
593 default:
594 *dtype = val;
597 break;
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;
607 * daylight savings time modifier (solves "MET
608 * DST" syntax)
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;
618 case DTZ:
621 * set mask for TZ here _or_ check for DTZ later
622 * when getting default timezone
624 tmask |= DTK_M(TZ);
625 tm->tm_isdst = 1;
626 if (tzp == NULL)
627 return -1;
628 *tzp = val * 60;
629 break;
631 case TZ:
632 tm->tm_isdst = 0;
633 if (tzp == NULL)
634 return -1;
635 *tzp = val * 60;
636 break;
638 case IGNOREFIELD:
639 break;
641 case AMPM:
642 mer = val;
643 break;
645 case ADBC:
646 bc = (val == BC);
647 break;
649 case DOW:
650 tm->tm_wday = val;
651 break;
653 default:
654 return -1;
656 break;
658 default:
659 return -1;
662 #ifdef DATEDEBUG
663 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
664 i, field[i], fmask, tmask, val);
665 #endif
667 if (tmask & fmask)
668 return -1;
669 fmask |= tmask;
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);
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;
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
689 if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) != DTK_DATE_M))
690 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
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))))
698 * daylight savings time modifier but no standard timezone? then
699 * error
701 if (fmask & DTK_M(DTZMOD))
702 return -1;
704 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
706 /* FIXME: The code below is not correct */
707 #if 0 /* defined(USE_POSIX_TIME) */
708 tm->tm_year -= 1900;
709 tm->tm_mon -= 1;
710 tm->tm_isdst = -1;
711 mktime(tm);
712 tm->tm_year += 1900;
713 tm->tm_mon += 1;
715 #if 0 /* defined(HAVE_INT_TIMEZONE) */
716 *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
718 #else /* !HAVE_INT_TIMEZONE */
719 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
720 #endif
722 #else /* !USE_POSIX_TIME */
723 *tzp = CTimeZone;
724 #endif
726 else
728 tm->tm_isdst = 0;
729 *tzp = 0;
733 return 0;
734 } /* DecodeDateTime() */
737 /* DecodeTimeOnly()
738 * Interpret parsed string as time fields only.
741 DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
743 int fmask,
744 tmask,
745 type;
746 int i;
747 int flen,
748 val;
749 int mer = HR24;
751 *dtype = DTK_TIME;
752 tm->tm_hour = 0;
753 tm->tm_min = 0;
754 tm->tm_sec = 0;
755 tm->tm_isdst = -1; /* don't know daylight savings time status
756 * apriori */
757 *fsec = 0;
759 fmask = DTK_DATE_M;
761 for (i = 0; i < nf; i++)
763 #ifdef DATEDEBUG
764 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i, field[i], ftype[i]);
765 #endif
766 switch (ftype[i])
768 case DTK_TIME:
769 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
770 return -1;
771 break;
773 case DTK_NUMBER:
774 flen = strlen(field[i]);
776 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec) != 0)
777 return -1;
778 break;
780 case DTK_STRING:
781 case DTK_SPECIAL:
782 type = DecodeSpecial(i, field[i], &val);
783 #ifdef DATEDEBUG
784 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i, field[i], type, val);
785 #endif
786 if (type == IGNOREFIELD)
787 continue;
789 tmask = DTK_M(type);
790 switch (type)
792 case RESERV:
793 #ifdef DATEDEBUG
794 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field[i], val);
795 #endif
796 switch (val)
799 default:
800 return -1;
803 break;
805 case IGNOREFIELD:
806 break;
808 case AMPM:
809 mer = val;
810 break;
812 default:
813 return -1;
815 break;
817 default:
818 return -1;
821 if (tmask & fmask)
822 return -1;
823 fmask |= tmask;
825 #ifdef DATEDEBUG
826 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i, field[i], val);
827 #endif
830 #ifdef DATEDEBUG
831 printf("DecodeTimeOnly- mask %08x (%08x)", fmask, DTK_TIME_M);
832 printf(" %02d:%02d:%02d (%f)\n", tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec);
833 #endif
835 if ((mer != HR24) && (tm->tm_hour > 12))
836 return -1;
837 if ((mer == AM) && (tm->tm_hour == 12))
838 tm->tm_hour = 0;
839 else if ((mer == PM) && (tm->tm_hour != 12))
840 tm->tm_hour += 12;
842 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
843 return -1;
845 return 0;
846 } /* DecodeTimeOnly() */
849 /* DecodeDate()
850 * Decode date string which includes delimiters.
851 * Insist on a complete set of fields.
853 static int
854 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
856 double fsec;
858 int nf = 0;
859 int i,
860 len;
861 int type,
862 val,
863 dmask = 0;
864 char *field[MAXDATEFIELDS];
866 /* parse this string... */
867 while ((*str != '\0') && (nf < MAXDATEFIELDS))
869 /* skip field separators */
870 while (!isalnum(*str))
871 str++;
873 field[nf] = str;
874 if (isdigit(*str))
876 while (isdigit(*str))
877 str++;
879 else if (isalpha(*str))
881 while (isalpha(*str))
882 str++;
885 if (*str != '\0')
886 *str++ = '\0';
887 nf++;
890 /* don't allow too many fields */
891 if (nf > 3)
892 return -1;
894 *tmask = 0;
896 /* look first for text fields, since that will be unambiguous month */
897 for (i = 0; i < nf; i++)
899 if (isalpha(*field[i]))
901 type = DecodeSpecial(i, field[i], &val);
902 if (type == IGNOREFIELD)
903 continue;
905 dmask = DTK_M(type);
906 switch (type)
908 case MONTH:
909 #ifdef DATEDEBUG
910 printf("DecodeDate- month field %s value is %d\n", field[i], val);
911 #endif
912 tm->tm_mon = val;
913 break;
915 default:
916 #ifdef DATEDEBUG
917 printf("DecodeDate- illegal field %s value is %d\n", field[i], val);
918 #endif
919 return -1;
921 if (fmask & dmask)
922 return -1;
924 fmask |= dmask;
925 *tmask |= dmask;
927 /* mark this field as being completed */
928 field[i] = NULL;
932 /* now pick up remaining numeric fields */
933 for (i = 0; i < nf; i++)
935 if (field[i] == NULL)
936 continue;
938 if ((len = strlen(field[i])) <= 0)
939 return -1;
941 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec) != 0)
942 return -1;
944 if (fmask & dmask)
945 return -1;
947 fmask |= dmask;
948 *tmask |= dmask;
951 return 0;
952 } /* DecodeDate() */
955 /* DecodeTime()
956 * Decode time string which includes delimiters.
957 * Only check the lower limit on hours, since this same code
958 * can be used to represent time spans.
960 static int
961 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
963 char *cp;
965 *tmask = DTK_TIME_M;
967 tm->tm_hour = strtol(str, &cp, 10);
968 if (*cp != ':')
969 return -1;
970 str = cp + 1;
971 tm->tm_min = strtol(str, &cp, 10);
972 if (*cp == '\0')
974 tm->tm_sec = 0;
975 *fsec = 0;
978 else if (*cp != ':')
980 return -1;
983 else
985 str = cp + 1;
986 tm->tm_sec = strtol(str, &cp, 10);
987 if (*cp == '\0')
988 *fsec = 0;
989 else if (*cp == '.')
991 str = cp;
992 *fsec = strtod(str, &cp);
993 if (cp == str)
994 return -1;
996 else
997 return -1;
1000 /* do a sanity check */
1001 if ((tm->tm_hour < 0)
1002 || (tm->tm_min < 0) || (tm->tm_min > 59)
1003 || (tm->tm_sec < 0) || (tm->tm_sec > 59))
1004 return -1;
1006 return 0;
1007 } /* DecodeTime() */
1010 /* DecodeNumber()
1011 * Interpret numeric field as a date value in context.
1013 static int
1014 DecodeNumber(int flen, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1016 int val;
1017 char *cp;
1019 *tmask = 0;
1021 val = strtol(str, &cp, 10);
1022 if (cp == str)
1023 return -1;
1024 if (*cp == '.')
1026 *fsec = strtod(cp, &cp);
1027 if (*cp != '\0')
1028 return -1;
1031 #ifdef DATEDEBUG
1032 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str, val, fmask, *tmask);
1033 #endif
1035 /* enough digits to be unequivocal year? */
1036 if (flen == 4)
1038 #ifdef DATEDEBUG
1039 printf("DecodeNumber- match %d (%s) as year\n", val, str);
1040 #endif
1041 *tmask = DTK_M(YEAR);
1043 /* already have a year? then see if we can substitute... */
1044 if (fmask & DTK_M(YEAR))
1046 if ((!(fmask & DTK_M(DAY)))
1047 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1049 #ifdef DATEDEBUG
1050 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm->tm_mday);
1051 #endif
1052 tm->tm_mday = tm->tm_year;
1053 *tmask = DTK_M(DAY);
1057 tm->tm_year = val;
1059 /* special case day of year? */
1061 else if ((flen == 3) && (fmask & DTK_M(YEAR))
1062 && ((val >= 1) && (val <= 366)))
1064 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1065 tm->tm_yday = val;
1066 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
1067 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1069 /* already have year? then could be month */
1071 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1072 && ((val >= 1) && (val <= 12)))
1074 #ifdef DATEDEBUG
1075 printf("DecodeNumber- match %d (%s) as month\n", val, str);
1076 #endif
1077 *tmask = DTK_M(MONTH);
1078 tm->tm_mon = val;
1080 /* no year and EuroDates enabled? then could be day */
1082 else if ((EuroDates || (fmask & DTK_M(MONTH)))
1083 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1084 && ((val >= 1) && (val <= 31)))
1086 #ifdef DATEDEBUG
1087 printf("DecodeNumber- match %d (%s) as day\n", val, str);
1088 #endif
1089 *tmask = DTK_M(DAY);
1090 tm->tm_mday = val;
1093 else if ((!(fmask & DTK_M(MONTH)))
1094 && ((val >= 1) && (val <= 12)))
1096 #ifdef DATEDEBUG
1097 printf("DecodeNumber- (2) match %d (%s) as month\n", val, str);
1098 #endif
1099 *tmask = DTK_M(MONTH);
1100 tm->tm_mon = val;
1103 else if ((!(fmask & DTK_M(DAY)))
1104 && ((val >= 1) && (val <= 31)))
1106 #ifdef DATEDEBUG
1107 printf("DecodeNumber- (2) match %d (%s) as day\n", val, str);
1108 #endif
1109 *tmask = DTK_M(DAY);
1110 tm->tm_mday = val;
1113 else if (!(fmask & DTK_M(YEAR)))
1115 #ifdef DATEDEBUG
1116 printf("DecodeNumber- (2) match %d (%s) as year\n", val, str);
1117 #endif
1118 *tmask = DTK_M(YEAR);
1119 tm->tm_year = val;
1120 if (tm->tm_year < 70)
1121 tm->tm_year += 2000;
1122 else if (tm->tm_year < 100)
1123 tm->tm_year += 1900;
1126 else
1127 return -1;
1129 return 0;
1130 } /* DecodeNumber() */
1133 /* DecodeNumberField()
1134 * Interpret numeric string as a concatenated date field.
1136 static int
1137 DecodeNumberField(int len, char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1139 char *cp;
1141 /* yyyymmdd? */
1142 if (len == 8)
1144 #ifdef DATEDEBUG
1145 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1146 #endif
1148 *tmask = DTK_DATE_M;
1150 tm->tm_mday = atoi(str + 6);
1151 *(str + 6) = '\0';
1152 tm->tm_mon = atoi(str + 4);
1153 *(str + 4) = '\0';
1154 tm->tm_year = atoi(str + 0);
1156 /* yymmdd or hhmmss? */
1158 else if (len == 6)
1160 #ifdef DATEDEBUG
1161 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1162 #endif
1163 if (fmask & DTK_DATE_M)
1165 #ifdef DATEDEBUG
1166 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1167 #endif
1168 *tmask = DTK_TIME_M;
1169 tm->tm_sec = atoi(str + 4);
1170 *(str + 4) = '\0';
1171 tm->tm_min = atoi(str + 2);
1172 *(str + 2) = '\0';
1173 tm->tm_hour = atoi(str + 0);
1176 else
1178 #ifdef DATEDEBUG
1179 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1180 #endif
1181 *tmask = DTK_DATE_M;
1182 tm->tm_mday = atoi(str + 4);
1183 *(str + 4) = '\0';
1184 tm->tm_mon = atoi(str + 2);
1185 *(str + 2) = '\0';
1186 tm->tm_year = atoi(str + 0);
1190 else if (strchr(str, '.') != NULL)
1192 #ifdef DATEDEBUG
1193 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str, fmask, *tmask);
1194 #endif
1195 *tmask = DTK_TIME_M;
1196 tm->tm_sec = strtod((str + 4), &cp);
1197 if (cp == (str + 4))
1198 return -1;
1199 if (*cp == '.')
1200 *fsec = strtod(cp, NULL);
1201 *(str + 4) = '\0';
1202 tm->tm_min = strtod((str + 2), &cp);
1203 *(str + 2) = '\0';
1204 tm->tm_hour = strtod((str + 0), &cp);
1207 else
1208 return -1;
1210 return 0;
1211 } /* DecodeNumberField() */
1214 /* DecodeTimezone()
1215 * Interpret string as a numeric timezone.
1217 static int
1218 DecodeTimezone(char *str, int *tzp)
1220 int tz;
1221 int hr,
1222 min;
1223 char *cp;
1224 int len;
1226 /* assume leading character is "+" or "-" */
1227 hr = strtol((str + 1), &cp, 10);
1229 /* explicit delimiter? */
1230 if (*cp == ':')
1232 min = strtol((cp + 1), &cp, 10);
1234 /* otherwise, might have run things together... */
1236 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1238 min = strtol((str + len - 2), &cp, 10);
1239 *(str + len - 2) = '\0';
1240 hr = strtol((str + 1), &cp, 10);
1243 else
1244 min = 0;
1246 tz = (hr * 60 + min) * 60;
1247 if (*str == '-')
1248 tz = -tz;
1250 *tzp = -tz;
1251 return *cp != '\0';
1252 } /* DecodeTimezone() */
1255 /* DecodeSpecial()
1256 * Decode text string using lookup table.
1257 * Implement a cache lookup since it is likely that dates
1258 * will be related in format.
1260 static int
1261 DecodeSpecial(int field, char *lowtoken, int *val)
1263 int type;
1264 datetkn *tp;
1266 #if USE_DATE_CACHE
1267 if ((datecache[field] != NULL)
1268 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1269 tp = datecache[field];
1270 else
1272 #endif
1273 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1274 #if USE_DATE_CACHE
1276 datecache[field] = tp;
1277 #endif
1278 if (tp == NULL)
1280 type = IGNOREFIELD;
1281 *val = 0;
1283 else
1285 type = tp->type;
1286 switch (type)
1288 case TZ:
1289 case DTZ:
1290 case DTZMOD:
1291 *val = FROMVAL(tp);
1292 break;
1294 default:
1295 *val = tp->value;
1296 break;
1300 return type;
1301 } /* DecodeSpecial() */
1305 /* datebsearch()
1306 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1307 * is WAY faster than the generic bsearch().
1309 static datetkn *
1310 datebsearch(char *key, datetkn *base, unsigned int nel)
1312 datetkn *last = base + nel - 1,
1313 *position;
1314 int result;
1316 while (last >= base)
1318 position = base + ((last - base) >> 1);
1319 result = key[0] - position->token[0];
1320 if (result == 0)
1322 result = strncmp(key, position->token, TOKMAXLEN);
1323 if (result == 0)
1324 return position;
1326 if (result < 0)
1327 last = position - 1;
1328 else
1329 base = position + 1;
1331 return NULL;