source: trunk/src/oleaut32/parsedt.cpp@ 1036

Last change on this file since 1036 was 632, checked in by sandervl, 26 years ago

Created (WINE Port of OLEAUT32)

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