1 /*-------------------------------------------------------------------------
4 * Support functions for date/time types.
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.97 2002/11/13 17:24:05 tgl Exp $
13 *-------------------------------------------------------------------------
23 #include "miscadmin.h"
24 #include "utils/guc.h"
25 #include "utils/datetime.h"
28 static int DecodeNumber(int flen, char *field,
29 int fmask, int *tmask,
30 struct tm * tm, fsec_t *fsec, int *is2digits);
31 static int DecodeNumberField(int len, char *str,
32 int fmask, int *tmask,
33 struct tm * tm, fsec_t *fsec, int *is2digits);
34 static int DecodeTime(char *str, int fmask, int *tmask,
35 struct tm * tm, fsec_t *fsec);
36 static int DecodeTimezone(char *str, int *tzp);
37 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
38 static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
39 static int DecodePosixTimezone(char *str, int *val);
40 void TrimTrailingZeros(char *str);
43 int day_tab[2][13] = {
44 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
45 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
47 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
48 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
50 char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
51 "Thursday", "Friday", "Saturday", NULL};
54 /*****************************************************************************
56 *****************************************************************************/
59 * Definitions for squeezing values into "value"
60 * We set aside a high bit for a sign, and scale the timezone offsets
61 * in minutes by a factor of 15 (so can represent quarter-hour increments).
63 #define ABS_SIGNBIT ((char) 0200)
64 #define VALMASK ((char) 0177)
66 #define NEG(n) ((n)|ABS_SIGNBIT)
67 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
68 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
69 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
72 * datetktbl holds date/time keywords. Note that this table must be strictly
73 * ordered to allow an O(ln(N)) search algorithm.
75 * The text field is not guaranteed to be NULL-terminated.
77 * To keep this table reasonably small, we divide the lexval for TZ and DTZ
78 * entries by 15 (so they are on 15 minute boundaries) and truncate the text
79 * field at MAXTOKLEN characters.
80 * Formerly, we divided by 10 rather than 15 but there are a few time zones
81 * which are 30 or 45 minutes away from an even hour, most are on an hour
82 * boundary, and none on other boundaries.
84 * Let's include all strings from my current zinc time zone database.
85 * Not all of them are unique, or even very understandable, so we will
86 * leave some commented out for now.
88 static datetkn datetktbl[] = {
89 /* text, token, lexval */
90 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
91 {"acsst", DTZ, POS(42)}, /* Cent. Australia */
92 {"acst", DTZ, NEG(16)}, /* Atlantic/Porto Acre */
93 {"act", TZ, NEG(20)}, /* Atlantic/Porto Acre */
94 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
95 {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
96 {"adt", DTZ, NEG(12)}, /* Atlantic Daylight Time */
97 {"aesst", DTZ, POS(44)}, /* E. Australia */
98 {"aest", TZ, POS(40)}, /* Australia Eastern Std Time */
99 {"aft", TZ, POS(18)}, /* Kabul */
100 {"ahst", TZ, NEG(40)}, /* Alaska-Hawaii Std Time */
101 {"akdt", DTZ, NEG(32)}, /* Alaska Daylight Time */
102 {"akst", DTZ, NEG(36)}, /* Alaska Standard Time */
103 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
104 {"almt", TZ, POS(24)}, /* Almaty Time */
105 {"almst", TZ, POS(28)}, /* Almaty Savings Time */
107 {"amst", DTZ, POS(20)}, /* Armenia Summer Time (Yerevan) */
108 {"amt", TZ, POS(16)}, /* Armenia Time (Yerevan) */
110 {"amst", DTZ, NEG(12)}, /* Porto Velho */
112 {"anast", DTZ, POS(52)}, /* Anadyr Summer Time (Russia) */
113 {"anat", TZ, POS(48)}, /* Anadyr Time (Russia) */
119 {"art", TZ, NEG(12)}, /* Argentina Time */
122 ast /* Atlantic Standard Time, Arabia Standard
123 * Time, Acre Standard Time */
127 {"ast", TZ, NEG(16)}, /* Atlantic Std Time (Canada) */
128 {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
130 {"august", MONTH, 8},
131 {"awsst", DTZ, POS(36)}, /* W. Australia */
132 {"awst", TZ, POS(32)}, /* W. Australia */
133 {"awt", DTZ, NEG(12)},
134 {"azost", DTZ, POS(0)}, /* Azores Summer Time */
135 {"azot", TZ, NEG(4)}, /* Azores Time */
136 {"azst", DTZ, POS(20)}, /* Azerbaijan Summer Time */
137 {"azt", TZ, POS(16)}, /* Azerbaijan Time */
138 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
139 {"bdst", TZ, POS(8)}, /* British Double Summer Time */
140 {"bdt", TZ, POS(24)}, /* Dacca */
141 {"bnt", TZ, POS(32)}, /* Brunei Darussalam Time */
142 {"bort", TZ, POS(32)}, /* Borneo Time (Indonesia) */
147 {"bot", TZ, NEG(16)}, /* Bolivia Time */
148 {"bra", TZ, NEG(12)}, /* Brazil Time */
153 {"bst", DTZ, POS(4)}, /* British Summer Time */
155 {"bst", TZ, NEG(12)}, /* Brazil Standard Time */
156 {"bst", DTZ, NEG(44)}, /* Bering Summer Time */
158 {"bt", TZ, POS(12)}, /* Baghdad Time */
159 {"btt", TZ, POS(24)}, /* Bhutan Time */
160 {"cadt", DTZ, POS(42)}, /* Central Australian DST */
161 {"cast", TZ, POS(38)}, /* Central Australian ST */
162 {"cat", TZ, NEG(40)}, /* Central Alaska Time */
163 {"cct", TZ, POS(32)}, /* China Coast Time */
165 {"cct", TZ, POS(26)}, /* Indian Cocos (Island) Time */
167 {"cdt", DTZ, NEG(20)}, /* Central Daylight Time */
168 {"cest", DTZ, POS(8)}, /* Central European Dayl.Time */
169 {"cet", TZ, POS(4)}, /* Central European Time */
170 {"cetdst", DTZ, POS(8)}, /* Central European Dayl.Time */
171 {"chadt", DTZ, POS(55)}, /* Chatham Island Daylight Time (13:45) */
172 {"chast", TZ, POS(51)}, /* Chatham Island Time (12:45) */
176 {"ckt", TZ, POS(48)}, /* Cook Islands Time */
177 {"clst", DTZ, NEG(12)}, /* Chile Summer Time */
178 {"clt", TZ, NEG(16)}, /* Chile Time */
182 {"cot", TZ, NEG(20)}, /* Columbia Time */
183 {"cst", TZ, NEG(24)}, /* Central Standard Time */
187 {"cvt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
188 {"cxt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
189 {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
190 {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
191 {"davt", TZ, POS(28)}, /* Davis Time (Antarctica) */
192 {"ddut", TZ, POS(40)}, /* Dumont-d'Urville Time (Antarctica) */
194 {"december", MONTH, 12},
195 {"dnt", TZ, POS(4)}, /* Dansk Normal Tid */
196 {"dow", RESERV, DTK_DOW}, /* day of week */
197 {"doy", RESERV, DTK_DOY}, /* day of year */
200 {"dusst", DTZ, POS(24)}, /* Dushanbe Summer Time */
202 {"easst", DTZ, NEG(20)}, /* Easter Island Summer Time */
203 {"east", TZ, NEG(24)}, /* Easter Island Time */
204 {"eat", TZ, POS(12)}, /* East Africa Time */
206 {"east", DTZ, POS(16)}, /* Indian Antananarivo Savings Time */
207 {"eat", TZ, POS(12)}, /* Indian Antananarivo Time */
208 {"ect", TZ, NEG(16)}, /* Eastern Caribbean Time */
209 {"ect", TZ, NEG(20)}, /* Ecuador Time */
211 {"edt", DTZ, NEG(16)}, /* Eastern Daylight Time */
212 {"eest", DTZ, POS(12)}, /* Eastern Europe Summer Time */
213 {"eet", TZ, POS(8)}, /* East. Europe, USSR Zone 1 */
214 {"eetdst", DTZ, POS(12)}, /* Eastern Europe Daylight Time */
215 {"egst", DTZ, POS(0)}, /* East Greenland Summer Time */
216 {"egt", TZ, NEG(4)}, /* East Greenland Time */
220 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
221 {"est", TZ, NEG(20)}, /* Eastern Standard Time */
223 {"february", MONTH, 2},
224 {"fjst", DTZ, NEG(52)}, /* Fiji Summer Time (13 hour offset!) */
225 {"fjt", TZ, NEG(48)}, /* Fiji Time */
226 {"fkst", DTZ, NEG(12)}, /* Falkland Islands Summer Time */
227 {"fkt", TZ, NEG(8)}, /* Falkland Islands Time */
234 {"fst", TZ, POS(4)}, /* French Summer Time */
235 {"fwt", DTZ, POS(8)}, /* French Winter Time */
236 {"galt", TZ, NEG(24)}, /* Galapagos Time */
237 {"gamt", TZ, NEG(36)}, /* Gambier Time */
238 {"gest", DTZ, POS(20)}, /* Georgia Summer Time */
239 {"get", TZ, POS(16)}, /* Georgia Time */
240 {"gft", TZ, NEG(12)}, /* French Guiana Time */
244 {"gilt", TZ, POS(48)}, /* Gilbert Islands Time */
245 {"gmt", TZ, POS(0)}, /* Greenwish Mean Time */
246 {"gst", TZ, POS(40)}, /* Guam Std Time, USSR Zone 9 */
247 {"gyt", TZ, NEG(16)}, /* Guyana Time */
248 {"h", UNITS, DTK_HOUR}, /* "hour" */
253 {"hdt", DTZ, NEG(36)}, /* Hawaii/Alaska Daylight Time */
257 {"hkt", TZ, POS(32)}, /* Hong Kong Time */
259 {"hmt", TZ, POS(12)}, /* Hellas ? ? */
263 {"hst", TZ, NEG(40)}, /* Hawaii Std Time */
267 {"ict", TZ, POS(28)}, /* Indochina Time */
268 {"idle", TZ, POS(48)}, /* Intl. Date Line, East */
269 {"idlw", TZ, NEG(48)}, /* Intl. Date Line, West */
271 idt /* Israeli, Iran, Indian Daylight Time */
273 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
274 {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
275 {"iot", TZ, POS(20)}, /* Indian Chagos Time */
276 {"irkst", DTZ, POS(36)}, /* Irkutsk Summer Time */
277 {"irkt", TZ, POS(32)}, /* Irkutsk Time */
278 {"irt", TZ, POS(14)}, /* Iran Time */
282 {"ist", TZ, POS(8)}, /* Israel */
283 {"it", TZ, POS(14)}, /* Iran Time */
284 {"j", UNITS, DTK_JULIAN},
286 {"january", MONTH, 1},
287 {"javt", TZ, POS(28)}, /* Java Time (07:00? see JT) */
288 {"jayt", TZ, POS(36)}, /* Jayapura Time (Indonesia) */
289 {"jd", UNITS, DTK_JULIAN},
290 {"jst", TZ, POS(36)}, /* Japan Std Time,USSR Zone 8 */
291 {"jt", TZ, POS(30)}, /* Java Time (07:30? see JAVT) */
293 {"julian", UNITS, DTK_JULIAN},
297 {"kdt", DTZ, POS(40)}, /* Korea Daylight Time */
298 {"kgst", DTZ, POS(24)}, /* Kyrgyzstan Summer Time */
299 {"kgt", TZ, POS(20)}, /* Kyrgyzstan Time */
300 {"kost", TZ, POS(48)}, /* Kosrae Time */
301 {"krast", DTZ, POS(28)}, /* Krasnoyarsk Summer Time */
302 {"krat", TZ, POS(32)}, /* Krasnoyarsk Standard Time */
303 {"kst", TZ, POS(36)}, /* Korea Standard Time */
304 {"lhdt", DTZ, POS(44)}, /* Lord Howe Daylight Time, Australia */
305 {"lhst", TZ, POS(42)}, /* Lord Howe Standard Time, Australia */
306 {"ligt", TZ, POS(40)}, /* From Melbourne, Australia */
307 {"lint", TZ, POS(56)}, /* Line Islands Time (Kiribati; +14
309 {"lkt", TZ, POS(24)}, /* Lanka Time */
310 {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
311 {"magst", DTZ, POS(48)}, /* Magadan Summer Time */
312 {"magt", TZ, POS(44)}, /* Magadan Time */
315 {"mart", TZ, NEG(38)}, /* Marquesas Time */
316 {"mawt", TZ, POS(24)}, /* Mawson, Antarctica */
318 {"mdt", DTZ, NEG(24)}, /* Mountain Daylight Time */
319 {"mest", DTZ, POS(8)}, /* Middle Europe Summer Time */
320 {"met", TZ, POS(4)}, /* Middle Europe Time */
321 {"metdst", DTZ, POS(8)}, /* Middle Europe Daylight Time */
322 {"mewt", TZ, POS(4)}, /* Middle Europe Winter Time */
323 {"mez", TZ, POS(4)}, /* Middle Europe Zone */
324 {"mht", TZ, POS(48)}, /* Kwajalein */
325 {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
326 {"mmt", TZ, POS(26)}, /* Myannar Time */
332 {"mpt", TZ, POS(40)}, /* North Mariana Islands Time */
333 {"msd", DTZ, POS(16)}, /* Moscow Summer Time */
334 {"msk", TZ, POS(12)}, /* Moscow Time */
335 {"mst", TZ, NEG(28)}, /* Mountain Standard Time */
336 {"mt", TZ, POS(34)}, /* Moluccas Time */
337 {"mut", TZ, POS(16)}, /* Mauritius Island Time */
338 {"mvt", TZ, POS(20)}, /* Maldives Island Time */
339 {"myt", TZ, POS(32)}, /* Malaysia Time */
343 {"nct", TZ, POS(44)}, /* New Caledonia Time */
344 {"ndt", DTZ, NEG(10)}, /* Nfld. Daylight Time */
345 {"nft", TZ, NEG(14)}, /* Newfoundland Standard Time */
346 {"nor", TZ, POS(4)}, /* Norway Standard Time */
348 {"november", MONTH, 11},
349 {"novst", DTZ, POS(28)}, /* Novosibirsk Summer Time */
350 {"novt", TZ, POS(24)}, /* Novosibirsk Standard Time */
351 {NOW, RESERV, DTK_NOW}, /* current transaction time */
352 {"npt", TZ, POS(23)}, /* Nepal Standard Time (GMT-5:45) */
353 {"nst", TZ, NEG(14)}, /* Nfld. Standard Time */
354 {"nt", TZ, NEG(44)}, /* Nome Time */
355 {"nut", TZ, NEG(44)}, /* Niue Time */
356 {"nzdt", DTZ, POS(52)}, /* New Zealand Daylight Time */
357 {"nzst", TZ, POS(48)}, /* New Zealand Standard Time */
358 {"nzt", TZ, POS(48)}, /* New Zealand Time */
360 {"october", MONTH, 10},
361 {"omsst", DTZ, POS(28)}, /* Omsk Summer Time */
362 {"omst", TZ, POS(24)}, /* Omsk Time */
363 {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
364 {"pdt", DTZ, NEG(28)}, /* Pacific Daylight Time */
368 {"pet", TZ, NEG(20)}, /* Peru Time */
369 {"petst", DTZ, POS(52)}, /* Petropavlovsk-Kamchatski Summer Time */
370 {"pett", TZ, POS(48)}, /* Petropavlovsk-Kamchatski Time */
371 {"pgt", TZ, POS(40)}, /* Papua New Guinea Time */
372 {"phot", TZ, POS(52)}, /* Phoenix Islands (Kiribati) Time */
376 {"pht", TZ, POS(32)}, /* Phillipine Time */
377 {"pkt", TZ, POS(20)}, /* Pakistan Time */
379 {"pmdt", DTZ, NEG(8)}, /* Pierre & Miquelon Daylight Time */
383 {"pont", TZ, POS(44)}, /* Ponape Time (Micronesia) */
384 {"pst", TZ, NEG(32)}, /* Pacific Standard Time */
385 {"pwt", TZ, POS(36)}, /* Palau Time */
386 {"pyst", DTZ, NEG(12)}, /* Paraguay Summer Time */
387 {"pyt", TZ, NEG(16)}, /* Paraguay Time */
388 {"ret", DTZ, POS(16)}, /* Reunion Island Time */
389 {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
390 {"sadt", DTZ, POS(42)}, /* S. Australian Dayl. Time */
395 {"sast", TZ, POS(38)}, /* South Australian Std Time */
397 {"saturday", DOW, 6},
401 {"sct", DTZ, POS(16)}, /* Mahe Island Time */
404 {"september", MONTH, 9},
405 {"set", TZ, NEG(4)}, /* Seychelles Time ?? */
409 {"sst", DTZ, POS(8)}, /* Swedish Summer Time */
412 {"swt", TZ, POS(4)}, /* Swedish Winter Time */
416 {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
417 {"that", TZ, NEG(40)}, /* Tahiti Time */
418 {"tft", TZ, POS(20)}, /* Kerguelen Time */
422 {"thursday", DOW, 4},
423 {"tjt", TZ, POS(20)}, /* Tajikistan Time */
424 {"tkt", TZ, NEG(40)}, /* Tokelau Time */
425 {"tmt", TZ, POS(20)}, /* Turkmenistan Time */
426 {TODAY, RESERV, DTK_TODAY}, /* midnight */
427 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
431 {"tot", TZ, POS(52)}, /* Tonga Time */
435 {"truk", TZ, POS(40)}, /* Truk Time */
439 {"tvt", TZ, POS(48)}, /* Tuvalu Time */
443 {"ulast", DTZ, POS(36)}, /* Ulan Bator Summer Time */
444 {"ulat", TZ, POS(32)}, /* Ulan Bator Time */
445 {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
448 {"uyst", DTZ, NEG(8)}, /* Uruguay Summer Time */
449 {"uyt", TZ, NEG(12)}, /* Uruguay Time */
450 {"uzst", DTZ, POS(24)}, /* Uzbekistan Summer Time */
451 {"uzt", TZ, POS(20)}, /* Uzbekistan Time */
452 {"vet", TZ, NEG(16)}, /* Venezuela Time */
453 {"vlast", DTZ, POS(44)}, /* Vladivostok Summer Time */
454 {"vlat", TZ, POS(40)}, /* Vladivostok Time */
458 {"vut", TZ, POS(44)}, /* Vanuata Time */
459 {"wadt", DTZ, POS(32)}, /* West Australian DST */
460 {"wakt", TZ, POS(48)}, /* Wake Time */
464 {"wast", TZ, POS(28)}, /* West Australian Std Time */
465 {"wat", TZ, NEG(4)}, /* West Africa Time */
466 {"wdt", DTZ, POS(36)}, /* West Australian DST */
468 {"wednesday", DOW, 3},
470 {"west", DTZ, POS(4)}, /* Western Europe Summer Time */
471 {"wet", TZ, POS(0)}, /* Western Europe */
472 {"wetdst", DTZ, POS(4)}, /* Western Europe Daylight Savings Time */
473 {"wft", TZ, POS(48)}, /* Wallis and Futuna Time */
474 {"wgst", DTZ, NEG(8)}, /* West Greenland Summer Time */
475 {"wgt", TZ, NEG(12)}, /* West Greenland Time */
476 {"wst", TZ, POS(32)}, /* West Australian Standard Time */
477 {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
478 {"yakst", DTZ, POS(40)}, /* Yakutsk Summer Time */
479 {"yakt", TZ, POS(36)}, /* Yakutsk Time */
480 {"yapt", TZ, POS(40)}, /* Yap Time (Micronesia) */
481 {"ydt", DTZ, NEG(32)}, /* Yukon Daylight Time */
482 {"yekst", DTZ, POS(24)}, /* Yekaterinburg Summer Time */
483 {"yekt", TZ, POS(20)}, /* Yekaterinburg Time */
484 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
485 {"yst", TZ, NEG(36)}, /* Yukon Standard Time */
486 {"z", TZ, POS(0)}, /* time zone tag per ISO-8601 */
487 {"zp4", TZ, NEG(16)}, /* UTC +4 hours. */
488 {"zp5", TZ, NEG(20)}, /* UTC +5 hours. */
489 {"zp6", TZ, NEG(24)}, /* UTC +6 hours. */
490 {ZULU, TZ, POS(0)}, /* UTC */
493 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
495 /* Used for SET australian_timezones to override North American ones */
496 static datetkn australian_datetktbl[] = {
497 {"acst", TZ, POS(38)}, /* Cent. Australia */
498 {"cst", TZ, POS(42)}, /* Australia Central Std Time */
499 {"east", TZ, POS(40)}, /* East Australian Std Time */
500 {"est", TZ, POS(40)}, /* Australia Eastern Std Time */
501 {"sat", TZ, POS(38)},
504 static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
505 sizeof australian_datetktbl[0];
507 static datetkn deltatktbl[] = {
508 /* text, token, lexval */
509 {"@", IGNORE_DTF, 0}, /* postgres relative prefix */
510 {DAGO, AGO, 0}, /* "ago" indicates negative time offset */
511 {"c", UNITS, DTK_CENTURY}, /* "century" relative */
512 {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
513 {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
514 {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
515 {"d", UNITS, DTK_DAY}, /* "day" relative */
516 {DDAY, UNITS, DTK_DAY}, /* "day" relative */
517 {"days", UNITS, DTK_DAY}, /* "days" relative */
518 {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
519 {"decs", UNITS, DTK_DECADE}, /* "decades" relative */
520 {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
521 {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
522 {"h", UNITS, DTK_HOUR}, /* "hour" relative */
523 {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
524 {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
525 {"hr", UNITS, DTK_HOUR}, /* "hour" relative */
526 {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
527 {INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */
528 {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
529 {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
530 {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
531 {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
532 {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
533 {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
534 {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
535 {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
536 {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
537 {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
538 {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
539 {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
540 {"mon", UNITS, DTK_MONTH}, /* "months" relative */
541 {"mons", UNITS, DTK_MONTH}, /* "months" relative */
542 {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
543 {"months", UNITS, DTK_MONTH},
544 {"ms", UNITS, DTK_MILLISEC},
545 {"msec", UNITS, DTK_MILLISEC},
546 {DMILLISEC, UNITS, DTK_MILLISEC},
547 {"mseconds", UNITS, DTK_MILLISEC},
548 {"msecs", UNITS, DTK_MILLISEC},
549 {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
550 {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
551 {"reltime", IGNORE_DTF, 0}, /* pre-v6.1 "Undefined Reltime" */
552 {"s", UNITS, DTK_SECOND},
553 {"sec", UNITS, DTK_SECOND},
554 {DSECOND, UNITS, DTK_SECOND},
555 {"seconds", UNITS, DTK_SECOND},
556 {"secs", UNITS, DTK_SECOND},
557 {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
558 {"timezone", UNITS, DTK_TZ}, /* "timezone" time offset */
559 {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
560 {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
561 {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
562 {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
563 {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
564 {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
565 {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
566 {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
567 {"w", UNITS, DTK_WEEK}, /* "week" relative */
568 {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
569 {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
570 {"y", UNITS, DTK_YEAR}, /* "year" relative */
571 {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
572 {"years", UNITS, DTK_YEAR}, /* "years" relative */
573 {"yr", UNITS, DTK_YEAR}, /* "year" relative */
574 {"yrs", UNITS, DTK_YEAR}, /* "years" relative */
577 static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
579 datetkn *datecache[MAXDATEFIELDS] = {NULL};
581 datetkn *deltacache[MAXDATEFIELDS] = {NULL};
585 * Calendar time to Julian date conversions.
586 * Julian date is commonly used in astronomical applications,
587 * since it is numerically accurate and computationally simple.
588 * The algorithms here will accurately convert between Julian day
589 * and calendar date for all non-negative Julian days
590 * (i.e. from Nov 23, -4713 on).
592 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
593 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
595 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
596 * now at Aerospace Corp. (hi, Henry!)
598 * These routines will be used by other date/time packages
603 date2j(int y, int m, int d)
605 int m12 = (m - 14) / 12;
607 return ((1461 * (y + 4800 + m12)) / 4
608 + (367 * (m - 2 - 12 * (m12))) / 12
609 - (3 * ((y + 4900 + m12) / 100)) / 4
614 j2date(int jd, int *year, int *month, int *day)
626 n = (4 * l) / 146097;
627 l -= (146097 * n + 3) / 4;
628 i = (4000 * (l + 1)) / 1461001;
629 l += 31 - (1461 * i) / 4;
631 d = l - (2447 * j) / 80;
633 m = (j + 2) - (12 * l);
634 y = 100 * (n - 49) + i + l;
647 day = (date + 1) % 7;
653 /* TrimTrailingZeros()
654 * ... resulting from printing numbers with full precision.
657 TrimTrailingZeros(char *str)
659 int len = strlen(str);
662 /* chop off trailing one to cope with interval rounding */
663 if (strcmp((str + len - 4), "0001") == 0)
670 /* chop off trailing zeros... */
671 while ((*(str + len - 1) == '0')
672 && (*(str + len - 3) != '.'))
682 * Break string into tokens based on a date/time context.
683 * Several field types are assigned:
684 * DTK_NUMBER - digits and (possibly) a decimal point
685 * DTK_DATE - digits and two delimiters, or digits and text
686 * DTK_TIME - digits, colon delimiters, and possibly a decimal point
687 * DTK_STRING - text (no digits)
688 * DTK_SPECIAL - leading "+" or "-" followed by text
689 * DTK_TZ - leading "+" or "-" followed by digits
690 * Note that some field types can hold unexpected items:
691 * DTK_NUMBER can hold date fields (yy.ddd)
692 * DTK_STRING can hold months (January) and time zones (PST)
693 * DTK_DATE can hold Posix time zones (GMT-8)
696 ParseDateTime(char *timestr, char *lowstr,
697 char **field, int *ftype, int maxfields, int *numfields)
703 /* outer loop through fields */
708 /* leading digit? then date or time */
709 if (isdigit((unsigned char) *cp))
712 while (isdigit((unsigned char) *cp))
718 ftype[nf] = DTK_TIME;
720 while (isdigit((unsigned char) *cp) ||
721 (*cp == ':') || (*cp == '.'))
724 /* date field? allow embedded text month */
725 else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
727 /* save delimiting character to use later */
731 /* second field is all digits? then no embedded text month */
732 if (isdigit((unsigned char) *cp))
734 ftype[nf] = ((*dp == '.') ? DTK_NUMBER : DTK_DATE);
735 while (isdigit((unsigned char) *cp))
739 * insist that the delimiters match to get a
744 ftype[nf] = DTK_DATE;
746 while (isdigit((unsigned char) *cp) || (*cp == *dp))
752 ftype[nf] = DTK_DATE;
753 while (isalnum((unsigned char) *cp) || (*cp == *dp))
754 *lp++ = tolower((unsigned char) *cp++);
759 * otherwise, number only and will determine year, month, day,
760 * or concatenated fields later...
763 ftype[nf] = DTK_NUMBER;
765 /* Leading decimal point? Then fractional seconds... */
769 while (isdigit((unsigned char) *cp))
772 ftype[nf] = DTK_NUMBER;
776 * text? then date string, month, day of week, special, or
779 else if (isalpha((unsigned char) *cp))
781 ftype[nf] = DTK_STRING;
782 *lp++ = tolower((unsigned char) *cp++);
783 while (isalpha((unsigned char) *cp))
784 *lp++ = tolower((unsigned char) *cp++);
787 * Full date string with leading text month? Could also be a
790 if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
794 ftype[nf] = DTK_DATE;
796 while (isdigit((unsigned char) *cp) || (*cp == *dp))
800 /* skip leading spaces */
801 else if (isspace((unsigned char) *cp))
806 /* sign? then special or numeric timezone */
807 else if ((*cp == '+') || (*cp == '-'))
810 /* soak up leading whitespace */
811 while (isspace((unsigned char) *cp))
813 /* numeric timezone? */
814 if (isdigit((unsigned char) *cp))
818 while (isdigit((unsigned char) *cp) ||
819 (*cp == ':') || (*cp == '.'))
823 else if (isalpha((unsigned char) *cp))
825 ftype[nf] = DTK_SPECIAL;
826 *lp++ = tolower((unsigned char) *cp++);
827 while (isalpha((unsigned char) *cp))
828 *lp++ = tolower((unsigned char) *cp++);
830 /* otherwise something wrong... */
834 /* ignore punctuation but use as delimiter */
835 else if (ispunct((unsigned char) *cp))
841 /* otherwise, something is not right... */
845 /* force in a delimiter after each field */
848 if (nf > MAXDATEFIELDS)
855 } /* ParseDateTime() */
859 * Interpret previously parsed fields for general date and time.
860 * Return 0 if full date, 1 if only time, and -1 if problems.
861 * External format(s):
862 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
863 * "Fri Feb-7-1997 15:23:27"
864 * "Feb-7-1997 15:23:27"
865 * "2-7-1997 15:23:27"
866 * "1997-2-7 15:23:27"
867 * "1997.038 15:23:27" (day of year 1-366)
868 * Also supports input in compact time:
871 * "20011225T040506.789-07"
873 * Use the system-provided functions to get the current time zone
874 * if not specified in the input string.
875 * If the date is outside the time_t system-supported time range,
876 * then assume UTC time zone. - thomas 1997-05-27
879 DecodeDateTime(char **field, int *ftype, int nf,
880 int *dtype, struct tm * tm, fsec_t *fsec, int *tzp)
885 int ptype = 0; /* "prefix type" for ISO y2001m02d04
890 int haveTextMonth = FALSE;
891 int is2digits = FALSE;
895 * We'll insist on at least all of the date fields, but initialize the
896 * remaining fields in case they are not set later...
903 /* don't know daylight savings time status apriori */
908 for (i = 0; i < nf; i++)
914 * Integral julian day with attached time zone?
915 * All other forms with JD will be separated into
916 * distinct fields, so we handle just this case here.
918 if (ptype == DTK_JULIAN)
926 val = strtol(field[i], &cp, 10);
930 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
931 /* Get the time zone from the end of the string */
932 if (DecodeTimezone(cp, tzp) != 0)
935 tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
940 * Already have a date? Then this might be a POSIX time
941 * zone with an embedded dash (e.g. "PST-3" == "EST") or
942 * a run-together time with trailing time zone (e.g. hhmmss-zz).
943 * - thomas 2001-12-25
945 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
948 /* No time zone accepted? Then quit... */
952 if (isdigit((unsigned char) *field[i]) || ptype != 0)
958 /* Sanity check; should not fail this test */
959 if (ptype != DTK_TIME)
965 * Starts with a digit but we already have a time
966 * field? Then we are in trouble with a date and
969 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
972 if ((cp = strchr(field[i], '-')) == NULL)
975 /* Get the time zone from the end of the string */
976 if (DecodeTimezone(cp, tzp) != 0)
981 * Then read the rest of the field as a
984 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
985 &tmask, tm, fsec, &is2digits)) < 0)
989 * modify tmask after returning from
990 * DecodeNumberField()
996 if (DecodePosixTimezone(field[i], tzp) != 0)
1003 else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1008 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
1012 * Check upper limit on hours; other limits checked in
1015 if (tm->tm_hour > 23)
1026 if (DecodeTimezone(field[i], &tz) != 0)
1030 * Already have a time zone? Then maybe this is the
1031 * second field of a POSIX time: EST+3 (equivalent to
1034 if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1035 && (ftype[i - 1] == DTK_TZ)
1036 && (isalpha((unsigned char) *field[i - 1])))
1052 * Was this an "ISO date" with embedded field labels? An
1053 * example is "y2001m02d04" - thomas 2001-02-04
1060 val = strtol(field[i], &cp, 10);
1063 * only a few kinds are allowed to have an embedded
1077 else if (*cp != '\0')
1084 tmask = DTK_M(YEAR);
1090 * already have a month and hour? then assume
1093 if (((fmask & DTK_M(MONTH)) != 0)
1094 && ((fmask & DTK_M(HOUR)) != 0))
1097 tmask = DTK_M(MINUTE);
1102 tmask = DTK_M(MONTH);
1113 tmask = DTK_M(HOUR);
1118 tmask = DTK_M(MINUTE);
1123 tmask = DTK_M(SECOND);
1128 frac = strtod(cp, &cp);
1131 #ifdef HAVE_INT64_TIMESTAMP
1132 *fsec = frac * 1000000;
1141 if (DecodeTimezone(field[i], tzp) != 0)
1147 * previous field was a label for "julian date"?
1150 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1151 /* fractional Julian Day? */
1156 time = strtod(cp, &cp);
1160 tmask |= DTK_TIME_M;
1161 #ifdef HAVE_INT64_TIMESTAMP
1162 dt2time((time * 86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1164 dt2time((time * 86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1170 /* previous field was "t" for ISO time */
1171 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1172 &tmask, tm, fsec, &is2digits)) < 0)
1175 if (tmask != DTK_TIME_M)
1192 flen = strlen(field[i]);
1193 cp = strchr(field[i], '.');
1195 /* Embedded decimal and no date yet? */
1196 if ((cp != NULL) && !(fmask & DTK_DATE_M))
1198 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1201 /* embedded decimal and several digits before? */
1202 else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
1205 * Interpret as a concatenated date or time Set
1206 * the type field to allow decoding other fields
1207 * later. Example: 20011223 or 040506
1209 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1210 &tmask, tm, fsec, &is2digits)) < 0)
1215 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1216 &tmask, tm, fsec, &is2digits)) < 0)
1219 /* otherwise it is a single date/time field... */
1220 else if (DecodeNumber(flen, field[i], fmask,
1221 &tmask, tm, fsec, &is2digits) != 0)
1228 type = DecodeSpecial(i, field[i], &val);
1229 if (type == IGNORE_DTF)
1232 tmask = DTK_M(type);
1239 elog(ERROR, "'CURRENT' is no longer supported");
1244 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1246 GetCurrentTimeUsec(tm, fsec);
1254 GetCurrentDateTime(tm);
1255 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
1256 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1265 GetCurrentDateTime(tm);
1274 GetCurrentDateTime(tm);
1275 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
1276 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1283 tmask = (DTK_TIME_M | DTK_M(TZ));
1301 * already have a (numeric) month? then see if we
1304 if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
1305 && (!(fmask & DTK_M(DAY)))
1306 && ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
1308 tm->tm_mday = tm->tm_mon;
1311 haveTextMonth = TRUE;
1318 * daylight savings time modifier (solves "MET
1321 tmask |= DTK_M(DTZ);
1331 * set mask for TZ here _or_ check for DTZ later
1332 * when getting default timezone
1373 * This is a filler field "t" indicating that the
1374 * next field is time. Try to verify that this is
1379 /* No preceeding date? Then quit... */
1380 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1384 * We will need one of the following fields:
1385 * DTK_NUMBER should be hhmmss.fff
1386 * DTK_TIME should be hh:mm:ss.fff
1387 * DTK_DATE should be hhmmss-zz
1390 || ((ftype[i + 1] != DTK_NUMBER)
1391 && (ftype[i + 1] != DTK_TIME)
1392 && (ftype[i + 1] != DTK_DATE)))
1412 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1415 if (tm->tm_year > 0)
1416 tm->tm_year = -(tm->tm_year - 1);
1418 elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year);
1422 if (tm->tm_year < 70)
1423 tm->tm_year += 2000;
1424 else if (tm->tm_year < 100)
1425 tm->tm_year += 1900;
1428 if ((mer != HR24) && (tm->tm_hour > 12))
1430 if ((mer == AM) && (tm->tm_hour == 12))
1432 else if ((mer == PM) && (tm->tm_hour != 12))
1435 /* do additional checking for full date specs... */
1436 if (*dtype == DTK_DATE)
1438 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1439 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
1442 * check for valid day of month, now that we know for sure the
1445 if ((tm->tm_mday < 1)
1446 || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
1449 /* timezone not specified? then find local timezone if possible */
1450 if (((fmask & DTK_DATE_M) == DTK_DATE_M)
1451 && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
1454 * daylight savings time modifier but no standard timezone?
1457 if (fmask & DTK_M(DTZMOD))
1460 *tzp = DetermineLocalTimeZone(tm);
1465 } /* DecodeDateTime() */
1468 /* DetermineLocalTimeZone()
1470 * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1471 * tm_sec fields are set, attempt to determine the applicable local zone
1472 * (ie, regular or daylight-savings time) at that time. Set the struct tm's
1473 * tm_isdst field accordingly, and return the actual timezone offset.
1475 * This subroutine exists to centralize uses of mktime() and defend against
1476 * mktime() bugs/restrictions on various platforms. This should be
1477 * the *only* call of mktime() in the backend.
1480 DetermineLocalTimeZone(struct tm * tm)
1486 tm->tm_isdst = 0; /* for lack of a better idea */
1489 else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
1491 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
1494 * Some buggy mktime() implementations may change the
1495 * year/month/day when given a time right at a DST boundary. To
1496 * prevent corruption of the caller's data, give mktime() a
1503 /* change to Unix conventions for year/month */
1504 tmp->tm_year -= 1900;
1507 /* indicate timezone unknown */
1510 if (mktime(tmp) != ((time_t) -1) &&
1513 /* mktime() succeeded, trust its result */
1514 tm->tm_isdst = tmp->tm_isdst;
1516 #if defined(HAVE_TM_ZONE)
1517 /* tm_gmtoff is Sun/DEC-ism */
1518 tz = -(tmp->tm_gmtoff);
1519 #elif defined(HAVE_INT_TIMEZONE)
1520 tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
1521 #endif /* HAVE_INT_TIMEZONE */
1526 * We have a buggy (not to say deliberately brain damaged)
1527 * mktime(). Work around it by using localtime() instead.
1529 * First, generate the time_t value corresponding to the given
1530 * y/m/d/h/m/s taken as GMT time. This will not overflow (at
1531 * least not for time_t taken as signed) because of the range
1532 * check we did above.
1541 day = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) -
1542 date2j(1970, 1, 1));
1543 mysec = tm->tm_sec + (tm->tm_min + (day * 24 + tm->tm_hour) * 60) * 60;
1544 mytime = (time_t) mysec;
1547 * Use localtime to convert that time_t to broken-down time,
1548 * and reassemble to get a representation of local time.
1550 tmp = localtime(&mytime);
1551 day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1552 date2j(1970, 1, 1));
1553 locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1556 * The local time offset corresponding to that GMT time is now
1557 * computable as mysec - locsec.
1559 delta1 = mysec - locsec;
1562 * However, if that GMT time and the local time we are
1563 * actually interested in are on opposite sides of a
1564 * daylight-savings-time transition, then this is not the time
1565 * offset we want. So, adjust the time_t to be what we think
1566 * the GMT time corresponding to our target local time is, and
1567 * repeat the localtime() call and delta calculation. We may
1568 * have to do it twice before we have a trustworthy delta.
1570 * Note: think not to put a loop here, since if we've been given
1571 * an "impossible" local time (in the gap during a
1572 * spring-forward transition) we'd never get out of the loop.
1573 * Twice is enough to give the behavior we want, which is that
1574 * "impossible" times are taken as standard time, while at a
1575 * fall-back boundary ambiguous times are also taken as
1579 mytime = (time_t) mysec;
1580 tmp = localtime(&mytime);
1581 day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1582 date2j(1970, 1, 1));
1583 locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1584 delta2 = mysec - locsec;
1585 if (delta2 != delta1)
1587 mysec += (delta2 - delta1);
1588 mytime = (time_t) mysec;
1589 tmp = localtime(&mytime);
1590 day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1591 date2j(1970, 1, 1));
1592 locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1593 delta2 = mysec - locsec;
1595 tm->tm_isdst = tmp->tm_isdst;
1598 #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1605 /* Given date is out of range, so assume UTC */
1615 * Interpret parsed string as time fields only.
1616 * Note that support for time zone is here for
1617 * SQL92 TIME WITH TIME ZONE, but it reveals
1618 * bogosity with SQL92 date/time standards, since
1619 * we must infer a time zone from current time.
1620 * - thomas 2000-03-10
1621 * Allow specifying date to get a better time zone,
1622 * if time zones are allowed. - thomas 2001-12-26
1625 DecodeTimeOnly(char **field, int *ftype, int nf,
1626 int *dtype, struct tm * tm, fsec_t *fsec, int *tzp)
1631 int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */
1634 int is2digits = FALSE;
1642 /* don't know daylight savings time status apriori */
1648 for (i = 0; i < nf; i++)
1655 * Time zone not allowed? Then should not accept dates or
1656 * time zones no matter what else!
1661 /* Under limited circumstances, we will accept a date... */
1662 if ((i == 0) && (nf >= 2)
1663 && ((ftype[nf - 1] == DTK_DATE)
1664 || (ftype[1] == DTK_TIME)))
1666 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1669 /* otherwise, this is a time and/or time zone */
1672 if (isdigit((unsigned char) *field[i]))
1677 * Starts with a digit but we already have a time
1678 * field? Then we are in trouble with time
1681 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1685 * Should not get here and fail. Sanity check
1688 if ((cp = strchr(field[i], '-')) == NULL)
1691 /* Get the time zone from the end of the string */
1692 if (DecodeTimezone(cp, tzp) != 0)
1697 * Then read the rest of the field as a
1700 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1701 &tmask, tm, fsec, &is2digits)) < 0)
1708 if (DecodePosixTimezone(field[i], tzp) != 0)
1718 if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0)
1729 if (DecodeTimezone(field[i], &tz) != 0)
1733 * Already have a time zone? Then maybe this is the
1734 * second field of a POSIX time: EST+3 (equivalent to
1737 if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1738 && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1])))
1754 * Was this an "ISO time" with embedded field labels? An
1755 * example is "h04m05s06" - thomas 2001-02-04
1762 /* Only accept a date under limited circumstances */
1775 val = strtol(field[i], &cp, 10);
1778 * only a few kinds are allowed to have an embedded
1792 else if (*cp != '\0')
1799 tmask = DTK_M(YEAR);
1805 * already have a month and hour? then assume
1808 if (((fmask & DTK_M(MONTH)) != 0)
1809 && ((fmask & DTK_M(HOUR)) != 0))
1812 tmask = DTK_M(MINUTE);
1817 tmask = DTK_M(MONTH);
1828 tmask = DTK_M(HOUR);
1833 tmask = DTK_M(MINUTE);
1838 tmask = DTK_M(SECOND);
1841 *fsec = strtod(cp, &cp);
1849 if (DecodeTimezone(field[i], tzp) != 0)
1855 * previous field was a label for "julian date"?
1858 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1863 time = strtod(cp, &cp);
1867 tmask |= DTK_TIME_M;
1868 #ifdef HAVE_INT64_TIMESTAMP
1869 dt2time((time * 86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1871 dt2time((time * 86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1877 /* previous field was "t" for ISO time */
1878 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1879 &tmask, tm, fsec, &is2digits)) < 0)
1882 if (tmask != DTK_TIME_M)
1899 flen = strlen(field[i]);
1900 cp = strchr(field[i], '.');
1902 /* Embedded decimal? */
1906 * Under limited circumstances, we will accept a
1909 if ((i == 0) && ((nf >= 2) && (ftype[nf - 1] == DTK_DATE)))
1911 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1914 /* embedded decimal and several digits before? */
1915 else if ((flen - strlen(cp)) > 2)
1918 * Interpret as a concatenated date or time
1919 * Set the type field to allow decoding other
1920 * fields later. Example: 20011223 or 040506
1922 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1923 &tmask, tm, fsec, &is2digits)) < 0)
1931 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1932 &tmask, tm, fsec, &is2digits)) < 0)
1935 /* otherwise it is a single date/time field... */
1936 else if (DecodeNumber(flen, field[i], fmask,
1937 &tmask, tm, fsec, &is2digits) != 0)
1944 type = DecodeSpecial(i, field[i], &val);
1945 if (type == IGNORE_DTF)
1948 tmask = DTK_M(type);
1955 elog(ERROR, "'CURRENT' is no longer supported");
1962 GetCurrentTimeUsec(tm, fsec);
1966 tmask = (DTK_TIME_M | DTK_M(TZ));
1983 * daylight savings time modifier (solves "MET
1986 tmask |= DTK_M(DTZ);
1996 * set mask for TZ here _or_ check for DTZ later
1997 * when getting default timezone
2031 * We will need one of the following fields:
2032 * DTK_NUMBER should be hhmmss.fff
2033 * DTK_TIME should be hh:mm:ss.fff
2034 * DTK_DATE should be hhmmss-zz
2037 || ((ftype[i + 1] != DTK_NUMBER)
2038 && (ftype[i + 1] != DTK_TIME)
2039 && (ftype[i + 1] != DTK_DATE)))
2059 if ((mer != HR24) && (tm->tm_hour > 12))
2061 if ((mer == AM) && (tm->tm_hour == 12))
2063 else if ((mer == PM) && (tm->tm_hour != 12))
2066 #ifdef HAVE_INT64_TIMESTAMP
2067 if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
2068 || ((tm->tm_min < 0) || (tm->tm_min > 59))
2069 || ((tm->tm_sec < 0) || (tm->tm_sec > 60))
2070 || (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
2073 if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
2074 || ((tm->tm_min < 0) || (tm->tm_min > 59))
2075 || ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60)))
2079 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2082 /* timezone not specified? then find local timezone if possible */
2083 if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
2089 * daylight savings time modifier but no standard timezone? then
2092 if (fmask & DTK_M(DTZMOD))
2095 if ((fmask & DTK_DATE_M) == 0)
2096 GetCurrentDateTime(tmp);
2099 tmp->tm_year = tm->tm_year;
2100 tmp->tm_mon = tm->tm_mon;
2101 tmp->tm_mday = tm->tm_mday;
2103 tmp->tm_hour = tm->tm_hour;
2104 tmp->tm_min = tm->tm_min;
2105 tmp->tm_sec = tm->tm_sec;
2106 *tzp = DetermineLocalTimeZone(tmp);
2107 tm->tm_isdst = tmp->tm_isdst;
2111 } /* DecodeTimeOnly() */
2114 * Decode date string which includes delimiters.
2115 * Insist on a complete set of fields.
2118 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
2126 int is2digits = FALSE;
2130 char *field[MAXDATEFIELDS];
2132 /* parse this string... */
2133 while ((*str != '\0') && (nf < MAXDATEFIELDS))
2135 /* skip field separators */
2136 while (!isalnum((unsigned char) *str))
2140 if (isdigit((unsigned char) *str))
2142 while (isdigit((unsigned char) *str))
2145 else if (isalpha((unsigned char) *str))
2147 while (isalpha((unsigned char) *str))
2151 /* Just get rid of any non-digit, non-alpha characters... */
2158 /* don't allow too many fields */
2165 /* look first for text fields, since that will be unambiguous month */
2166 for (i = 0; i < nf; i++)
2168 if (isalpha((unsigned char) *field[i]))
2170 type = DecodeSpecial(i, field[i], &val);
2171 if (type == IGNORE_DTF)
2174 dmask = DTK_M(type);
2194 /* mark this field as being completed */
2199 /* now pick up remaining numeric fields */
2200 for (i = 0; i < nf; i++)
2202 if (field[i] == NULL)
2205 if ((len = strlen(field[i])) <= 0)
2208 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
2218 if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2221 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2224 if (tm->tm_year > 0)
2225 tm->tm_year = -(tm->tm_year - 1);
2227 elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year);
2231 if (tm->tm_year < 70)
2232 tm->tm_year += 2000;
2233 else if (tm->tm_year < 100)
2234 tm->tm_year += 1900;
2238 } /* DecodeDate() */
2242 * Decode time string which includes delimiters.
2243 * Only check the lower limit on hours, since this same code
2244 * can be used to represent time spans.
2247 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
2251 *tmask = DTK_TIME_M;
2253 tm->tm_hour = strtol(str, &cp, 10);
2257 tm->tm_min = strtol(str, &cp, 10);
2263 else if (*cp != ':')
2268 tm->tm_sec = strtol(str, &cp, 10);
2271 else if (*cp == '.')
2273 #ifdef HAVE_INT64_TIMESTAMP
2274 char fstr[MAXDATELEN + 1];
2277 * OK, we have at most six digits to work with. Let's
2278 * construct a string and then do the conversion to an
2281 strncpy(fstr, (cp + 1), 7);
2282 strcpy((fstr + strlen(fstr)), "000000");
2284 *fsec = strtol(fstr, &cp, 10);
2287 *fsec = strtod(str, &cp);
2296 /* do a sanity check */
2297 #ifdef HAVE_INT64_TIMESTAMP
2298 if ((tm->tm_hour < 0)
2299 || (tm->tm_min < 0) || (tm->tm_min > 59)
2300 || (tm->tm_sec < 0) || (tm->tm_sec > 59)
2301 || (*fsec >= INT64CONST(1000000)))
2304 if ((tm->tm_hour < 0)
2305 || (tm->tm_min < 0) || (tm->tm_min > 59)
2306 || (tm->tm_sec < 0) || (tm->tm_sec > 59)
2312 } /* DecodeTime() */
2316 * Interpret plain numeric field as a date value in context.
2319 DecodeNumber(int flen, char *str, int fmask,
2320 int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
2327 val = strtol(str, &cp, 10);
2334 * More than two digits? Then could be a date or a run-together
2335 * time: 2001.360 20011225 040506.789
2338 return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
2339 tmask, tm, fsec, is2digits);
2341 *fsec = strtod(cp, &cp);
2345 else if (*cp != '\0')
2348 /* Special case day of year? */
2349 if ((flen == 3) && (fmask & DTK_M(YEAR))
2350 && ((val >= 1) && (val <= 366)))
2352 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2354 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
2355 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2359 * Enough digits to be unequivocal year? Used to test for 4 digits or
2360 * more, but we now test first for a three-digit doy so anything
2361 * bigger than two digits had better be an explicit year.
2362 * - thomas 1999-01-09
2363 * Back to requiring a 4 digit year. We accept a two digit
2364 * year farther down. - thomas 2000-03-28
2368 *tmask = DTK_M(YEAR);
2370 /* already have a year? then see if we can substitute... */
2371 if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
2372 && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
2374 tm->tm_mday = tm->tm_year;
2375 *tmask = DTK_M(DAY);
2381 /* already have year? then could be month */
2382 else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
2383 && ((val >= 1) && (val <= 12)))
2385 *tmask = DTK_M(MONTH);
2388 /* no year and EuroDates enabled? then could be day */
2389 else if ((EuroDates || (fmask & DTK_M(MONTH)))
2390 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
2391 && ((val >= 1) && (val <= 31)))
2393 *tmask = DTK_M(DAY);
2396 else if ((!(fmask & DTK_M(MONTH)))
2397 && ((val >= 1) && (val <= 12)))
2399 *tmask = DTK_M(MONTH);
2402 else if ((!(fmask & DTK_M(DAY)))
2403 && ((val >= 1) && (val <= 31)))
2405 *tmask = DTK_M(DAY);
2410 * Check for 2 or 4 or more digits, but currently we reach here only
2411 * if two digits. - thomas 2000-03-28
2413 else if (!(fmask & DTK_M(YEAR))
2414 && ((flen >= 4) || (flen == 2)))
2416 *tmask = DTK_M(YEAR);
2419 /* adjust ONLY if exactly two digits... */
2420 *is2digits = (flen == 2);
2426 } /* DecodeNumber() */
2429 /* DecodeNumberField()
2430 * Interpret numeric string as a concatenated date or time field.
2431 * Use the context of previously decoded fields to help with
2432 * the interpretation.
2435 DecodeNumberField(int len, char *str, int fmask,
2436 int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
2441 * Have a decimal point? Then this is a date or something with a
2444 if ((cp = strchr(str, '.')) != NULL)
2446 #ifdef HAVE_INT64_TIMESTAMP
2447 char fstr[MAXDATELEN + 1];
2450 * OK, we have at most six digits to care about. Let's construct a
2451 * string and then do the conversion to an integer.
2453 strcpy(fstr, (cp + 1));
2454 strcpy((fstr + strlen(fstr)), "000000");
2456 *fsec = strtol(fstr, NULL, 10);
2458 *fsec = strtod(cp, NULL);
2463 /* No decimal point and no complete date yet? */
2464 else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2469 *tmask = DTK_DATE_M;
2471 tm->tm_mday = atoi(str + 6);
2473 tm->tm_mon = atoi(str + 4);
2475 tm->tm_year = atoi(str + 0);
2482 *tmask = DTK_DATE_M;
2483 tm->tm_mday = atoi(str + 4);
2485 tm->tm_mon = atoi(str + 2);
2487 tm->tm_year = atoi(str + 0);
2495 *tmask = DTK_DATE_M;
2496 tm->tm_mday = atoi(str + 2);
2499 tm->tm_year = atoi(str + 0);
2506 /* not all time fields are specified? */
2507 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2512 *tmask = DTK_TIME_M;
2513 tm->tm_sec = atoi(str + 4);
2515 tm->tm_min = atoi(str + 2);
2517 tm->tm_hour = atoi(str + 0);
2524 *tmask = DTK_TIME_M;
2526 tm->tm_min = atoi(str + 2);
2528 tm->tm_hour = atoi(str + 0);
2535 } /* DecodeNumberField() */
2539 * Interpret string as a numeric timezone.
2541 * Note: we allow timezone offsets up to 13:59. There are places that
2542 * use +1300 summer time.
2545 DecodeTimezone(char *str, int *tzp)
2553 /* assume leading character is "+" or "-" */
2554 hr = strtol((str + 1), &cp, 10);
2556 /* explicit delimiter? */
2558 min = strtol((cp + 1), &cp, 10);
2559 /* otherwise, might have run things together... */
2560 else if ((*cp == '\0') && ((len = strlen(str)) > 3))
2562 min = strtol((str + len - 2), &cp, 10);
2563 if ((min < 0) || (min >= 60))
2566 *(str + len - 2) = '\0';
2567 hr = strtol((str + 1), &cp, 10);
2568 if ((hr < 0) || (hr > 13))
2574 tz = (hr * 60 + min) * 60;
2580 } /* DecodeTimezone() */
2583 /* DecodePosixTimezone()
2584 * Interpret string as a POSIX-compatible timezone:
2587 * - thomas 2000-03-15
2590 DecodePosixTimezone(char *str, int *tzp)
2599 while ((*cp != '\0') && isalpha((unsigned char) *cp))
2602 if (DecodeTimezone(cp, &tz) != 0)
2607 type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
2614 *tzp = (val * 60) - tz;
2622 } /* DecodePosixTimezone() */
2626 * Decode text string using lookup table.
2627 * Implement a cache lookup since it is likely that dates
2628 * will be related in format.
2631 DecodeSpecial(int field, char *lowtoken, int *val)
2636 if ((datecache[field] != NULL)
2637 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
2638 tp = datecache[field];
2642 if (Australian_timezones)
2643 tp = datebsearch(lowtoken, australian_datetktbl,
2644 australian_szdatetktbl);
2646 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
2648 datecache[field] = tp;
2651 type = UNKNOWN_FIELD;
2672 } /* DecodeSpecial() */
2676 * Interpret previously parsed fields for general time interval.
2677 * Return 0 if decoded and -1 if problems.
2679 * Allow "date" field DTK_DATE since this could be just
2680 * an unsigned floating point number. - thomas 1997-11-16
2682 * Allow ISO-style time span, with implicit units on number of days
2683 * preceding an hh:mm:ss field. - thomas 1998-04-30
2686 DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
2688 int is_before = FALSE;
2709 /* read through list backwards to pick up units before values */
2710 for (i = nf - 1; i >= 0; i--)
2715 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
2723 * Timezone is a token with a leading sign character and
2724 * otherwise the same as a non-signed time field
2726 Assert((*field[i] == '-') || (*field[i] == '+'));
2729 * A single signed number ends up here, but will be
2730 * rejected by DecodeTime(). So, work this out to drop
2731 * through to DTK_NUMBER, which *can* tolerate this.
2734 while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
2737 && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0))
2739 if (*field[i] == '-')
2741 /* flip the sign on all fields */
2742 tm->tm_hour = -tm->tm_hour;
2743 tm->tm_min = -tm->tm_min;
2744 tm->tm_sec = -tm->tm_sec;
2749 * Set the next type to be a day, if units are not
2750 * specified. This handles the case of '1 +02:03'
2751 * since we are reading right to left.
2757 else if (type == IGNORE_DTF)
2762 * Got a decimal point? Then assume some sort of
2763 * seconds specification
2767 else if (*cp == '\0')
2770 * Only a signed integer? Then must assume a
2771 * timezone-like usage
2780 val = strtol(field[i], &cp, 10);
2782 if (type == IGNORE_DTF)
2787 fval = strtod(cp, &cp);
2794 else if (*cp == '\0')
2799 tmask = 0; /* DTK_M(type); */
2804 #ifdef HAVE_INT64_TIMESTAMP
2805 *fsec += (val + fval);
2807 *fsec += ((val + fval) * 1e-6);
2812 #ifdef HAVE_INT64_TIMESTAMP
2813 *fsec += ((val + fval) * 1000);
2815 *fsec += ((val + fval) * 1e-3);
2821 #ifdef HAVE_INT64_TIMESTAMP
2822 *fsec += (fval * 1000000);
2826 tmask = DTK_M(SECOND);
2838 #ifdef HAVE_INT64_TIMESTAMP
2839 *fsec += ((fval - sec) * 1000000);
2841 *fsec += (fval - sec);
2844 tmask = DTK_M(MINUTE);
2856 #ifdef HAVE_INT64_TIMESTAMP
2857 *fsec += ((fval - sec) * 1000000);
2859 *fsec += (fval - sec);
2862 tmask = DTK_M(HOUR);
2874 #ifdef HAVE_INT64_TIMESTAMP
2875 *fsec += ((fval - sec) * 1000000);
2877 *fsec += (fval - sec);
2880 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
2884 tm->tm_mday += val * 7;
2889 fval *= (7 * 86400);
2892 #ifdef HAVE_INT64_TIMESTAMP
2893 *fsec += ((fval - sec) * 1000000);
2895 *fsec += (fval - sec);
2898 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
2907 fval *= (30 * 86400);
2910 #ifdef HAVE_INT64_TIMESTAMP
2911 *fsec += ((fval - sec) * 1000000);
2913 *fsec += (fval - sec);
2916 tmask = DTK_M(MONTH);
2922 tm->tm_mon += (fval * 12);
2923 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2927 tm->tm_year += val * 10;
2929 tm->tm_mon += (fval * 120);
2930 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2934 tm->tm_year += val * 100;
2936 tm->tm_mon += (fval * 1200);
2937 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2940 case DTK_MILLENNIUM:
2941 tm->tm_year += val * 1000;
2943 tm->tm_mon += (fval * 12000);
2944 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2954 type = DecodeUnits(i, field[i], &val);
2955 if (type == IGNORE_DTF)
2958 tmask = 0; /* DTK_M(type); */
2971 tmask = (DTK_DATE_M || DTK_TIME_M);
2993 #ifdef HAVE_INT64_TIMESTAMP
2994 sec = (*fsec / INT64CONST(1000000));
2995 *fsec -= (sec * INT64CONST(1000000));
2997 TMODULO(*fsec, sec, 1e0);
3005 tm->tm_sec = -(tm->tm_sec);
3006 tm->tm_min = -(tm->tm_min);
3007 tm->tm_hour = -(tm->tm_hour);
3008 tm->tm_mday = -(tm->tm_mday);
3009 tm->tm_mon = -(tm->tm_mon);
3010 tm->tm_year = -(tm->tm_year);
3013 /* ensure that at least one time field has been found */
3014 return (fmask != 0) ? 0 : -1;
3015 } /* DecodeInterval() */
3019 * Decode text string using lookup table.
3020 * This routine supports time interval decoding.
3023 DecodeUnits(int field, char *lowtoken, int *val)
3028 if ((deltacache[field] != NULL)
3029 && (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
3030 tp = deltacache[field];
3032 tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3033 deltacache[field] = tp;
3036 type = UNKNOWN_FIELD;
3042 if ((type == TZ) || (type == DTZ))
3049 } /* DecodeUnits() */
3053 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
3054 * is WAY faster than the generic bsearch().
3057 datebsearch(char *key, datetkn *base, unsigned int nel)
3059 datetkn *last = base + nel - 1,
3063 while (last >= base)
3065 position = base + ((last - base) >> 1);
3066 result = key[0] - position->token[0];
3069 result = strncmp(key, position->token, TOKMAXLEN);
3074 last = position - 1;
3076 base = position + 1;
3083 * Encode date as local time.
3086 EncodeDateOnly(struct tm * tm, int style, char *str)
3088 if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
3094 /* compatible with ISO date formats */
3095 if (tm->tm_year > 0)
3096 sprintf(str, "%04d-%02d-%02d",
3097 tm->tm_year, tm->tm_mon, tm->tm_mday);
3099 sprintf(str, "%04d-%02d-%02d %s",
3100 -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
3104 /* compatible with Oracle/Ingres date formats */
3106 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3108 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3109 if (tm->tm_year > 0)
3110 sprintf((str + 5), "/%04d", tm->tm_year);
3112 sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
3115 case USE_GERMAN_DATES:
3116 /* German-style date format */
3117 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3118 if (tm->tm_year > 0)
3119 sprintf((str + 5), ".%04d", tm->tm_year);
3121 sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
3124 case USE_POSTGRES_DATES:
3126 /* traditional date-only style for Postgres */
3128 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
3130 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
3131 if (tm->tm_year > 0)
3132 sprintf((str + 5), "-%04d", tm->tm_year);
3134 sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
3139 } /* EncodeDateOnly() */
3143 * Encode time fields only.
3146 EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str)
3148 #ifndef HAVE_INT64_TIMESTAMP
3152 if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
3155 #ifndef HAVE_INT64_TIMESTAMP
3156 sec = (tm->tm_sec + fsec);
3159 sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
3162 * If we have fractional seconds, then include a decimal point We will
3163 * do up to 6 fractional digits, and we have rounded any inputs to
3164 * eliminate anything to the right of 6 digits anyway. If there are no
3165 * fractional seconds, then do not bother printing a decimal point at
3166 * all. - thomas 2001-09-29
3170 #ifdef HAVE_INT64_TIMESTAMP
3171 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3172 sprintf((str + strlen(str)), ".%06d", fsec);
3174 sprintf((str + strlen(str)), ":%013.10f", sec);
3176 /* chop off trailing pairs of zeros... */
3177 while ((strcmp((str + strlen(str) - 2), "00") == 0)
3178 && (*(str + strlen(str) - 3) != '.'))
3179 *(str + strlen(str) - 2) = '\0';
3182 #ifdef HAVE_INT64_TIMESTAMP
3183 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3185 sprintf((str + strlen(str)), ":%02.0f", sec);
3193 hour = -(*tzp / 3600);
3194 min = ((abs(*tzp) / 60) % 60);
3195 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3199 } /* EncodeTimeOnly() */
3203 * Encode date and time interpreted as local time.
3204 * Support several date styles:
3205 * Postgres - day mon hh:mm:ss yyyy tz
3206 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
3207 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
3208 * German - dd.mm.yyyy hh:mm:ss tz
3209 * Variants (affects order of month and day for Postgres and SQL styles):
3211 * European - dd/mm/yyyy
3214 EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str)
3220 #ifndef HAVE_INT64_TIMESTAMP
3225 * Why are we checking only the month field? Change this to an
3226 * assert... if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) return -1;
3228 Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12));
3230 #ifndef HAVE_INT64_TIMESTAMP
3231 sec = (tm->tm_sec + fsec);
3237 /* Compatible with ISO-8601 date formats */
3239 sprintf(str, "%04d-%02d-%02d %02d:%02d",
3240 ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3241 tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
3244 * If we have fractional seconds, then include a decimal point
3245 * We will do up to 6 fractional digits, and we have rounded
3246 * any inputs to eliminate anything to the right of 6 digits
3247 * anyway. If there are no fractional seconds, then do not
3248 * bother printing a decimal point at all. - thomas 2001-09-29
3250 #ifdef HAVE_INT64_TIMESTAMP
3253 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3254 sprintf((str + strlen(str)), ".%06d", fsec);
3256 if ((fsec != 0) && (tm->tm_year > 0))
3258 sprintf((str + strlen(str)), ":%013.10f", sec);
3260 TrimTrailingZeros(str);
3263 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3265 if (tm->tm_year <= 0)
3266 sprintf((str + strlen(str)), " BC");
3269 * tzp == NULL indicates that we don't want *any* time zone
3270 * info in the output string. *tzn != NULL indicates that we
3271 * have alpha time zone info available. tm_isdst != -1
3272 * indicates that we have a valid time zone translation.
3274 if ((tzp != NULL) && (tm->tm_isdst >= 0))
3276 hour = -(*tzp / 3600);
3277 min = ((abs(*tzp) / 60) % 60);
3278 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3283 /* Compatible with Oracle/Ingres date formats */
3286 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3288 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3290 sprintf((str + 5), "/%04d %02d:%02d",
3291 ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3292 tm->tm_hour, tm->tm_min);
3295 * If we have fractional seconds, then include a decimal point
3296 * We will do up to 6 fractional digits, and we have rounded
3297 * any inputs to eliminate anything to the right of 6 digits
3298 * anyway. If there are no fractional seconds, then do not
3299 * bother printing a decimal point at all. - thomas 2001-09-29
3301 #ifdef HAVE_INT64_TIMESTAMP
3304 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3305 sprintf((str + strlen(str)), ".%06d", fsec);
3307 if ((fsec != 0) && (tm->tm_year > 0))
3309 sprintf((str + strlen(str)), ":%013.10f", sec);
3311 TrimTrailingZeros(str);
3314 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3316 if (tm->tm_year <= 0)
3317 sprintf((str + strlen(str)), " BC");
3319 if ((tzp != NULL) && (tm->tm_isdst >= 0))
3322 sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3325 hour = -(*tzp / 3600);
3326 min = ((abs(*tzp) / 60) % 60);
3327 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3332 case USE_GERMAN_DATES:
3333 /* German variant on European style */
3335 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3337 sprintf((str + 5), ".%04d %02d:%02d",
3338 ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3339 tm->tm_hour, tm->tm_min);
3342 * If we have fractional seconds, then include a decimal point
3343 * We will do up to 6 fractional digits, and we have rounded
3344 * any inputs to eliminate anything to the right of 6 digits
3345 * anyway. If there are no fractional seconds, then do not
3346 * bother printing a decimal point at all. - thomas 2001-09-29
3348 #ifdef HAVE_INT64_TIMESTAMP
3351 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3352 sprintf((str + strlen(str)), ".%06d", fsec);
3354 if ((fsec != 0) && (tm->tm_year > 0))
3356 sprintf((str + strlen(str)), ":%013.10f", sec);
3358 TrimTrailingZeros(str);
3361 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3363 if (tm->tm_year <= 0)
3364 sprintf((str + strlen(str)), " BC");
3366 if ((tzp != NULL) && (tm->tm_isdst >= 0))
3369 sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3372 hour = -(*tzp / 3600);
3373 min = ((abs(*tzp) / 60) % 60);
3374 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3379 case USE_POSTGRES_DATES:
3381 /* Backward-compatible with traditional Postgres abstime dates */
3383 day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3384 tm->tm_wday = j2day(day);
3386 strncpy(str, days[tm->tm_wday], 3);
3387 strcpy((str + 3), " ");
3390 sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
3392 sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
3394 sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
3397 * If we have fractional seconds, then include a decimal point
3398 * We will do up to 6 fractional digits, and we have rounded
3399 * any inputs to eliminate anything to the right of 6 digits
3400 * anyway. If there are no fractional seconds, then do not
3401 * bother printing a decimal point at all. - thomas 2001-09-29
3403 #ifdef HAVE_INT64_TIMESTAMP
3406 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3407 sprintf((str + strlen(str)), ".%06d", fsec);
3409 if ((fsec != 0) && (tm->tm_year > 0))
3411 sprintf((str + strlen(str)), ":%013.10f", sec);
3413 TrimTrailingZeros(str);
3416 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3418 sprintf((str + strlen(str)), " %04d",
3419 ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)));
3420 if (tm->tm_year <= 0)
3421 sprintf((str + strlen(str)), " BC");
3423 if ((tzp != NULL) && (tm->tm_isdst >= 0))
3426 sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3430 * We have a time zone, but no string version. Use the
3431 * numeric form, but be sure to include a leading
3432 * space to avoid formatting something which would be
3433 * rejected by the date/time parser later. - thomas
3436 hour = -(*tzp / 3600);
3437 min = ((abs(*tzp) / 60) % 60);
3438 sprintf((str + strlen(str)), ((min != 0) ? " %+03d:%02d" : " %+03d"), hour, min);
3445 } /* EncodeDateTime() */
3449 * Interpret time structure as a delta time and convert to string.
3451 * Support "traditional Postgres" and ISO-8601 styles.
3452 * Actually, afaik ISO does not address time interval formatting,
3453 * but this looks similar to the spec for absolute date/time.
3454 * - thomas 1998-04-30
3457 EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
3459 int is_before = FALSE;
3460 int is_nonzero = FALSE;
3464 * The sign of year and month are guaranteed to match, since they are
3465 * stored internally as "month". But we'll need to check for is_before
3466 * and is_nonzero when determining the signs of hour/minute/seconds
3471 /* compatible with ISO date formats */
3473 if (tm->tm_year != 0)
3475 sprintf(cp, "%d year%s",
3476 tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
3478 is_before = (tm->tm_year < 0);
3482 if (tm->tm_mon != 0)
3484 sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""),
3485 ((is_before && (tm->tm_mon > 0)) ? "+" : ""),
3486 tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
3488 is_before = (tm->tm_mon < 0);
3492 if (tm->tm_mday != 0)
3494 sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""),
3495 ((is_before && (tm->tm_mday > 0)) ? "+" : ""),
3496 tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
3498 is_before = (tm->tm_mday < 0);
3501 if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0)
3502 || (tm->tm_sec != 0) || (fsec != 0))
3504 int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
3505 || (tm->tm_sec < 0) || (fsec < 0));
3507 sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
3508 (minus ? "-" : (is_before ? "+" : "")),
3509 abs(tm->tm_hour), abs(tm->tm_min));
3511 /* Mark as "non-zero" since the fields are now filled in */
3514 /* fractional seconds? */
3517 #ifdef HAVE_INT64_TIMESTAMP
3518 sprintf(cp, ":%02d", abs(tm->tm_sec));
3520 sprintf(cp, ".%06d", ((fsec >= 0) ? fsec : -(fsec)));
3523 sprintf(cp, ":%013.10f", fabs(fsec));
3525 TrimTrailingZeros(cp);
3529 /* otherwise, integer seconds only? */
3530 else if (tm->tm_sec != 0)
3532 sprintf(cp, ":%02d", abs(tm->tm_sec));
3539 case USE_POSTGRES_DATES:
3544 if (tm->tm_year != 0)
3546 int year = tm->tm_year;
3548 if (tm->tm_year < 0)
3551 sprintf(cp, "%d year%s", year,
3552 ((year != 1) ? "s" : ""));
3554 is_before = (tm->tm_year < 0);
3558 if (tm->tm_mon != 0)
3560 int mon = tm->tm_mon;
3562 if (is_before || ((!is_nonzero) && (tm->tm_mon < 0)))
3565 sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon,
3566 ((mon != 1) ? "s" : ""));
3569 is_before = (tm->tm_mon < 0);
3573 if (tm->tm_mday != 0)
3575 int day = tm->tm_mday;
3577 if (is_before || ((!is_nonzero) && (tm->tm_mday < 0)))
3580 sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day,
3581 ((day != 1) ? "s" : ""));
3584 is_before = (tm->tm_mday < 0);
3587 if (tm->tm_hour != 0)
3589 int hour = tm->tm_hour;
3591 if (is_before || ((!is_nonzero) && (tm->tm_hour < 0)))
3594 sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour,
3595 ((hour != 1) ? "s" : ""));
3598 is_before = (tm->tm_hour < 0);
3602 if (tm->tm_min != 0)
3604 int min = tm->tm_min;
3606 if (is_before || ((!is_nonzero) && (tm->tm_min < 0)))
3609 sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min,
3610 ((min != 1) ? "s" : ""));
3613 is_before = (tm->tm_min < 0);
3617 /* fractional seconds? */
3620 #ifdef HAVE_INT64_TIMESTAMP
3621 if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3622 tm->tm_sec = -tm->tm_sec;
3623 sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""),
3624 tm->tm_sec, (((int) fsec) / 10000));
3627 is_before = (fsec < 0);
3633 if (is_before || ((!is_nonzero) && (fsec < 0)))
3636 sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec);
3639 is_before = (fsec < 0);
3643 /* otherwise, integer seconds only? */
3645 else if (tm->tm_sec != 0)
3647 int sec = tm->tm_sec;
3649 if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3652 sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec,
3653 ((sec != 1) ? "s" : ""));
3656 is_before = (tm->tm_sec < 0);
3662 /* identically zero? then put in a unitless zero... */
3669 if (is_before && (style == USE_POSTGRES_DATES))
3676 } /* EncodeInterval() */
3679 /* GUC assign_hook for australian_timezones */
3681 ClearDateCache(bool newval, bool doit, bool interactive)
3687 for (i = 0; i < MAXDATEFIELDS; i++)
3688 datecache[i] = NULL;