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

Last change on this file since 6688 was 6648, checked in by bird, 24 years ago

Added $Id:$ keyword.

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