source: trunk/src/oleaut32/parsedt.c@ 5280

Last change on this file since 5280 was 4837, checked in by sandervl, 25 years ago

merged with Wine 12-22-2000

File size: 30.5 KB
Line 
1/*
2PostgreSQL Data Base Management System (formerly known as Postgres, then
3as Postgres95).
4
5Copyright (c) 1994-7 Regents of the University of California
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose, without fee, and without a written agreement
9is hereby granted, provided that the above copyright notice and this
10paragraph and the following two paragraphs appear in all copies.
11
12IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
13DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
16POSSIBILITY OF SUCH DAMAGE.
17
18THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
22PROVIDE 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
46static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
47static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
48static int DecodeNumber(int flen, char *field,
49 int fmask, int *tmask, struct tm * tm, double *fsec);
50static int DecodeNumberField(int len, char *str,
51 int fmask, int *tmask, struct tm * tm, double *fsec);
52static int DecodeSpecial(int field, char *lowtoken, int *val);
53static int DecodeTime(char *str, int fmask, int *tmask,
54 struct tm * tm, double *fsec);
55static int DecodeTimezone(char *str, int *tzp);
56
57#define USE_DATE_CACHE 1
58#define ROUND_ALL 0
59
60static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
61
62static const char * const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
63"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
64
65static 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 */
71int DateStyle;
72bool EuroDates;
73int 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 */
109static 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
262static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
263
264
265
266#if USE_DATE_CACHE
267datetkn *datecache[MAXDATEFIELDS] = {NULL};
268
269datetkn *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
303int
304date2j(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
312void
313j2date(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 */
353int
354ParseDateTime(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 */
503int
504DecodeDateTime(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 */
739int
740DecodeTimeOnly(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 */
852static int
853DecodeDate(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 */
959static int
960DecodeTime(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 */
1012static int
1013DecodeNumber(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 */
1135static int
1136DecodeNumberField(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 */
1216static int
1217DecodeTimezone(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 */
1259static int
1260DecodeSpecial(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 */
1308static datetkn *
1309datebsearch(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
Note: See TracBrowser for help on using the repository browser.