1 /* src/interfaces/ecpg/pgtypeslib/dt_common.c */
3 #include "postgres_fe.h"
9 #include "common/string.h"
11 #include "pgtypes_timestamp.h"
12 #include "pgtypeslib_extern.h"
14 const int day_tab[2][13] = {
15 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
16 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
18 typedef long AbsoluteTime;
20 static const datetkn datetktbl[] = {
21 /* text, token, lexval */
22 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
23 {"acsst", DTZ, 37800}, /* Cent. Australia */
24 {"acst", DTZ, -14400}, /* Atlantic/Porto Acre */
25 {"act", TZ, -18000}, /* Atlantic/Porto Acre */
26 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
27 {"adt", DTZ, -10800}, /* Atlantic Daylight Time */
28 {"aesst", DTZ, 39600}, /* E. Australia */
29 {"aest", TZ, 36000}, /* Australia Eastern Std Time */
30 {"aft", TZ, 16200}, /* Kabul */
31 {"ahst", TZ, -36000}, /* Alaska-Hawaii Std Time */
32 {"akdt", DTZ, -28800}, /* Alaska Daylight Time */
33 {"akst", DTZ, -32400}, /* Alaska Standard Time */
34 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
35 {"almst", TZ, 25200}, /* Almaty Savings Time */
36 {"almt", TZ, 21600}, /* Almaty Time */
38 {"amst", DTZ, 18000}, /* Armenia Summer Time (Yerevan) */
40 {"amst", DTZ, -10800}, /* Porto Velho */
42 {"amt", TZ, 14400}, /* Armenia Time (Yerevan) */
43 {"anast", DTZ, 46800}, /* Anadyr Summer Time (Russia) */
44 {"anat", TZ, 43200}, /* Anadyr Time (Russia) */
52 {"art", TZ, -10800}, /* Argentina Time */
55 ast /* Atlantic Standard Time, Arabia Standard
56 * Time, Acre Standard Time */
58 {"ast", TZ, -14400}, /* Atlantic Std Time (Canada) */
59 {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
62 {"awsst", DTZ, 32400}, /* W. Australia */
63 {"awst", TZ, 28800}, /* W. Australia */
65 {"azost", DTZ, 0}, /* Azores Summer Time */
66 {"azot", TZ, -3600}, /* Azores Time */
67 {"azst", DTZ, 18000}, /* Azerbaijan Summer Time */
68 {"azt", TZ, 14400}, /* Azerbaijan Time */
69 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
70 {"bdst", TZ, 7200}, /* British Double Summer Time */
71 {"bdt", TZ, 21600}, /* Dacca */
72 {"bnt", TZ, 28800}, /* Brunei Darussalam Time */
73 {"bort", TZ, 28800}, /* Borneo Time (Indonesia) */
78 {"bot", TZ, -14400}, /* Bolivia Time */
79 {"bra", TZ, -10800}, /* Brazil Time */
84 {"bst", DTZ, 3600}, /* British Summer Time */
86 {"bst", TZ, -10800}, /* Brazil Standard Time */
87 {"bst", DTZ, -39600}, /* Bering Summer Time */
89 {"bt", TZ, 10800}, /* Baghdad Time */
90 {"btt", TZ, 21600}, /* Bhutan Time */
91 {"cadt", DTZ, 37800}, /* Central Australian DST */
92 {"cast", TZ, 34200}, /* Central Australian ST */
93 {"cat", TZ, -36000}, /* Central Alaska Time */
94 {"cct", TZ, 28800}, /* China Coast Time */
96 {"cct", TZ, 23400}, /* Indian Cocos (Island) Time */
98 {"cdt", DTZ, -18000}, /* Central Daylight Time */
99 {"cest", DTZ, 7200}, /* Central European Dayl.Time */
100 {"cet", TZ, 3600}, /* Central European Time */
101 {"cetdst", DTZ, 7200}, /* Central European Dayl.Time */
102 {"chadt", DTZ, 49500}, /* Chatham Island Daylight Time (13:45) */
103 {"chast", TZ, 45900}, /* Chatham Island Time (12:45) */
107 {"ckt", TZ, 43200}, /* Cook Islands Time */
108 {"clst", DTZ, -10800}, /* Chile Summer Time */
109 {"clt", TZ, -14400}, /* Chile Time */
113 {"cot", TZ, -18000}, /* Columbia Time */
114 {"cst", TZ, -21600}, /* Central Standard Time */
118 {"cvt", TZ, 25200}, /* Christmas Island Time (Indian Ocean) */
119 {"cxt", TZ, 25200}, /* Christmas Island Time (Indian Ocean) */
120 {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
121 {"davt", TZ, 25200}, /* Davis Time (Antarctica) */
122 {"ddut", TZ, 36000}, /* Dumont-d'Urville Time (Antarctica) */
124 {"december", MONTH, 12},
125 {"dnt", TZ, 3600}, /* Dansk Normal Tid */
126 {"dow", UNITS, DTK_DOW}, /* day of week */
127 {"doy", UNITS, DTK_DOY}, /* day of year */
128 {"dst", DTZMOD, SECS_PER_HOUR},
130 {"dusst", DTZ, 21600}, /* Dushanbe Summer Time */
132 {"easst", DTZ, -18000}, /* Easter Island Summer Time */
133 {"east", TZ, -21600}, /* Easter Island Time */
134 {"eat", TZ, 10800}, /* East Africa Time */
136 {"east", DTZ, 14400}, /* Indian Antananarivo Savings Time */
137 {"eat", TZ, 10800}, /* Indian Antananarivo Time */
138 {"ect", TZ, -14400}, /* Eastern Caribbean Time */
139 {"ect", TZ, -18000}, /* Ecuador Time */
141 {"edt", DTZ, -14400}, /* Eastern Daylight Time */
142 {"eest", DTZ, 10800}, /* Eastern Europe Summer Time */
143 {"eet", TZ, 7200}, /* East. Europe, USSR Zone 1 */
144 {"eetdst", DTZ, 10800}, /* Eastern Europe Daylight Time */
145 {"egst", DTZ, 0}, /* East Greenland Summer Time */
146 {"egt", TZ, -3600}, /* East Greenland Time */
150 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
151 {"est", TZ, -18000}, /* Eastern Standard Time */
153 {"february", MONTH, 2},
154 {"fjst", DTZ, -46800}, /* Fiji Summer Time (13 hour offset!) */
155 {"fjt", TZ, -43200}, /* Fiji Time */
156 {"fkst", DTZ, -10800}, /* Falkland Islands Summer Time */
157 {"fkt", TZ, -7200}, /* Falkland Islands Time */
164 {"fst", TZ, 3600}, /* French Summer Time */
165 {"fwt", DTZ, 7200}, /* French Winter Time */
166 {"galt", TZ, -21600}, /* Galapagos Time */
167 {"gamt", TZ, -32400}, /* Gambier Time */
168 {"gest", DTZ, 18000}, /* Georgia Summer Time */
169 {"get", TZ, 14400}, /* Georgia Time */
170 {"gft", TZ, -10800}, /* French Guiana Time */
174 {"gilt", TZ, 43200}, /* Gilbert Islands Time */
175 {"gmt", TZ, 0}, /* Greenwich Mean Time */
176 {"gst", TZ, 36000}, /* Guam Std Time, USSR Zone 9 */
177 {"gyt", TZ, -14400}, /* Guyana Time */
178 {"h", UNITS, DTK_HOUR}, /* "hour" */
183 {"hdt", DTZ, -32400}, /* Hawaii/Alaska Daylight Time */
187 {"hkt", TZ, 28800}, /* Hong Kong Time */
189 {"hmt", TZ, 10800}, /* Hellas ? ? */
193 {"hst", TZ, -36000}, /* Hawaii Std Time */
197 {"ict", TZ, 25200}, /* Indochina Time */
198 {"idle", TZ, 43200}, /* Intl. Date Line, East */
199 {"idlw", TZ, -43200}, /* Intl. Date Line, West */
201 idt /* Israeli, Iran, Indian Daylight Time */
203 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
204 {"iot", TZ, 18000}, /* Indian Chagos Time */
205 {"irkst", DTZ, 32400}, /* Irkutsk Summer Time */
206 {"irkt", TZ, 28800}, /* Irkutsk Time */
207 {"irt", TZ, 12600}, /* Iran Time */
208 {"isodow", UNITS, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */
212 {"ist", TZ, 7200}, /* Israel */
213 {"it", TZ, 12600}, /* Iran Time */
214 {"j", UNITS, DTK_JULIAN},
216 {"january", MONTH, 1},
217 {"javt", TZ, 25200}, /* Java Time (07:00? see JT) */
218 {"jayt", TZ, 32400}, /* Jayapura Time (Indonesia) */
219 {"jd", UNITS, DTK_JULIAN},
220 {"jst", TZ, 32400}, /* Japan Std Time,USSR Zone 8 */
221 {"jt", TZ, 27000}, /* Java Time (07:30? see JAVT) */
223 {"julian", UNITS, DTK_JULIAN},
227 {"kdt", DTZ, 36000}, /* Korea Daylight Time */
228 {"kgst", DTZ, 21600}, /* Kyrgyzstan Summer Time */
229 {"kgt", TZ, 18000}, /* Kyrgyzstan Time */
230 {"kost", TZ, 43200}, /* Kosrae Time */
231 {"krast", DTZ, 25200}, /* Krasnoyarsk Summer Time */
232 {"krat", TZ, 28800}, /* Krasnoyarsk Standard Time */
233 {"kst", TZ, 32400}, /* Korea Standard Time */
234 {"lhdt", DTZ, 39600}, /* Lord Howe Daylight Time, Australia */
235 {"lhst", TZ, 37800}, /* Lord Howe Standard Time, Australia */
236 {"ligt", TZ, 36000}, /* From Melbourne, Australia */
237 {"lint", TZ, 50400}, /* Line Islands Time (Kiribati; +14 hours!) */
238 {"lkt", TZ, 21600}, /* Lanka Time */
239 {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
240 {"magst", DTZ, 43200}, /* Magadan Summer Time */
241 {"magt", TZ, 39600}, /* Magadan Time */
244 {"mart", TZ, -34200}, /* Marquesas Time */
245 {"mawt", TZ, 21600}, /* Mawson, Antarctica */
247 {"mdt", DTZ, -21600}, /* Mountain Daylight Time */
248 {"mest", DTZ, 7200}, /* Middle Europe Summer Time */
249 {"met", TZ, 3600}, /* Middle Europe Time */
250 {"metdst", DTZ, 7200}, /* Middle Europe Daylight Time */
251 {"mewt", TZ, 3600}, /* Middle Europe Winter Time */
252 {"mez", TZ, 3600}, /* Middle Europe Zone */
253 {"mht", TZ, 43200}, /* Kwajalein */
254 {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
255 {"mmt", TZ, 23400}, /* Myannar Time */
261 {"mpt", TZ, 36000}, /* North Mariana Islands Time */
262 {"msd", DTZ, 14400}, /* Moscow Summer Time */
263 {"msk", TZ, 10800}, /* Moscow Time */
264 {"mst", TZ, -25200}, /* Mountain Standard Time */
265 {"mt", TZ, 30600}, /* Moluccas Time */
266 {"mut", TZ, 14400}, /* Mauritius Island Time */
267 {"mvt", TZ, 18000}, /* Maldives Island Time */
268 {"myt", TZ, 28800}, /* Malaysia Time */
272 {"nct", TZ, 39600}, /* New Caledonia Time */
273 {"ndt", DTZ, -9000}, /* Nfld. Daylight Time */
274 {"nft", TZ, -12600}, /* Newfoundland Standard Time */
275 {"nor", TZ, 3600}, /* Norway Standard Time */
277 {"november", MONTH, 11},
278 {"novst", DTZ, 25200}, /* Novosibirsk Summer Time */
279 {"novt", TZ, 21600}, /* Novosibirsk Standard Time */
280 {NOW, RESERV, DTK_NOW}, /* current transaction time */
281 {"npt", TZ, 20700}, /* Nepal Standard Time (GMT-5:45) */
282 {"nst", TZ, -12600}, /* Nfld. Standard Time */
283 {"nt", TZ, -39600}, /* Nome Time */
284 {"nut", TZ, -39600}, /* Niue Time */
285 {"nzdt", DTZ, 46800}, /* New Zealand Daylight Time */
286 {"nzst", TZ, 43200}, /* New Zealand Standard Time */
287 {"nzt", TZ, 43200}, /* New Zealand Time */
289 {"october", MONTH, 10},
290 {"omsst", DTZ, 25200}, /* Omsk Summer Time */
291 {"omst", TZ, 21600}, /* Omsk Time */
292 {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
293 {"pdt", DTZ, -25200}, /* Pacific Daylight Time */
297 {"pet", TZ, -18000}, /* Peru Time */
298 {"petst", DTZ, 46800}, /* Petropavlovsk-Kamchatski Summer Time */
299 {"pett", TZ, 43200}, /* Petropavlovsk-Kamchatski Time */
300 {"pgt", TZ, 36000}, /* Papua New Guinea Time */
301 {"phot", TZ, 46800}, /* Phoenix Islands (Kiribati) Time */
305 {"pht", TZ, 28800}, /* Philippine Time */
306 {"pkt", TZ, 18000}, /* Pakistan Time */
308 {"pmdt", DTZ, -7200}, /* Pierre & Miquelon Daylight Time */
312 {"pont", TZ, 39600}, /* Ponape Time (Micronesia) */
313 {"pst", TZ, -28800}, /* Pacific Standard Time */
314 {"pwt", TZ, 32400}, /* Palau Time */
315 {"pyst", DTZ, -10800}, /* Paraguay Summer Time */
316 {"pyt", TZ, -14400}, /* Paraguay Time */
317 {"ret", DTZ, 14400}, /* Reunion Island Time */
318 {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
319 {"sadt", DTZ, 37800}, /* S. Australian Dayl. Time */
324 {"sast", TZ, 34200}, /* South Australian Std Time */
326 {"saturday", DOW, 6},
330 {"sct", DTZ, 14400}, /* Mahe Island Time */
333 {"september", MONTH, 9},
334 {"set", TZ, -3600}, /* Seychelles Time ?? */
338 {"sst", DTZ, 7200}, /* Swedish Summer Time */
341 {"swt", TZ, 3600}, /* Swedish Winter Time */
345 {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
346 {"tft", TZ, 18000}, /* Kerguelen Time */
347 {"that", TZ, -36000}, /* Tahiti Time */
351 {"thursday", DOW, 4},
352 {"tjt", TZ, 18000}, /* Tajikistan Time */
353 {"tkt", TZ, -36000}, /* Tokelau Time */
354 {"tmt", TZ, 18000}, /* Turkmenistan Time */
355 {TODAY, RESERV, DTK_TODAY}, /* midnight */
356 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
360 {"tot", TZ, 46800}, /* Tonga Time */
364 {"truk", TZ, 36000}, /* Truk Time */
368 {"tvt", TZ, 43200}, /* Tuvalu Time */
372 {"ulast", DTZ, 32400}, /* Ulan Bator Summer Time */
373 {"ulat", TZ, 28800}, /* Ulan Bator Time */
376 {"uyst", DTZ, -7200}, /* Uruguay Summer Time */
377 {"uyt", TZ, -10800}, /* Uruguay Time */
378 {"uzst", DTZ, 21600}, /* Uzbekistan Summer Time */
379 {"uzt", TZ, 18000}, /* Uzbekistan Time */
380 {"vet", TZ, -14400}, /* Venezuela Time */
381 {"vlast", DTZ, 39600}, /* Vladivostok Summer Time */
382 {"vlat", TZ, 36000}, /* Vladivostok Time */
386 {"vut", TZ, 39600}, /* Vanuata Time */
387 {"wadt", DTZ, 28800}, /* West Australian DST */
388 {"wakt", TZ, 43200}, /* Wake Time */
392 {"wast", TZ, 25200}, /* West Australian Std Time */
393 {"wat", TZ, -3600}, /* West Africa Time */
394 {"wdt", DTZ, 32400}, /* West Australian DST */
396 {"wednesday", DOW, 3},
398 {"west", DTZ, 3600}, /* Western Europe Summer Time */
399 {"wet", TZ, 0}, /* Western Europe */
400 {"wetdst", DTZ, 3600}, /* Western Europe Daylight Savings Time */
401 {"wft", TZ, 43200}, /* Wallis and Futuna Time */
402 {"wgst", DTZ, -7200}, /* West Greenland Summer Time */
403 {"wgt", TZ, -10800}, /* West Greenland Time */
404 {"wst", TZ, 28800}, /* West Australian Standard Time */
405 {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
406 {"yakst", DTZ, 36000}, /* Yakutsk Summer Time */
407 {"yakt", TZ, 32400}, /* Yakutsk Time */
408 {"yapt", TZ, 36000}, /* Yap Time (Micronesia) */
409 {"ydt", DTZ, -28800}, /* Yukon Daylight Time */
410 {"yekst", DTZ, 21600}, /* Yekaterinburg Summer Time */
411 {"yekt", TZ, 18000}, /* Yekaterinburg Time */
412 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
413 {"yst", TZ, -32400}, /* Yukon Standard Time */
414 {"z", TZ, 0}, /* time zone tag per ISO-8601 */
415 {"zp4", TZ, -14400}, /* UTC +4 hours. */
416 {"zp5", TZ, -18000}, /* UTC +5 hours. */
417 {"zp6", TZ, -21600}, /* UTC +6 hours. */
418 {ZULU, TZ, 0}, /* UTC */
421 static const datetkn deltatktbl[] = {
422 /* text, token, lexval */
423 {"@", IGNORE_DTF, 0}, /* postgres relative prefix */
424 {DAGO, AGO, 0}, /* "ago" indicates negative time offset */
425 {"c", UNITS, DTK_CENTURY}, /* "century" relative */
426 {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
427 {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
428 {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
429 {"d", UNITS, DTK_DAY}, /* "day" relative */
430 {DDAY, UNITS, DTK_DAY}, /* "day" relative */
431 {"days", UNITS, DTK_DAY}, /* "days" relative */
432 {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
433 {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
434 {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
435 {"decs", UNITS, DTK_DECADE}, /* "decades" relative */
436 {"h", UNITS, DTK_HOUR}, /* "hour" relative */
437 {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
438 {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
439 {"hr", UNITS, DTK_HOUR}, /* "hour" relative */
440 {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
441 {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
442 {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
443 {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
444 {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
445 {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
446 {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
447 {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
448 {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
449 {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
450 {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
451 {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
452 {"mon", UNITS, DTK_MONTH}, /* "months" relative */
453 {"mons", UNITS, DTK_MONTH}, /* "months" relative */
454 {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
455 {"months", UNITS, DTK_MONTH},
456 {"ms", UNITS, DTK_MILLISEC},
457 {"msec", UNITS, DTK_MILLISEC},
458 {DMILLISEC, UNITS, DTK_MILLISEC},
459 {"mseconds", UNITS, DTK_MILLISEC},
460 {"msecs", UNITS, DTK_MILLISEC},
461 {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
462 {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
463 {"s", UNITS, DTK_SECOND},
464 {"sec", UNITS, DTK_SECOND},
465 {DSECOND, UNITS, DTK_SECOND},
466 {"seconds", UNITS, DTK_SECOND},
467 {"secs", UNITS, DTK_SECOND},
468 {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
469 {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
470 {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
471 {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
472 {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
473 {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
474 {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
475 {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
476 {"w", UNITS, DTK_WEEK}, /* "week" relative */
477 {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
478 {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
479 {"y", UNITS, DTK_YEAR}, /* "year" relative */
480 {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
481 {"years", UNITS, DTK_YEAR}, /* "years" relative */
482 {"yr", UNITS, DTK_YEAR}, /* "year" relative */
483 {"yrs", UNITS, DTK_YEAR}, /* "years" relative */
486 static const unsigned int szdatetktbl = lengthof(datetktbl);
487 static const unsigned int szdeltatktbl = lengthof(deltatktbl);
489 static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
491 static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
493 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
495 char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL};
497 char *pgtypes_date_weekdays_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
499 char *pgtypes_date_months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL};
501 static const datetkn *
502 datebsearch(const char *key, const datetkn *base, unsigned int nel)
506 const datetkn *last = base + nel - 1,
512 position = base + ((last - base) >> 1);
513 /* precheck the first character for a bit of extra speed */
514 result = (int) key[0] - (int) position->token[0];
517 /* use strncmp so that we match truncated tokens */
518 result = strncmp(key, position->token, TOKMAXLEN);
532 * Decode text string using lookup table.
533 * This routine supports time interval decoding.
536 DecodeUnits(int field, char *lowtoken, int *val)
541 /* use strncmp so that we match truncated tokens */
542 if (deltacache[field] != NULL &&
543 strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
544 tp = deltacache[field];
546 tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
547 deltacache[field] = tp;
550 type = UNKNOWN_FIELD;
560 } /* DecodeUnits() */
563 * Calendar time to Julian date conversions.
564 * Julian date is commonly used in astronomical applications,
565 * since it is numerically accurate and computationally simple.
566 * The algorithms here will accurately convert between Julian day
567 * and calendar date for all non-negative Julian days
568 * (i.e. from Nov 24, -4713 on).
570 * These routines will be used by other date/time packages
573 * Rewritten to eliminate overflow problems. This now allows the
574 * routines to work correctly for all Julian day counts from
575 * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
576 * a 32-bit integer. Longer types should also work to the limits
577 * of their precision.
581 date2j(int y, int m, int d)
598 julian = y * 365 - 32167;
599 julian += y / 4 - century + century / 4;
600 julian += 7834 * m / 256 + d;
606 j2date(int jd, int *year, int *month, int *day)
615 quad = julian / 146097;
616 extra = (julian - quad * 146097) * 4 + 3;
617 julian += 60 + quad * 3 + extra / 146097;
618 quad = julian / 1461;
619 julian -= quad * 1461;
620 y = julian * 4 / 1461;
621 julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
624 quad = julian * 2141 / 65536;
625 *day = julian - 7834 * quad / 256;
626 *month = (quad + 10) % 12 + 1;
632 * Decode text string using lookup table.
633 * Implement a cache lookup since it is likely that dates
634 * will be related in format.
637 DecodeSpecial(int field, char *lowtoken, int *val)
642 /* use strncmp so that we match truncated tokens */
643 if (datecache[field] != NULL &&
644 strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
645 tp = datecache[field];
650 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
652 datecache[field] = tp;
655 type = UNKNOWN_FIELD;
665 } /* DecodeSpecial() */
668 * Encode date as local time.
671 EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates)
673 Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
678 /* compatible with ISO date formats */
680 sprintf(str, "%04d-%02d-%02d",
681 tm->tm_year, tm->tm_mon, tm->tm_mday);
683 sprintf(str, "%04d-%02d-%02d %s",
684 -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
688 /* compatible with Oracle/Ingres date formats */
690 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
692 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
694 sprintf(str + 5, "/%04d", tm->tm_year);
696 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
699 case USE_GERMAN_DATES:
700 /* German-style date format */
701 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
703 sprintf(str + 5, ".%04d", tm->tm_year);
705 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
708 case USE_POSTGRES_DATES:
710 /* traditional date-only style for Postgres */
712 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
714 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
716 sprintf(str + 5, "-%04d", tm->tm_year);
718 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
724 TrimTrailingZeros(char *str)
726 int len = strlen(str);
728 /* chop off trailing zeros... but leave at least 2 fractional digits */
729 while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
737 * Encode date and time interpreted as local time.
739 * tm and fsec are the value to encode, print_tz determines whether to include
740 * a time zone (the difference between timestamp and timestamptz types), tz is
741 * the numeric time zone offset, tzn is the textual time zone, which if
742 * specified will be used instead of tz by some styles, style is the date
743 * style, str is where to write the output.
745 * Supported date styles:
746 * Postgres - day mon hh:mm:ss yyyy tz
747 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
748 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
749 * German - dd.mm.yyyy hh:mm:ss tz
750 * Variants (affects order of month and day for Postgres and SQL styles):
752 * European - dd/mm/yyyy
755 EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
762 * Negative tm_isdst means we have no valid time zone translation.
764 if (tm->tm_isdst < 0)
770 /* Compatible with ISO-8601 date formats */
772 sprintf(str, "%04d-%02d-%02d %02d:%02d",
773 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
774 tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
777 * Print fractional seconds if any. The field widths here should
778 * be at least equal to MAX_TIMESTAMP_PRECISION.
782 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
783 TrimTrailingZeros(str);
786 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
788 if (tm->tm_year <= 0)
789 sprintf(str + strlen(str), " BC");
793 hour = -(tz / SECS_PER_HOUR);
794 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
796 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
798 sprintf(str + strlen(str), "%+03d", hour);
803 /* Compatible with Oracle/Ingres date formats */
806 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
808 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
810 sprintf(str + 5, "/%04d %02d:%02d",
811 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
812 tm->tm_hour, tm->tm_min);
815 * Print fractional seconds if any. The field widths here should
816 * be at least equal to MAX_TIMESTAMP_PRECISION.
820 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
821 TrimTrailingZeros(str);
824 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
826 if (tm->tm_year <= 0)
827 sprintf(str + strlen(str), " BC");
830 * Note: the uses of %.*s in this function would be risky if the
831 * timezone names ever contain non-ASCII characters. However, all
832 * TZ abbreviations in the IANA database are plain ASCII.
838 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
841 hour = -(tz / SECS_PER_HOUR);
842 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
844 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
846 sprintf(str + strlen(str), "%+03d", hour);
851 case USE_GERMAN_DATES:
852 /* German variant on European style */
854 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
856 sprintf(str + 5, ".%04d %02d:%02d",
857 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
858 tm->tm_hour, tm->tm_min);
861 * Print fractional seconds if any. The field widths here should
862 * be at least equal to MAX_TIMESTAMP_PRECISION.
866 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
867 TrimTrailingZeros(str);
870 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
872 if (tm->tm_year <= 0)
873 sprintf(str + strlen(str), " BC");
878 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
881 hour = -(tz / SECS_PER_HOUR);
882 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
884 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
886 sprintf(str + strlen(str), "%+03d", hour);
891 case USE_POSTGRES_DATES:
893 /* Backward-compatible with traditional Postgres abstime dates */
895 day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
896 tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
898 memcpy(str, days[tm->tm_wday], 3);
899 strcpy(str + 3, " ");
902 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
904 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
906 sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
909 * Print fractional seconds if any. The field widths here should
910 * be at least equal to MAX_TIMESTAMP_PRECISION.
914 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
915 TrimTrailingZeros(str);
918 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
920 sprintf(str + strlen(str), " %04d",
921 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
922 if (tm->tm_year <= 0)
923 sprintf(str + strlen(str), " BC");
928 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
932 * We have a time zone, but no string version. Use the
933 * numeric form, but be sure to include a leading space to
934 * avoid formatting something which would be rejected by
935 * the date/time parser later. - thomas 2001-10-19
937 hour = -(tz / SECS_PER_HOUR);
938 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
940 sprintf(str + strlen(str), " %+03d:%02d", hour, min);
942 sprintf(str + strlen(str), " %+03d", hour);
950 GetEpochTime(struct tm *tm)
959 tm->tm_year = t0->tm_year + 1900;
960 tm->tm_mon = t0->tm_mon + 1;
961 tm->tm_mday = t0->tm_mday;
962 tm->tm_hour = t0->tm_hour;
963 tm->tm_min = t0->tm_min;
964 tm->tm_sec = t0->tm_sec;
970 } /* GetEpochTime() */
973 abstime2tm(AbsoluteTime _time, int *tzp, struct tm *tm, char **tzn)
975 time_t time = (time_t) _time;
980 tx = localtime((time_t *) &time);
982 tx = gmtime((time_t *) &time);
986 errno = PGTYPES_TS_BAD_TIMESTAMP;
990 tm->tm_year = tx->tm_year + 1900;
991 tm->tm_mon = tx->tm_mon + 1;
992 tm->tm_mday = tx->tm_mday;
993 tm->tm_hour = tx->tm_hour;
994 tm->tm_min = tx->tm_min;
995 tm->tm_sec = tx->tm_sec;
996 tm->tm_isdst = tx->tm_isdst;
998 #if defined(HAVE_STRUCT_TM_TM_ZONE)
999 tm->tm_gmtoff = tx->tm_gmtoff;
1000 tm->tm_zone = tx->tm_zone;
1005 * We have a brute force time zone per SQL99? Then use it without
1006 * change since we have already rotated to the time zone.
1008 *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
1011 * FreeBSD man pages indicate that this should work - tgl 97/04/23
1016 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1017 * contains an error message, which doesn't fit in the buffer
1019 StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
1020 if (strlen(tm->tm_zone) > MAXTZLEN)
1026 #elif defined(HAVE_INT_TIMEZONE)
1029 *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
1034 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1035 * contains an error message, which doesn't fit in the buffer
1037 StrNCpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
1038 if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
1044 #else /* not (HAVE_STRUCT_TM_TM_ZONE || HAVE_INT_TIMEZONE) */
1047 /* default to UTC */
1058 GetCurrentDateTime(struct tm *tm)
1062 abstime2tm(time(NULL), &tz, tm, NULL);
1066 dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
1071 *hour = time / USECS_PER_HOUR;
1072 time -= (*hour) * USECS_PER_HOUR;
1073 *min = time / USECS_PER_MINUTE;
1074 time -= (*min) * USECS_PER_MINUTE;
1075 *sec = time / USECS_PER_SEC;
1076 *fsec = time - (*sec * USECS_PER_SEC);
1081 /* DecodeNumberField()
1082 * Interpret numeric string as a concatenated date or time field.
1083 * Use the context of previously decoded fields to help with
1084 * the interpretation.
1087 DecodeNumberField(int len, char *str, int fmask,
1088 int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits)
1093 * Have a decimal point? Then this is a date or something with a seconds
1096 if ((cp = strchr(str, '.')) != NULL)
1104 * OK, we have at most six digits to care about. Let's construct a
1105 * string with those digits, zero-padded on the right, and then do the
1106 * conversion to an integer.
1108 * XXX This truncates the seventh digit, unlike rounding it as the
1111 for (i = 0; i < 6; i++)
1112 fstr[i] = *cp != '\0' ? *cp++ : '0';
1114 *fsec = strtoint(fstr, NULL, 10);
1118 /* No decimal point and no complete date yet? */
1119 else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1124 *tmask = DTK_DATE_M;
1126 tm->tm_mday = atoi(str + 6);
1128 tm->tm_mon = atoi(str + 4);
1130 tm->tm_year = atoi(str + 0);
1137 *tmask = DTK_DATE_M;
1138 tm->tm_mday = atoi(str + 4);
1140 tm->tm_mon = atoi(str + 2);
1142 tm->tm_year = atoi(str + 0);
1150 *tmask = DTK_DATE_M;
1151 tm->tm_mday = atoi(str + 2);
1154 tm->tm_year = atoi(str + 0);
1161 /* not all time fields are specified? */
1162 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
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);
1179 *tmask = DTK_TIME_M;
1181 tm->tm_min = atoi(str + 2);
1183 tm->tm_hour = atoi(str + 0);
1190 } /* DecodeNumberField() */
1194 * Interpret plain numeric field as a date value in context.
1197 DecodeNumber(int flen, char *str, int fmask,
1198 int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits, bool EuroDates)
1205 val = strtoint(str, &cp, 10);
1212 * More than two digits? Then could be a date or a run-together time:
1213 * 2001.360 20011225 040506.789
1216 return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
1217 tmask, tm, fsec, is2digits);
1219 *fsec = strtod(cp, &cp);
1223 else if (*cp != '\0')
1226 /* Special case day of year? */
1227 if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
1229 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1231 j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
1232 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1236 * Enough digits to be unequivocal year? Used to test for 4 digits or
1237 * more, but we now test first for a three-digit doy so anything
1238 * bigger than two digits had better be an explicit year.
1239 * - thomas 1999-01-09
1240 * Back to requiring a 4 digit year. We accept a two digit
1241 * year farther down. - thomas 2000-03-28
1245 *tmask = DTK_M(YEAR);
1247 /* already have a year? then see if we can substitute... */
1248 if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1249 tm->tm_year >= 1 && tm->tm_year <= 31)
1251 tm->tm_mday = tm->tm_year;
1252 *tmask = DTK_M(DAY);
1258 /* already have year? then could be month */
1259 else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1261 *tmask = DTK_M(MONTH);
1264 /* no year and EuroDates enabled? then could be day */
1265 else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
1266 !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1267 val >= 1 && val <= 31)
1269 *tmask = DTK_M(DAY);
1272 else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1274 *tmask = DTK_M(MONTH);
1277 else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
1279 *tmask = DTK_M(DAY);
1284 * Check for 2 or 4 or more digits, but currently we reach here only if
1285 * two digits. - thomas 2000-03-28
1287 else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
1289 *tmask = DTK_M(YEAR);
1292 /* adjust ONLY if exactly two digits... */
1293 *is2digits = (flen == 2);
1299 } /* DecodeNumber() */
1302 * Decode date string which includes delimiters.
1303 * Insist on a complete set of fields.
1306 DecodeDate(char *str, int fmask, int *tmask, struct tm *tm, bool EuroDates)
1314 bool is2digits = false;
1318 char *field[MAXDATEFIELDS];
1320 /* parse this string... */
1321 while (*str != '\0' && nf < MAXDATEFIELDS)
1323 /* skip field separators */
1324 while (!isalnum((unsigned char) *str))
1328 if (isdigit((unsigned char) *str))
1330 while (isdigit((unsigned char) *str))
1333 else if (isalpha((unsigned char) *str))
1335 while (isalpha((unsigned char) *str))
1339 /* Just get rid of any non-digit, non-alpha characters... */
1346 /* don't allow too many fields */
1353 /* look first for text fields, since that will be unambiguous month */
1354 for (i = 0; i < nf; i++)
1356 if (isalpha((unsigned char) *field[i]))
1358 type = DecodeSpecial(i, field[i], &val);
1359 if (type == IGNORE_DTF)
1362 dmask = DTK_M(type);
1382 /* mark this field as being completed */
1387 /* now pick up remaining numeric fields */
1388 for (i = 0; i < nf; i++)
1390 if (field[i] == NULL)
1393 if ((len = strlen(field[i])) <= 0)
1396 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
1406 if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
1409 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1412 if (tm->tm_year > 0)
1413 tm->tm_year = -(tm->tm_year - 1);
1419 if (tm->tm_year < 70)
1420 tm->tm_year += 2000;
1421 else if (tm->tm_year < 100)
1422 tm->tm_year += 1900;
1426 } /* DecodeDate() */
1430 * Decode time string which includes delimiters.
1431 * Only check the lower limit on hours, since this same code
1432 * can be used to represent time spans.
1435 DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec)
1439 *tmask = DTK_TIME_M;
1441 tm->tm_hour = strtoint(str, &cp, 10);
1445 tm->tm_min = strtoint(str, &cp, 10);
1451 else if (*cp != ':')
1456 tm->tm_sec = strtoint(str, &cp, 10);
1459 else if (*cp == '.')
1467 * OK, we have at most six digits to care about. Let's construct a
1468 * string with those digits, zero-padded on the right, and then do
1469 * the conversion to an integer.
1471 * XXX This truncates the seventh digit, unlike rounding it as the
1474 for (i = 0; i < 6; i++)
1475 fstr[i] = *cp != '\0' ? *cp++ : '0';
1477 *fsec = strtoint(fstr, &cp, 10);
1485 /* do a sanity check */
1486 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
1487 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
1491 } /* DecodeTime() */
1494 * Interpret string as a numeric timezone.
1496 * Note: we allow timezone offsets up to 13:59. There are places that
1497 * use +1300 summer time.
1500 DecodeTimezone(char *str, int *tzp)
1508 /* assume leading character is "+" or "-" */
1509 hr = strtoint(str + 1, &cp, 10);
1511 /* explicit delimiter? */
1513 min = strtoint(cp + 1, &cp, 10);
1514 /* otherwise, might have run things together... */
1515 else if (*cp == '\0' && (len = strlen(str)) > 3)
1517 min = strtoint(str + len - 2, &cp, 10);
1518 if (min < 0 || min >= 60)
1521 *(str + len - 2) = '\0';
1522 hr = strtoint(str + 1, &cp, 10);
1523 if (hr < 0 || hr > 13)
1529 tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
1535 } /* DecodeTimezone() */
1538 /* DecodePosixTimezone()
1539 * Interpret string as a POSIX-compatible timezone:
1542 * - thomas 2000-03-15
1545 DecodePosixTimezone(char *str, int *tzp)
1554 while (*cp != '\0' && isalpha((unsigned char) *cp))
1557 if (DecodeTimezone(cp, &tz) != 0)
1562 type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
1577 } /* DecodePosixTimezone() */
1580 * Break string into tokens based on a date/time context.
1581 * Several field types are assigned:
1582 * DTK_NUMBER - digits and (possibly) a decimal point
1583 * DTK_DATE - digits and two delimiters, or digits and text
1584 * DTK_TIME - digits, colon delimiters, and possibly a decimal point
1585 * DTK_STRING - text (no digits)
1586 * DTK_SPECIAL - leading "+" or "-" followed by text
1587 * DTK_TZ - leading "+" or "-" followed by digits
1588 * Note that some field types can hold unexpected items:
1589 * DTK_NUMBER can hold date fields (yy.ddd)
1590 * DTK_STRING can hold months (January) and time zones (PST)
1591 * DTK_DATE can hold Posix time zones (GMT-8)
1593 * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
1594 * bytes of space. On output, field[] entries will point into it.
1595 * The field[] and ftype[] arrays must have at least MAXDATEFIELDS entries.
1598 ParseDateTime(char *timestr, char *lowstr,
1599 char **field, int *ftype, int *numfields, char **endstr)
1605 /* outer loop through fields */
1606 while (*(*endstr) != '\0')
1608 /* Record start of current field */
1609 if (nf >= MAXDATEFIELDS)
1613 /* leading digit? then date or time */
1614 if (isdigit((unsigned char) *(*endstr)))
1616 *lp++ = *(*endstr)++;
1617 while (isdigit((unsigned char) *(*endstr)))
1618 *lp++ = *(*endstr)++;
1621 if (*(*endstr) == ':')
1623 ftype[nf] = DTK_TIME;
1624 *lp++ = *(*endstr)++;
1625 while (isdigit((unsigned char) *(*endstr)) ||
1626 (*(*endstr) == ':') || (*(*endstr) == '.'))
1627 *lp++ = *(*endstr)++;
1629 /* date field? allow embedded text month */
1630 else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1632 /* save delimiting character to use later */
1633 char *dp = (*endstr);
1635 *lp++ = *(*endstr)++;
1636 /* second field is all digits? then no embedded text month */
1637 if (isdigit((unsigned char) *(*endstr)))
1639 ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
1640 while (isdigit((unsigned char) *(*endstr)))
1641 *lp++ = *(*endstr)++;
1644 * insist that the delimiters match to get a three-field
1647 if (*(*endstr) == *dp)
1649 ftype[nf] = DTK_DATE;
1650 *lp++ = *(*endstr)++;
1651 while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1652 *lp++ = *(*endstr)++;
1657 ftype[nf] = DTK_DATE;
1658 while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1659 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1664 * otherwise, number only and will determine year, month, day, or
1665 * concatenated fields later...
1668 ftype[nf] = DTK_NUMBER;
1670 /* Leading decimal point? Then fractional seconds... */
1671 else if (*(*endstr) == '.')
1673 *lp++ = *(*endstr)++;
1674 while (isdigit((unsigned char) *(*endstr)))
1675 *lp++ = *(*endstr)++;
1677 ftype[nf] = DTK_NUMBER;
1681 * text? then date string, month, day of week, special, or timezone
1683 else if (isalpha((unsigned char) *(*endstr)))
1685 ftype[nf] = DTK_STRING;
1686 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1687 while (isalpha((unsigned char) *(*endstr)))
1688 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1691 * Full date string with leading text month? Could also be a POSIX
1694 if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1696 char *dp = (*endstr);
1698 ftype[nf] = DTK_DATE;
1699 *lp++ = *(*endstr)++;
1700 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
1701 *lp++ = *(*endstr)++;
1704 /* skip leading spaces */
1705 else if (isspace((unsigned char) *(*endstr)))
1710 /* sign? then special or numeric timezone */
1711 else if (*(*endstr) == '+' || *(*endstr) == '-')
1713 *lp++ = *(*endstr)++;
1714 /* soak up leading whitespace */
1715 while (isspace((unsigned char) *(*endstr)))
1717 /* numeric timezone? */
1718 if (isdigit((unsigned char) *(*endstr)))
1721 *lp++ = *(*endstr)++;
1722 while (isdigit((unsigned char) *(*endstr)) ||
1723 (*(*endstr) == ':') || (*(*endstr) == '.'))
1724 *lp++ = *(*endstr)++;
1727 else if (isalpha((unsigned char) *(*endstr)))
1729 ftype[nf] = DTK_SPECIAL;
1730 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1731 while (isalpha((unsigned char) *(*endstr)))
1732 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1734 /* otherwise something wrong... */
1738 /* ignore punctuation but use as delimiter */
1739 else if (ispunct((unsigned char) *(*endstr)))
1745 /* otherwise, something is not right... */
1749 /* force in a delimiter after each field */
1757 } /* ParseDateTime() */
1761 * Interpret previously parsed fields for general date and time.
1762 * Return 0 if full date, 1 if only time, and -1 if problems.
1763 * External format(s):
1764 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
1765 * "Fri Feb-7-1997 15:23:27"
1766 * "Feb-7-1997 15:23:27"
1767 * "2-7-1997 15:23:27"
1768 * "1997-2-7 15:23:27"
1769 * "1997.038 15:23:27" (day of year 1-366)
1770 * Also supports input in compact time:
1773 * "20011225T040506.789-07"
1775 * Use the system-provided functions to get the current time zone
1776 * if not specified in the input string.
1777 * If the date is outside the time_t system-supported time range,
1778 * then assume UTC time zone. - thomas 1997-05-27
1781 DecodeDateTime(char **field, int *ftype, int nf,
1782 int *dtype, struct tm *tm, fsec_t *fsec, bool EuroDates)
1787 int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */
1791 bool haveTextMonth = false;
1792 bool is2digits = false;
1798 * We'll insist on at least all of the date fields, but initialize the
1799 * remaining fields in case they are not set later...
1806 /* don't know daylight savings time status apriori */
1811 for (i = 0; i < nf; i++)
1817 * Integral julian day with attached time zone?
1818 * All other forms with JD will be separated into
1819 * distinct fields, so we handle just this case here.
1821 if (ptype == DTK_JULIAN)
1829 val = strtoint(field[i], &cp, 10);
1833 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1834 /* Get the time zone from the end of the string */
1835 if (DecodeTimezone(cp, tzp) != 0)
1838 tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1843 * Already have a date? Then this might be a POSIX time
1844 * zone with an embedded dash (e.g. "PST-3" == "EST") or
1845 * a run-together time with trailing time zone (e.g. hhmmss-zz).
1846 * - thomas 2001-12-25
1848 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
1851 /* No time zone accepted? Then quit... */
1855 if (isdigit((unsigned char) *field[i]) || ptype != 0)
1861 /* Sanity check; should not fail this test */
1862 if (ptype != DTK_TIME)
1868 * Starts with a digit but we already have a time
1869 * field? Then we are in trouble with a date and time
1872 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1875 if ((cp = strchr(field[i], '-')) == NULL)
1878 /* Get the time zone from the end of the string */
1879 if (DecodeTimezone(cp, tzp) != 0)
1884 * Then read the rest of the field as a concatenated
1887 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
1888 &tmask, tm, fsec, &is2digits)) < 0)
1892 * modify tmask after returning from
1893 * DecodeNumberField()
1899 if (DecodePosixTimezone(field[i], tzp) != 0)
1906 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
1911 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
1915 * Check upper limit on hours; other limits checked in
1918 /* test for > 24:00:00 */
1919 if (tm->tm_hour > 24 ||
1920 (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
1931 if (DecodeTimezone(field[i], &tz) != 0)
1935 * Already have a time zone? Then maybe this is the second
1936 * field of a POSIX time: EST+3 (equivalent to PST)
1938 if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
1939 ftype[i - 1] == DTK_TZ &&
1940 isalpha((unsigned char) *field[i - 1]))
1956 * Was this an "ISO date" with embedded field labels? An
1957 * example is "y2001m02d04" - thomas 2001-02-04
1964 val = strtoint(field[i], &cp, 10);
1967 * only a few kinds are allowed to have an embedded
1981 else if (*cp != '\0')
1988 tmask = DTK_M(YEAR);
1994 * already have a month and hour? then assume
1997 if ((fmask & DTK_M(MONTH)) != 0 &&
1998 (fmask & DTK_M(HOUR)) != 0)
2001 tmask = DTK_M(MINUTE);
2006 tmask = DTK_M(MONTH);
2017 tmask = DTK_M(HOUR);
2022 tmask = DTK_M(MINUTE);
2027 tmask = DTK_M(SECOND);
2032 frac = strtod(cp, &cp);
2035 *fsec = frac * 1000000;
2041 if (DecodeTimezone(field[i], tzp) != 0)
2047 * previous field was a label for "julian date"?
2050 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2051 /* fractional Julian Day? */
2056 time = strtod(cp, &cp);
2060 tmask |= DTK_TIME_M;
2061 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
2066 /* previous field was "t" for ISO time */
2067 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
2068 &tmask, tm, fsec, &is2digits)) < 0)
2071 if (tmask != DTK_TIME_M)
2088 flen = strlen(field[i]);
2089 cp = strchr(field[i], '.');
2091 /* Embedded decimal and no date yet? */
2092 if (cp != NULL && !(fmask & DTK_DATE_M))
2094 if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
2097 /* embedded decimal and several digits before? */
2098 else if (cp != NULL && flen - strlen(cp) > 2)
2101 * Interpret as a concatenated date or time Set the
2102 * type field to allow decoding other fields later.
2103 * Example: 20011223 or 040506
2105 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2106 &tmask, tm, fsec, &is2digits)) < 0)
2111 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2112 &tmask, tm, fsec, &is2digits)) < 0)
2115 /* otherwise it is a single date/time field... */
2116 else if (DecodeNumber(flen, field[i], fmask,
2117 &tmask, tm, fsec, &is2digits, EuroDates) != 0)
2124 type = DecodeSpecial(i, field[i], &val);
2125 if (type == IGNORE_DTF)
2128 tmask = DTK_M(type);
2135 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
2137 GetCurrentDateTime(tm);
2143 GetCurrentDateTime(tm);
2144 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
2145 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2154 GetCurrentDateTime(tm);
2163 GetCurrentDateTime(tm);
2164 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
2165 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2172 tmask = (DTK_TIME_M | DTK_M(TZ));
2190 * already have a (numeric) month? then see if we can
2193 if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
2194 !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
2196 tm->tm_mday = tm->tm_mon;
2199 haveTextMonth = true;
2206 * daylight savings time modifier (solves "MET DST"
2209 tmask |= DTK_M(DTZ);
2219 * set mask for TZ here _or_ check for DTZ later when
2220 * getting default timezone
2261 * This is a filler field "t" indicating that the next
2262 * field is time. Try to verify that this is sensible.
2266 /* No preceding date? Then quit... */
2267 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2271 * We will need one of the following fields:
2272 * DTK_NUMBER should be hhmmss.fff
2273 * DTK_TIME should be hh:mm:ss.fff
2274 * DTK_DATE should be hhmmss-zz
2277 (ftype[i + 1] != DTK_NUMBER &&
2278 ftype[i + 1] != DTK_TIME &&
2279 ftype[i + 1] != DTK_DATE))
2299 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2302 if (tm->tm_year > 0)
2303 tm->tm_year = -(tm->tm_year - 1);
2309 if (tm->tm_year < 70)
2310 tm->tm_year += 2000;
2311 else if (tm->tm_year < 100)
2312 tm->tm_year += 1900;
2315 if (mer != HR24 && tm->tm_hour > 12)
2317 if (mer == AM && tm->tm_hour == 12)
2319 else if (mer == PM && tm->tm_hour != 12)
2322 /* do additional checking for full date specs... */
2323 if (*dtype == DTK_DATE)
2325 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2326 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
2329 * check for valid day of month, now that we know for sure the month
2332 if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2336 * backend tried to find local timezone here but we don't use the
2337 * result afterwards anyway so we only check for this error: daylight
2338 * savings time modifier but no standard timezone?
2340 if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
2345 } /* DecodeDateTime() */
2347 /* Function works as follows:
2353 find_end_token(char *str, char *fmt)
2356 * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
2358 * we extract the 28, we read the percent sign and the type "d" then this
2359 * functions gets called as find_end_token("28the day12the hour", "the
2362 * fmt points to "the day%hthehour", next_percent points to %hthehour and
2363 * we have to find a match for everything between these positions ("the
2364 * day"). We look for "the day" in str and know that the pattern we are
2365 * about to scan ends where this string starts (right after the "28")
2367 * At the end, *fmt is '\0' and *str isn't. end_position then is
2370 char *end_position = NULL;
2372 *subst_location = NULL;
2373 int scan_offset = 0;
2376 /* are we at the end? */
2380 return end_position;
2383 /* not at the end */
2384 while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
2387 * there is no delimiter, skip to the next delimiter if we're reading
2388 * a number and then something that is not a number "9:15pm", we might
2389 * be able to recover with the strtol end pointer. Go for the next
2394 next_percent = strchr(fmt + scan_offset, '%');
2398 * we don't want to allocate extra memory, so we temporarily set the
2399 * '%' sign to '\0' and call strstr However since we allow whitespace
2400 * to float around everything, we have to shorten the pattern until we
2401 * reach a non-whitespace character
2404 subst_location = next_percent;
2405 while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
2407 last_char = *subst_location;
2408 *subst_location = '\0';
2411 * the haystack is the str and the needle is the original fmt but it
2412 * ends at the position where the next percent sign would be
2416 * There is one special case. Imagine: str = " 2", fmt = "%d %...",
2417 * since we want to allow blanks as "dynamic" padding we have to
2418 * accept this. Now, we are called with a fmt of " %..." and look for
2419 * " " in str. We find it at the first position and never read the
2424 end_position = strstr(str, fmt + scan_offset);
2425 *subst_location = last_char;
2430 * there is no other percent sign. So everything up to the end has to
2433 end_position = str + strlen(str);
2438 * maybe we have the following case:
2440 * str = "4:15am" fmt = "%M:%S %p"
2442 * at this place we could have
2444 * str = "15am" fmt = " %p"
2446 * and have set fmt to " " because overwrote the % sign with a NULL
2448 * In this case where we would have to match a space but can't find
2449 * it, set end_position to the end of the string
2451 if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
2452 end_position = str + strlen(str);
2454 return end_position;
2458 pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char *pfmt)
2461 * scan everything between pstr and pstr_end. This is not including the
2462 * last character so we might set it to '\0' for the parsing
2468 char *strtol_end = NULL;
2470 while (**pstr == ' ')
2472 pstr_end = find_end_token(*pstr, pfmt);
2475 /* there was an error, no match */
2478 last_char = *pstr_end;
2483 case PGTYPES_TYPE_UINT:
2486 * numbers may be blank-padded, this is the only deviation from
2487 * the fmt-string we accept
2489 while (**pstr == ' ')
2492 scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
2496 case PGTYPES_TYPE_UINT_LONG:
2497 while (**pstr == ' ')
2500 scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
2504 case PGTYPES_TYPE_STRING_MALLOCED:
2505 scan_val->str_val = pgtypes_strdup(*pstr);
2506 if (scan_val->str_val == NULL)
2510 if (strtol_end && *strtol_end)
2514 *pstr_end = last_char;
2518 /* XXX range checking */
2520 PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
2521 int *year, int *month, int *day,
2522 int *hour, int *minute, int *second,
2525 union un_fmt_comb scan_val;
2541 while (*pfmt == ' ')
2543 while (*pstr == ' ')
2554 /* Error: no match */
2560 /* here *pfmt equals '%' */
2568 * we parse the day and see if it is a week day but we do not
2569 * check if the week day really matches the date
2573 while (pgtypes_date_weekdays_short[j])
2575 if (strncmp(pgtypes_date_weekdays_short[j], pstr,
2576 strlen(pgtypes_date_weekdays_short[j])) == 0)
2580 pstr += strlen(pgtypes_date_weekdays_short[j]);
2587 /* see note above */
2593 if (strncmp(days[j], pstr, strlen(days[j])) == 0)
2597 pstr += strlen(days[j]);
2610 if (strncmp(months[j], pstr, strlen(months[j])) == 0)
2614 pstr += strlen(months[j]);
2622 /* see note above */
2626 while (pgtypes_date_months[j])
2628 if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
2632 pstr += strlen(pgtypes_date_months[j]);
2644 scan_type = PGTYPES_TYPE_UINT;
2645 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2646 *year = scan_val.uint_val * 100;
2651 scan_type = PGTYPES_TYPE_UINT;
2652 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2653 *day = scan_val.uint_val;
2658 * we have to concatenate the strings in order to be able to
2659 * find the end of the substitution
2662 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
2663 strcpy(tmp, "%m/%d/%y");
2665 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2670 scan_type = PGTYPES_TYPE_UINT;
2671 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2672 *month = scan_val.uint_val;
2675 case 'g': /* XXX difference to y (ISO) */
2677 scan_type = PGTYPES_TYPE_UINT;
2678 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2682 *year = scan_val.uint_val;
2685 *year += scan_val.uint_val;
2690 /* XXX difference to %V (ISO) */
2692 scan_type = PGTYPES_TYPE_UINT;
2693 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2694 *year = scan_val.uint_val;
2701 scan_type = PGTYPES_TYPE_UINT;
2702 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2703 *hour += scan_val.uint_val;
2707 scan_type = PGTYPES_TYPE_UINT;
2708 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2711 * XXX what should we do with that? We could say that it's
2712 * sufficient if we have the year and the day within the year
2713 * to get at least a specific day.
2718 scan_type = PGTYPES_TYPE_UINT;
2719 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2720 *minute = scan_val.uint_val;
2732 if (strncmp(pstr, "am", 2) == 0)
2738 if (strncmp(pstr, "a.m.", 4) == 0)
2744 if (strncmp(pstr, "pm", 2) == 0)
2750 if (strncmp(pstr, "p.m.", 4) == 0)
2760 if (strncmp(pstr, "AM", 2) == 0)
2766 if (strncmp(pstr, "A.M.", 4) == 0)
2772 if (strncmp(pstr, "PM", 2) == 0)
2778 if (strncmp(pstr, "P.M.", 4) == 0)
2787 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
2788 strcpy(tmp, "%I:%M:%S %p");
2790 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2795 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
2796 strcpy(tmp, "%H:%M");
2798 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2803 scan_type = PGTYPES_TYPE_UINT_LONG;
2804 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2805 /* number of seconds in scan_val.luint_val */
2808 time_t et = (time_t) scan_val.luint_val;
2814 *year = tms->tm_year + 1900;
2815 *month = tms->tm_mon + 1;
2816 *day = tms->tm_mday;
2817 *hour = tms->tm_hour;
2818 *minute = tms->tm_min;
2819 *second = tms->tm_sec;
2827 scan_type = PGTYPES_TYPE_UINT;
2828 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2829 *second = scan_val.uint_val;
2840 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
2841 strcpy(tmp, "%H:%M:%S");
2843 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2848 scan_type = PGTYPES_TYPE_UINT;
2849 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2850 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
2855 scan_type = PGTYPES_TYPE_UINT;
2856 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2857 if (scan_val.uint_val > 53)
2862 scan_type = PGTYPES_TYPE_UINT;
2863 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2864 if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
2869 scan_type = PGTYPES_TYPE_UINT;
2870 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2871 if (scan_val.uint_val > 6)
2876 scan_type = PGTYPES_TYPE_UINT;
2877 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2878 if (scan_val.uint_val > 53)
2887 scan_type = PGTYPES_TYPE_UINT;
2888 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2889 *year = scan_val.uint_val;
2893 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2894 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2897 err = DecodeTimezone(scan_val.str_val, tz);
2898 free(scan_val.str_val);
2903 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2904 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2908 * XXX use DecodeSpecial instead? Do we need strcasecmp
2912 for (j = 0; j < szdatetktbl; j++)
2914 if ((datetktbl[j].type == TZ || datetktbl[j].type == DTZ) &&
2915 pg_strcasecmp(datetktbl[j].token,
2916 scan_val.str_val) == 0)
2918 *tz = -datetktbl[j].value;
2923 free(scan_val.str_val);
2974 if (*hour > 24 || /* test for > 24:00:00 */
2975 (*hour == 24 && (*minute > 0 || *second > 0)))
2980 if (*month > MONTHS_PER_YEAR)
2985 if (*day > day_tab[isleap(*year)][*month - 1])
2987 *day = day_tab[isleap(*year)][*month - 1];
2991 tm.tm_sec = *second;
2992 tm.tm_min = *minute;
2998 tm2timestamp(&tm, 0, tz, d);
3003 /* XXX: 1900 is compiled in as the base for years */