2 PostgreSQL Data Base Management System (formerly known as Postgres, then
5 Copyright (c) 1994-7 Regents of the University of California
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose, without fee, and without a written agreement
9 is hereby granted, provided that the above copyright notice and this
10 paragraph and the following two paragraphs appear in all copies.
12 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
13 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
16 POSSIBILITY OF SUCH DAMAGE.
18 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
22 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 /*-------------------------------------------------------------------------
28 * Functions for the built-in type "dt".
30 * Copyright (c) 1994, Regents of the University of California
33 *-------------------------------------------------------------------------
40 #include <sys/types.h>
42 #include <sys/timeb.h>
46 static datetkn
*datebsearch(char *key
, datetkn
*base
, unsigned int nel
);
47 static int DecodeDate(char *str
, int fmask
, int *tmask
, struct tm
* tm
);
48 static int DecodeNumber(int flen
, char *field
,
49 int fmask
, int *tmask
, struct tm
* tm
, double *fsec
);
50 static int DecodeNumberField(int len
, char *str
,
51 int fmask
, int *tmask
, struct tm
* tm
, double *fsec
);
52 static int DecodeSpecial(int field
, char *lowtoken
, int *val
);
53 static int DecodeTime(char *str
, int fmask
, int *tmask
,
54 struct tm
* tm
, double *fsec
);
55 static int DecodeTimezone(char *str
, int *tzp
);
57 #define USE_DATE_CACHE 1
60 static const int mdays
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
62 static const char * const months
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
65 static const char * const days
[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
66 "Thursday", "Friday", "Saturday", NULL
};
68 /* those three vars are useless, and not even initialized, so
69 * I'd rather remove them all (EPP)
75 #define UTIME_MINYEAR (1901)
76 #define UTIME_MINMONTH (12)
77 #define UTIME_MINDAY (14)
78 #define UTIME_MAXYEAR (2038)
79 #define UTIME_MAXMONTH (01)
80 #define UTIME_MAXDAY (18)
82 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
83 || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
84 || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
85 && ((y < UTIME_MAXYEAR) \
86 || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
87 || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
92 /*****************************************************************************
94 *****************************************************************************/
96 /* definitions for squeezing values into "value" */
97 #define ABS_SIGNBIT (char) 0200
98 #define VALMASK (char) 0177
99 #define NEG(n) ((n)|ABS_SIGNBIT)
100 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
101 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
102 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
105 * to keep this table reasonably small, we divide the lexval for TZ and DTZ
106 * entries by 10 and truncate the text field at MAXTOKLEN characters.
107 * the text field is not guaranteed to be NULL-terminated.
109 static datetkn datetktbl
[] = {
110 /* text token lexval */
111 {EARLY
, RESERV
, DTK_EARLY
}, /* "-infinity" reserved for "early time" */
112 {"acsst", DTZ
, 63}, /* Cent. Australia */
113 {"acst", TZ
, 57}, /* Cent. Australia */
114 {DA_D
, ADBC
, AD
}, /* "ad" for years >= 0 */
115 {"abstime", IGNOREFIELD
, 0}, /* "abstime" for pre-v6.1 "Invalid
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 */
125 {"ast", TZ
, NEG(24)}, /* Atlantic Std Time (Canada) */
126 {"at", IGNOREFIELD
, 0}, /* "at" (throwaway) */
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 */
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 */
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 */
157 {"est", TZ
, NEG(30)}, /* Eastern Standard Time */
160 {"february", MONTH
, 2},
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
175 {"ist", TZ
, 12}, /* Israel */
176 {"it", TZ
, 22}, /* Iran Time */
178 {"january", MONTH
, 1},
179 {"jst", TZ
, 54}, /* Japan Std Time,USSR Zone 8 */
180 {"jt", TZ
, 45}, /* Java Time */
185 {"kst", TZ
, 54}, /* Korea Standard Time */
186 {"ligt", TZ
, 60}, /* From Melbourne, Australia */
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 */
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 */
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 */
212 {"october", MONTH
, 10},
213 {"on", IGNOREFIELD
, 0}, /* "on" (throwaway) */
214 {"pdt", DTZ
, NEG(42)}, /* Pacific Daylight Time */
216 {"pst", TZ
, NEG(48)}, /* Pacific Standard Time */
217 {"sadt", DTZ
, 63}, /* S. Australian Dayl. Time */
218 {"sast", TZ
, 57}, /* South Australian Std Time */
220 {"saturday", DOW
, 6},
223 {"september", MONTH
, 9},
224 {"set", TZ
, NEG(6)}, /* Seychelles Time ?? */
225 {"sst", DTZ
, 12}, /* Swedish Summer Time */
228 {"swt", TZ
, 6}, /* Swedish Winter Time */
232 {"thursday", DOW
, 4},
233 {TODAY
, RESERV
, DTK_TODAY
}, /* midnight */
234 {TOMORROW
, RESERV
, DTK_TOMORROW
}, /* tomorrow midnight */
238 {"undefined", RESERV
, DTK_INVALID
}, /* "undefined" pre-v6.1 invalid
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 */
247 {"wednesday", DOW
, 3},
249 {"wet", TZ
, 0}, /* Western Europe */
250 {"wetdst", DTZ
, 6}, /* Western Europe */
251 {"wst", TZ
, 48}, /* West Australian Std Time */
252 {"ydt", DTZ
, NEG(48)}, /* Yukon Daylight Time */
253 {YESTERDAY
, RESERV
, DTK_YESTERDAY
}, /* yesterday midnight */
254 {"yst", TZ
, NEG(54)}, /* Yukon Standard Time */
255 {"zp4", TZ
, NEG(24)}, /* GMT +4 hours. */
256 {"zp5", TZ
, NEG(30)}, /* GMT +5 hours. */
257 {"zp6", TZ
, NEG(36)}, /* GMT +6 hours. */
258 {"z", RESERV
, DTK_ZULU
}, /* 00:00:00 */
259 {ZULU
, RESERV
, DTK_ZULU
}, /* 00:00:00 */
262 static unsigned int szdatetktbl
= sizeof datetktbl
/ sizeof datetktbl
[0];
267 datetkn
*datecache
[MAXDATEFIELDS
] = {NULL
};
269 datetkn
*deltacache
[MAXDATEFIELDS
] = {NULL
};
275 * Calendar time to Julian date conversions.
276 * Julian date is commonly used in astronomical applications,
277 * since it is numerically accurate and computationally simple.
278 * The algorithms here will accurately convert between Julian day
279 * and calendar date for all non-negative Julian days
280 * (i.e. from Nov 23, -4713 on).
282 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
283 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
285 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
286 * now at Aerospace Corp. (hi, Henry!)
288 * These routines will be used by other date/time packages - tgl 97/02/25
291 /* Set the minimum year to one greater than the year of the first valid day
292 * to avoid having to check year and day both. - tgl 97/05/08
295 #define JULIAN_MINYEAR (-4713)
296 #define JULIAN_MINMONTH (11)
297 #define JULIAN_MINDAY (23)
299 #define IS_VALID_JULIAN(y,m,d) ((y > JULIAN_MINYEAR) \
300 || ((y == JULIAN_MINYEAR) && ((m > JULIAN_MINMONTH) \
301 || ((m == JULIAN_MINMONTH) && (d >= JULIAN_MINDAY)))))
304 date2j(int y
, int m
, int d
)
306 int m12
= (m
- 14) / 12;
308 return ((1461 * (y
+ 4800 + m12
)) / 4 + (367 * (m
- 2 - 12 * (m12
))) / 12
309 - (3 * ((y
+ 4900 + m12
) / 100)) / 4 + d
- 32075);
313 j2date(int jd
, int *year
, int *month
, int *day
)
325 n
= (4 * l
) / 146097;
326 l
-= (146097 * n
+ 3) / 4;
327 i
= (4000 * (l
+ 1)) / 1461001;
328 l
+= 31 - (1461 * i
) / 4;
330 d
= l
- (2447 * j
) / 80;
332 m
= (j
+ 2) - (12 * l
);
333 y
= 100 * (n
- 49) + i
+ l
;
345 * parse and convert date in timestr (the normal interface)
347 * Returns the number of seconds since epoch (J2000)
351 * Break string into tokens based on a date/time context.
354 ParseDateTime(char *timestr
, char *lowstr
,
355 char **field
, int *ftype
, int maxfields
, int *numfields
)
362 printf("ParseDateTime- input string is %s\n", timestr
);
364 /* outer loop through fields */
369 /* leading digit? then date or time */
370 if (isdigit(*cp
) || (*cp
== '.'))
378 ftype
[nf
] = DTK_TIME
;
379 while (isdigit(*cp
) || (*cp
== ':') || (*cp
== '.'))
383 /* date field? allow embedded text month */
384 else if ((*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
386 ftype
[nf
] = DTK_DATE
;
387 while (isalnum(*cp
) || (*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
388 *lp
++ = tolower(*cp
++);
393 * otherwise, number only and will determine year, month, or
397 ftype
[nf
] = DTK_NUMBER
;
402 * text? then date string, month, day of week, special, or
405 else if (isalpha(*cp
))
407 ftype
[nf
] = DTK_STRING
;
408 *lp
++ = tolower(*cp
++);
410 *lp
++ = tolower(*cp
++);
412 /* full date string with leading text month? */
413 if ((*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
415 ftype
[nf
] = DTK_DATE
;
416 while (isdigit(*cp
) || (*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
417 *lp
++ = tolower(*cp
++);
420 /* skip leading spaces */
422 else if (isspace(*cp
))
427 /* sign? then special or numeric timezone */
429 else if ((*cp
== '+') || (*cp
== '-'))
432 /* soak up leading whitespace */
435 /* numeric timezone? */
440 while (isdigit(*cp
) || (*cp
== ':'))
445 else if (isalpha(*cp
))
447 ftype
[nf
] = DTK_SPECIAL
;
448 *lp
++ = tolower(*cp
++);
450 *lp
++ = tolower(*cp
++);
452 /* otherwise something wrong... */
457 /* ignore punctuation but use as delimiter */
459 else if (ispunct(*cp
))
468 /* force in a delimiter */
471 if (nf
> MAXDATEFIELDS
)
474 printf("ParseDateTime- set field[%d] to %s type %d\n", (nf
- 1), field
[nf
- 1], ftype
[nf
- 1]);
481 } /* ParseDateTime() */
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:
498 * Use the system-provided functions to get the current time zone
499 * if not specified in the input string.
500 * If the date is outside the time_t system-supported time range,
501 * then assume GMT time zone. - tgl 97/05/27
504 DecodeDateTime(char **field
, int *ftype
, int nf
,
505 int *dtype
, struct tm
* tm
, double *fsec
, int *tzp
)
521 tm
->tm_isdst
= -1; /* don't know daylight savings time status
526 for (i
= 0; i
< nf
; i
++)
529 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i
, field
[i
], ftype
[i
]);
534 if (DecodeDate(field
[i
], fmask
, &tmask
, tm
) != 0)
539 if (DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
543 * check upper limit on hours; other limits checked in
546 if (tm
->tm_hour
> 23)
553 if (DecodeTimezone(field
[i
], tzp
) != 0)
559 flen
= strlen(field
[i
]);
563 if (DecodeNumberField(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
569 if (DecodeNumber(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
576 type
= DecodeSpecial(i
, field
[i
], &val
);
578 printf("DecodeDateTime- special field[%d] %s type=%d value=%d\n", i
, field
[i
], type
, val
);
580 if (type
== IGNOREFIELD
)
588 printf("DecodeDateTime- RESERV field %s value is %d\n", field
[i
], val
);
601 printf("DecodeDateTime- month field %s value is %d\n", field
[i
], val
);
607 * daylight savings time modifier (solves "MET
621 * set mask for TZ here _or_ check for DTZ later
622 * when getting default timezone
663 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
664 i
, field
[i
], fmask
, tmask
, val
);
672 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
674 tm
->tm_year
= -(tm
->tm_year
- 1);
676 if ((mer
!= HR24
) && (tm
->tm_hour
> 12))
678 if ((mer
== AM
) && (tm
->tm_hour
== 12))
680 else if ((mer
== PM
) && (tm
->tm_hour
!= 12))
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
);
689 if ((*dtype
== DTK_DATE
) && ((fmask
& DTK_DATE_M
) != DTK_DATE_M
))
690 return ((fmask
& DTK_TIME_M
) == DTK_TIME_M
) ? 1 : -1;
692 /* timezone not specified? then find local timezone if possible */
693 if ((*dtype
== DTK_DATE
) && ((fmask
& DTK_DATE_M
) == DTK_DATE_M
)
694 && (tzp
!= NULL
) && (!(fmask
& DTK_M(TZ
))))
698 * daylight savings time modifier but no standard timezone? then
701 if (fmask
& DTK_M(DTZMOD
))
704 if (IS_VALID_UTIME(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
706 #ifdef USE_POSIX_TIME
714 #ifdef HAVE_INT_TIMEZONE
715 *tzp
= ((tm
->tm_isdst
> 0) ? (timezone
- 3600) : timezone
);
717 #else /* !HAVE_INT_TIMEZONE */
718 *tzp
= -(tm
->tm_gmtoff
); /* tm_gmtoff is Sun/DEC-ism */
721 #else /* !USE_POSIX_TIME */
733 } /* DecodeDateTime() */
737 * Interpret parsed string as time fields only.
740 DecodeTimeOnly(char **field
, int *ftype
, int nf
, int *dtype
, struct tm
* tm
, double *fsec
)
754 tm
->tm_isdst
= -1; /* don't know daylight savings time status
760 for (i
= 0; i
< nf
; i
++)
763 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i
, field
[i
], ftype
[i
]);
768 if (DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
773 flen
= strlen(field
[i
]);
775 if (DecodeNumberField(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
781 type
= DecodeSpecial(i
, field
[i
], &val
);
783 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i
, field
[i
], type
, val
);
785 if (type
== IGNOREFIELD
)
793 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field
[i
], val
);
825 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i
, field
[i
], val
);
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
);
834 if ((mer
!= HR24
) && (tm
->tm_hour
> 12))
836 if ((mer
== AM
) && (tm
->tm_hour
== 12))
838 else if ((mer
== PM
) && (tm
->tm_hour
!= 12))
841 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
845 } /* DecodeTimeOnly() */
849 * Decode date string which includes delimiters.
850 * Insist on a complete set of fields.
853 DecodeDate(char *str
, int fmask
, int *tmask
, struct tm
* tm
)
863 char *field
[MAXDATEFIELDS
];
865 /* parse this string... */
866 while ((*str
!= '\0') && (nf
< MAXDATEFIELDS
))
868 /* skip field separators */
869 while (!isalnum(*str
))
875 while (isdigit(*str
))
878 else if (isalpha(*str
))
880 while (isalpha(*str
))
889 /* don't allow too many fields */
895 /* look first for text fields, since that will be unambiguous month */
896 for (i
= 0; i
< nf
; i
++)
898 if (isalpha(*field
[i
]))
900 type
= DecodeSpecial(i
, field
[i
], &val
);
901 if (type
== IGNOREFIELD
)
909 printf("DecodeDate- month field %s value is %d\n", field
[i
], val
);
916 printf("DecodeDate- illegal field %s value is %d\n", field
[i
], val
);
926 /* mark this field as being completed */
931 /* now pick up remaining numeric fields */
932 for (i
= 0; i
< nf
; i
++)
934 if (field
[i
] == NULL
)
937 if ((len
= strlen(field
[i
])) <= 0)
940 if (DecodeNumber(len
, field
[i
], fmask
, &dmask
, tm
, &fsec
) != 0)
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.
960 DecodeTime(char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
966 tm
->tm_hour
= strtol(str
, &cp
, 10);
970 tm
->tm_min
= strtol(str
, &cp
, 10);
985 tm
->tm_sec
= strtol(str
, &cp
, 10);
991 *fsec
= strtod(str
, &cp
);
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))
1006 } /* DecodeTime() */
1010 * Interpret numeric field as a date value in context.
1013 DecodeNumber(int flen
, char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
1020 val
= strtol(str
, &cp
, 10);
1025 *fsec
= strtod(cp
, &cp
);
1031 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str
, val
, fmask
, *tmask
);
1034 /* enough digits to be unequivocal year? */
1038 printf("DecodeNumber- match %d (%s) as year\n", val
, str
);
1040 *tmask
= DTK_M(YEAR
);
1042 /* already have a year? then see if we can substitute... */
1043 if (fmask
& DTK_M(YEAR
))
1045 if ((!(fmask
& DTK_M(DAY
)))
1046 && ((tm
->tm_year
>= 1) && (tm
->tm_year
<= 31)))
1049 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm
->tm_mday
);
1051 tm
->tm_mday
= tm
->tm_year
;
1052 *tmask
= DTK_M(DAY
);
1058 /* special case day of year? */
1060 else if ((flen
== 3) && (fmask
& DTK_M(YEAR
))
1061 && ((val
>= 1) && (val
<= 366)))
1063 *tmask
= (DTK_M(DOY
) | DTK_M(MONTH
) | DTK_M(DAY
));
1065 j2date((date2j(tm
->tm_year
, 1, 1) + tm
->tm_yday
- 1),
1066 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1068 /* already have year? then could be month */
1070 else if ((fmask
& DTK_M(YEAR
)) && (!(fmask
& DTK_M(MONTH
)))
1071 && ((val
>= 1) && (val
<= 12)))
1074 printf("DecodeNumber- match %d (%s) as month\n", val
, str
);
1076 *tmask
= DTK_M(MONTH
);
1079 /* no year and EuroDates enabled? then could be day */
1081 else if ((EuroDates
|| (fmask
& DTK_M(MONTH
)))
1082 && (!(fmask
& DTK_M(YEAR
)) && !(fmask
& DTK_M(DAY
)))
1083 && ((val
>= 1) && (val
<= 31)))
1086 printf("DecodeNumber- match %d (%s) as day\n", val
, str
);
1088 *tmask
= DTK_M(DAY
);
1092 else if ((!(fmask
& DTK_M(MONTH
)))
1093 && ((val
>= 1) && (val
<= 12)))
1096 printf("DecodeNumber- (2) match %d (%s) as month\n", val
, str
);
1098 *tmask
= DTK_M(MONTH
);
1102 else if ((!(fmask
& DTK_M(DAY
)))
1103 && ((val
>= 1) && (val
<= 31)))
1106 printf("DecodeNumber- (2) match %d (%s) as day\n", val
, str
);
1108 *tmask
= DTK_M(DAY
);
1112 else if (!(fmask
& DTK_M(YEAR
)))
1115 printf("DecodeNumber- (2) match %d (%s) as year\n", val
, str
);
1117 *tmask
= DTK_M(YEAR
);
1119 if (tm
->tm_year
< 70)
1120 tm
->tm_year
+= 2000;
1121 else if (tm
->tm_year
< 100)
1122 tm
->tm_year
+= 1900;
1129 } /* DecodeNumber() */
1132 /* DecodeNumberField()
1133 * Interpret numeric string as a concatenated date field.
1136 DecodeNumberField(int len
, char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
1144 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1147 *tmask
= DTK_DATE_M
;
1149 tm
->tm_mday
= atoi(str
+ 6);
1151 tm
->tm_mon
= atoi(str
+ 4);
1153 tm
->tm_year
= atoi(str
+ 0);
1155 /* yymmdd or hhmmss? */
1160 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1162 if (fmask
& DTK_DATE_M
)
1165 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1167 *tmask
= DTK_TIME_M
;
1168 tm
->tm_sec
= atoi(str
+ 4);
1170 tm
->tm_min
= atoi(str
+ 2);
1172 tm
->tm_hour
= atoi(str
+ 0);
1178 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1180 *tmask
= DTK_DATE_M
;
1181 tm
->tm_mday
= atoi(str
+ 4);
1183 tm
->tm_mon
= atoi(str
+ 2);
1185 tm
->tm_year
= atoi(str
+ 0);
1189 else if (strchr(str
, '.') != NULL
)
1192 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1194 *tmask
= DTK_TIME_M
;
1195 tm
->tm_sec
= strtod((str
+ 4), &cp
);
1196 if (cp
== (str
+ 4))
1199 *fsec
= strtod(cp
, NULL
);
1201 tm
->tm_min
= strtod((str
+ 2), &cp
);
1203 tm
->tm_hour
= strtod((str
+ 0), &cp
);
1210 } /* DecodeNumberField() */
1214 * Interpret string as a numeric timezone.
1217 DecodeTimezone(char *str
, int *tzp
)
1225 /* assume leading character is "+" or "-" */
1226 hr
= strtol((str
+ 1), &cp
, 10);
1228 /* explicit delimiter? */
1231 min
= strtol((cp
+ 1), &cp
, 10);
1233 /* otherwise, might have run things together... */
1235 else if ((*cp
== '\0') && ((len
= strlen(str
)) > 3))
1237 min
= strtol((str
+ len
- 2), &cp
, 10);
1238 *(str
+ len
- 2) = '\0';
1239 hr
= strtol((str
+ 1), &cp
, 10);
1245 tz
= (hr
* 60 + min
) * 60;
1251 } /* DecodeTimezone() */
1255 * Decode text string using lookup table.
1256 * Implement a cache lookup since it is likely that dates
1257 * will be related in format.
1260 DecodeSpecial(int field
, char *lowtoken
, int *val
)
1266 if ((datecache
[field
] != NULL
)
1267 && (strncmp(lowtoken
, datecache
[field
]->token
, TOKMAXLEN
) == 0))
1268 tp
= datecache
[field
];
1272 tp
= datebsearch(lowtoken
, datetktbl
, szdatetktbl
);
1275 datecache
[field
] = tp
;
1300 } /* DecodeSpecial() */
1305 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1306 * is WAY faster than the generic bsearch().
1309 datebsearch(char *key
, datetkn
*base
, unsigned int nel
)
1311 datetkn
*last
= base
+ nel
- 1,
1315 while (last
>= base
)
1317 position
= base
+ ((last
- base
) >> 1);
1318 result
= key
[0] - position
->token
[0];
1321 result
= strncmp(key
, position
->token
, TOKMAXLEN
);
1326 last
= position
- 1;
1328 base
= position
+ 1;