1
/*-------------------------------------------------------------------------
4
* Support functions for date/time types.
6
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.137.4.1 2005-04-20 17:14:58 tgl Exp $
13
*-------------------------------------------------------------------------
23
#include "miscadmin.h"
24
#include "utils/datetime.h"
25
#include "utils/guc.h"
28
static int DecodeNumber(int flen, char *field, bool haveTextMonth,
29
int fmask, int *tmask,
30
struct pg_tm * tm, fsec_t *fsec, int *is2digits);
31
static int DecodeNumberField(int len, char *str,
32
int fmask, int *tmask,
33
struct pg_tm * tm, fsec_t *fsec, int *is2digits);
34
static int DecodeTime(char *str, int fmask, int *tmask,
35
struct pg_tm * tm, fsec_t *fsec);
36
static int DecodeTimezone(char *str, int *tzp);
37
static int DecodePosixTimezone(char *str, int *tzp);
38
static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
39
static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm);
40
static void TrimTrailingZeros(char *str);
43
const int day_tab[2][13] =
45
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
46
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
49
char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
50
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
52
char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
53
"Thursday", "Friday", "Saturday", NULL};
56
/*****************************************************************************
58
*****************************************************************************/
61
* Definitions for squeezing values into "value"
62
* We set aside a high bit for a sign, and scale the timezone offsets
63
* in minutes by a factor of 15 (so can represent quarter-hour increments).
65
#define ABS_SIGNBIT ((char) 0200)
66
#define VALMASK ((char) 0177)
68
#define NEG(n) ((n)|ABS_SIGNBIT)
69
#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
70
#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
71
#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
74
* datetktbl holds date/time keywords.
76
* Note that this table must be strictly alphabetically ordered to allow an
77
* O(ln(N)) search algorithm to be used.
79
* The text field is NOT guaranteed to be NULL-terminated.
81
* To keep this table reasonably small, we divide the lexval for TZ and DTZ
82
* entries by 15 (so they are on 15 minute boundaries) and truncate the text
83
* field at TOKMAXLEN characters.
84
* Formerly, we divided by 10 rather than 15 but there are a few time zones
85
* which are 30 or 45 minutes away from an even hour, most are on an hour
86
* boundary, and none on other boundaries.
88
* Let's include all strings from my current zic time zone database.
89
* Not all of them are unique, or even very understandable, so we will
90
* leave some commented out for now.
92
static datetkn datetktbl[] = {
93
/* text, token, lexval */
94
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
95
{"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
96
{"acsst", DTZ, POS(42)}, /* Cent. Australia */
97
{"acst", DTZ, NEG(16)}, /* Atlantic/Porto Acre Summer Time */
98
{"act", TZ, NEG(20)}, /* Atlantic/Porto Acre Time */
99
{DA_D, ADBC, AD}, /* "ad" for years > 0 */
100
{"adt", DTZ, NEG(12)}, /* Atlantic Daylight Time */
101
{"aesst", DTZ, POS(44)}, /* E. Australia */
102
{"aest", TZ, POS(40)}, /* Australia Eastern Std Time */
103
{"aft", TZ, POS(18)}, /* Kabul */
104
{"ahst", TZ, NEG(40)}, /* Alaska-Hawaii Std Time */
105
{"akdt", DTZ, NEG(32)}, /* Alaska Daylight Time */
106
{"akst", DTZ, NEG(36)}, /* Alaska Standard Time */
107
{"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
108
{"almst", TZ, POS(28)}, /* Almaty Savings Time */
109
{"almt", TZ, POS(24)}, /* Almaty Time */
111
{"amst", DTZ, POS(20)}, /* Armenia Summer Time (Yerevan) */
113
{"amst", DTZ, NEG(12)}, /* Amazon Summer Time (Porto Velho) */
115
{"amt", TZ, POS(16)}, /* Armenia Time (Yerevan) */
117
{"amt", TZ, NEG(16)}, /* Amazon Time (Porto Velho) */
119
{"anast", DTZ, POS(52)}, /* Anadyr Summer Time (Russia) */
120
{"anat", TZ, POS(48)}, /* Anadyr Time (Russia) */
128
{"art", TZ, NEG(12)}, /* Argentina Time */
131
ast /* Atlantic Standard Time, Arabia Standard
132
* Time, Acre Standard Time */
134
{"ast", TZ, NEG(16)}, /* Atlantic Std Time (Canada) */
135
{"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
137
{"august", MONTH, 8},
138
{"awsst", DTZ, POS(36)}, /* W. Australia */
139
{"awst", TZ, POS(32)}, /* W. Australia */
140
{"awt", DTZ, NEG(12)},
141
{"azost", DTZ, POS(0)}, /* Azores Summer Time */
142
{"azot", TZ, NEG(4)}, /* Azores Time */
143
{"azst", DTZ, POS(20)}, /* Azerbaijan Summer Time */
144
{"azt", TZ, POS(16)}, /* Azerbaijan Time */
145
{DB_C, ADBC, BC}, /* "bc" for years <= 0 */
146
{"bdst", TZ, POS(8)}, /* British Double Summer Time */
147
{"bdt", TZ, POS(24)}, /* Dacca */
148
{"bnt", TZ, POS(32)}, /* Brunei Darussalam Time */
149
{"bort", TZ, POS(32)}, /* Borneo Time (Indonesia) */
154
{"bot", TZ, NEG(16)}, /* Bolivia Time */
155
{"bra", TZ, NEG(12)}, /* Brazil Time */
156
{"brst", DTZ, NEG(8)}, /* Brasilia Summer Time */
157
{"brt", TZ, NEG(12)}, /* Brasilia Time */
158
{"bst", DTZ, POS(4)}, /* British Summer Time */
160
{"bst", TZ, NEG(12)}, /* Brazil Standard Time */
161
{"bst", DTZ, NEG(44)}, /* Bering Summer Time */
163
{"bt", TZ, POS(12)}, /* Baghdad Time */
164
{"btt", TZ, POS(24)}, /* Bhutan Time */
165
{"cadt", DTZ, POS(42)}, /* Central Australian DST */
166
{"cast", TZ, POS(38)}, /* Central Australian ST */
167
{"cat", TZ, NEG(40)}, /* Central Alaska Time */
168
{"cct", TZ, POS(32)}, /* China Coast Time */
170
{"cct", TZ, POS(26)}, /* Indian Cocos (Island) Time */
172
{"cdt", DTZ, NEG(20)}, /* Central Daylight Time */
173
{"cest", DTZ, POS(8)}, /* Central European Dayl.Time */
174
{"cet", TZ, POS(4)}, /* Central European Time */
175
{"cetdst", DTZ, POS(8)}, /* Central European Dayl.Time */
176
{"chadt", DTZ, POS(55)}, /* Chatham Island Daylight Time (13:45) */
177
{"chast", TZ, POS(51)}, /* Chatham Island Time (12:45) */
181
{"ckt", TZ, POS(48)}, /* Cook Islands Time */
182
{"clst", DTZ, NEG(12)}, /* Chile Summer Time */
183
{"clt", TZ, NEG(16)}, /* Chile Time */
187
{"cot", TZ, NEG(20)}, /* Columbia Time */
188
{"cst", TZ, NEG(24)}, /* Central Standard Time */
189
{DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
193
{"cvt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
194
{"cxt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
195
{"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
196
{"davt", TZ, POS(28)}, /* Davis Time (Antarctica) */
197
{"ddut", TZ, POS(40)}, /* Dumont-d'Urville Time (Antarctica) */
199
{"december", MONTH, 12},
200
{"dnt", TZ, POS(4)}, /* Dansk Normal Tid */
201
{"dow", RESERV, DTK_DOW}, /* day of week */
202
{"doy", RESERV, DTK_DOY}, /* day of year */
205
{"dusst", DTZ, POS(24)}, /* Dushanbe Summer Time */
207
{"easst", DTZ, NEG(20)}, /* Easter Island Summer Time */
208
{"east", TZ, NEG(24)}, /* Easter Island Time */
209
{"eat", TZ, POS(12)}, /* East Africa Time */
211
{"east", DTZ, POS(16)}, /* Indian Antananarivo Savings Time */
212
{"eat", TZ, POS(12)}, /* Indian Antananarivo Time */
213
{"ect", TZ, NEG(16)}, /* Eastern Caribbean Time */
214
{"ect", TZ, NEG(20)}, /* Ecuador Time */
216
{"edt", DTZ, NEG(16)}, /* Eastern Daylight Time */
217
{"eest", DTZ, POS(12)}, /* Eastern Europe Summer Time */
218
{"eet", TZ, POS(8)}, /* East. Europe, USSR Zone 1 */
219
{"eetdst", DTZ, POS(12)}, /* Eastern Europe Daylight Time */
220
{"egst", DTZ, POS(0)}, /* East Greenland Summer Time */
221
{"egt", TZ, NEG(4)}, /* East Greenland Time */
225
{EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
226
{"est", TZ, NEG(20)}, /* Eastern Standard Time */
228
{"february", MONTH, 2},
229
{"fjst", DTZ, NEG(52)}, /* Fiji Summer Time (13 hour offset!) */
230
{"fjt", TZ, NEG(48)}, /* Fiji Time */
231
{"fkst", DTZ, NEG(12)}, /* Falkland Islands Summer Time */
232
{"fkt", TZ, NEG(8)}, /* Falkland Islands Time */
233
{"fnst", DTZ, NEG(4)}, /* Fernando de Noronha Summer Time */
234
{"fnt", TZ, NEG(8)}, /* Fernando de Noronha Time */
237
{"fst", TZ, POS(4)}, /* French Summer Time */
238
{"fwt", DTZ, POS(8)}, /* French Winter Time */
239
{"galt", TZ, NEG(24)}, /* Galapagos Time */
240
{"gamt", TZ, NEG(36)}, /* Gambier Time */
241
{"gest", DTZ, POS(20)}, /* Georgia Summer Time */
242
{"get", TZ, POS(16)}, /* Georgia Time */
243
{"gft", TZ, NEG(12)}, /* French Guiana Time */
247
{"gilt", TZ, POS(48)}, /* Gilbert Islands Time */
248
{"gmt", TZ, POS(0)}, /* Greenwich Mean Time */
249
{"gst", TZ, POS(40)}, /* Guam Std Time, USSR Zone 9 */
250
{"gyt", TZ, NEG(16)}, /* Guyana Time */
251
{"h", UNITS, DTK_HOUR}, /* "hour" */
256
{"hdt", DTZ, NEG(36)}, /* Hawaii/Alaska Daylight Time */
260
{"hkt", TZ, POS(32)}, /* Hong Kong Time */
262
{"hmt", TZ, POS(12)}, /* Hellas ? ? */
266
{"hst", TZ, NEG(40)}, /* Hawaii Std Time */
270
{"ict", TZ, POS(28)}, /* Indochina Time */
271
{"idle", TZ, POS(48)}, /* Intl. Date Line, East */
272
{"idlw", TZ, NEG(48)}, /* Intl. Date Line, West */
274
idt /* Israeli, Iran, Indian Daylight Time */
276
{LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
277
{INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
278
{"iot", TZ, POS(20)}, /* Indian Chagos Time */
279
{"irkst", DTZ, POS(36)}, /* Irkutsk Summer Time */
280
{"irkt", TZ, POS(32)}, /* Irkutsk Time */
281
{"irt", TZ, POS(14)}, /* Iran Time */
285
{"ist", TZ, POS(8)}, /* Israel */
286
{"it", TZ, POS(14)}, /* Iran Time */
287
{"j", UNITS, DTK_JULIAN},
289
{"january", MONTH, 1},
290
{"javt", TZ, POS(28)}, /* Java Time (07:00? see JT) */
291
{"jayt", TZ, POS(36)}, /* Jayapura Time (Indonesia) */
292
{"jd", UNITS, DTK_JULIAN},
293
{"jst", TZ, POS(36)}, /* Japan Std Time,USSR Zone 8 */
294
{"jt", TZ, POS(30)}, /* Java Time (07:30? see JAVT) */
296
{"julian", UNITS, DTK_JULIAN},
300
{"kdt", DTZ, POS(40)}, /* Korea Daylight Time */
301
{"kgst", DTZ, POS(24)}, /* Kyrgyzstan Summer Time */
302
{"kgt", TZ, POS(20)}, /* Kyrgyzstan Time */
303
{"kost", TZ, POS(48)}, /* Kosrae Time */
304
{"krast", DTZ, POS(28)}, /* Krasnoyarsk Summer Time */
305
{"krat", TZ, POS(32)}, /* Krasnoyarsk Standard Time */
306
{"kst", TZ, POS(36)}, /* Korea Standard Time */
307
{"lhdt", DTZ, POS(44)}, /* Lord Howe Daylight Time, Australia */
308
{"lhst", TZ, POS(42)}, /* Lord Howe Standard Time, Australia */
309
{"ligt", TZ, POS(40)}, /* From Melbourne, Australia */
310
{"lint", TZ, POS(56)}, /* Line Islands Time (Kiribati; +14
312
{"lkt", TZ, POS(24)}, /* Lanka Time */
313
{"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
314
{"magst", DTZ, POS(48)}, /* Magadan Summer Time */
315
{"magt", TZ, POS(44)}, /* Magadan Time */
318
{"mart", TZ, NEG(38)}, /* Marquesas Time */
319
{"mawt", TZ, POS(24)}, /* Mawson, Antarctica */
321
{"mdt", DTZ, NEG(24)}, /* Mountain Daylight Time */
322
{"mest", DTZ, POS(8)}, /* Middle Europe Summer Time */
323
{"met", TZ, POS(4)}, /* Middle Europe Time */
324
{"metdst", DTZ, POS(8)}, /* Middle Europe Daylight Time */
325
{"mewt", TZ, POS(4)}, /* Middle Europe Winter Time */
326
{"mez", TZ, POS(4)}, /* Middle Europe Zone */
327
{"mht", TZ, POS(48)}, /* Kwajalein */
328
{"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
329
{"mmt", TZ, POS(26)}, /* Myanmar Time */
335
{"mpt", TZ, POS(40)}, /* North Mariana Islands Time */
336
{"msd", DTZ, POS(16)}, /* Moscow Summer Time */
337
{"msk", TZ, POS(12)}, /* Moscow Time */
338
{"mst", TZ, NEG(28)}, /* Mountain Standard Time */
339
{"mt", TZ, POS(34)}, /* Moluccas Time */
340
{"mut", TZ, POS(16)}, /* Mauritius Island Time */
341
{"mvt", TZ, POS(20)}, /* Maldives Island Time */
342
{"myt", TZ, POS(32)}, /* Malaysia Time */
346
{"nct", TZ, POS(44)}, /* New Caledonia Time */
347
{"ndt", DTZ, NEG(10)}, /* Nfld. Daylight Time */
348
{"nft", TZ, NEG(14)}, /* Newfoundland Standard Time */
349
{"nor", TZ, POS(4)}, /* Norway Standard Time */
351
{"november", MONTH, 11},
352
{"novst", DTZ, POS(28)}, /* Novosibirsk Summer Time */
353
{"novt", TZ, POS(24)}, /* Novosibirsk Standard Time */
354
{NOW, RESERV, DTK_NOW}, /* current transaction time */
355
{"npt", TZ, POS(23)}, /* Nepal Standard Time (GMT-5:45) */
356
{"nst", TZ, NEG(14)}, /* Nfld. Standard Time */
357
{"nt", TZ, NEG(44)}, /* Nome Time */
358
{"nut", TZ, NEG(44)}, /* Niue Time */
359
{"nzdt", DTZ, POS(52)}, /* New Zealand Daylight Time */
360
{"nzst", TZ, POS(48)}, /* New Zealand Standard Time */
361
{"nzt", TZ, POS(48)}, /* New Zealand Time */
363
{"october", MONTH, 10},
364
{"omsst", DTZ, POS(28)}, /* Omsk Summer Time */
365
{"omst", TZ, POS(24)}, /* Omsk Time */
366
{"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
367
{"pdt", DTZ, NEG(28)}, /* Pacific Daylight Time */
371
{"pet", TZ, NEG(20)}, /* Peru Time */
372
{"petst", DTZ, POS(52)}, /* Petropavlovsk-Kamchatski Summer Time */
373
{"pett", TZ, POS(48)}, /* Petropavlovsk-Kamchatski Time */
374
{"pgt", TZ, POS(40)}, /* Papua New Guinea Time */
375
{"phot", TZ, POS(52)}, /* Phoenix Islands (Kiribati) Time */
379
{"pht", TZ, POS(32)}, /* Phillipine Time */
380
{"pkt", TZ, POS(20)}, /* Pakistan Time */
382
{"pmdt", DTZ, NEG(8)}, /* Pierre & Miquelon Daylight Time */
386
{"pont", TZ, POS(44)}, /* Ponape Time (Micronesia) */
387
{"pst", TZ, NEG(32)}, /* Pacific Standard Time */
388
{"pwt", TZ, POS(36)}, /* Palau Time */
389
{"pyst", DTZ, NEG(12)}, /* Paraguay Summer Time */
390
{"pyt", TZ, NEG(16)}, /* Paraguay Time */
391
{"ret", DTZ, POS(16)}, /* Reunion Island Time */
392
{"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
393
{"sadt", DTZ, POS(42)}, /* S. Australian Dayl. Time */
398
{"sast", TZ, POS(38)}, /* South Australian Std Time */
400
{"saturday", DOW, 6},
404
{"sct", DTZ, POS(16)}, /* Mahe Island Time */
407
{"september", MONTH, 9},
408
{"set", TZ, NEG(4)}, /* Seychelles Time ?? */
412
{"sst", DTZ, POS(8)}, /* Swedish Summer Time */
415
{"swt", TZ, POS(4)}, /* Swedish Winter Time */
419
{"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
420
{"tft", TZ, POS(20)}, /* Kerguelen Time */
421
{"that", TZ, NEG(40)}, /* Tahiti Time */
425
{"thursday", DOW, 4},
426
{"tjt", TZ, POS(20)}, /* Tajikistan Time */
427
{"tkt", TZ, NEG(40)}, /* Tokelau Time */
428
{"tmt", TZ, POS(20)}, /* Turkmenistan Time */
429
{TODAY, RESERV, DTK_TODAY}, /* midnight */
430
{TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
434
{"tot", TZ, POS(52)}, /* Tonga Time */
438
{"truk", TZ, POS(40)}, /* Truk Time */
442
{"tvt", TZ, POS(48)}, /* Tuvalu Time */
446
{"ulast", DTZ, POS(36)}, /* Ulan Bator Summer Time */
447
{"ulat", TZ, POS(32)}, /* Ulan Bator Time */
448
{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
451
{"uyst", DTZ, NEG(8)}, /* Uruguay Summer Time */
452
{"uyt", TZ, NEG(12)}, /* Uruguay Time */
453
{"uzst", DTZ, POS(24)}, /* Uzbekistan Summer Time */
454
{"uzt", TZ, POS(20)}, /* Uzbekistan Time */
455
{"vet", TZ, NEG(16)}, /* Venezuela Time */
456
{"vlast", DTZ, POS(44)}, /* Vladivostok Summer Time */
457
{"vlat", TZ, POS(40)}, /* Vladivostok Time */
461
{"vut", TZ, POS(44)}, /* Vanuata Time */
462
{"wadt", DTZ, POS(32)}, /* West Australian DST */
463
{"wakt", TZ, POS(48)}, /* Wake Time */
467
{"wast", TZ, POS(28)}, /* West Australian Std Time */
468
{"wat", TZ, NEG(4)}, /* West Africa Time */
469
{"wdt", DTZ, POS(36)}, /* West Australian DST */
471
{"wednesday", DOW, 3},
473
{"west", DTZ, POS(4)}, /* Western Europe Summer Time */
474
{"wet", TZ, POS(0)}, /* Western Europe */
475
{"wetdst", DTZ, POS(4)}, /* Western Europe Daylight Savings Time */
476
{"wft", TZ, POS(48)}, /* Wallis and Futuna Time */
477
{"wgst", DTZ, NEG(8)}, /* West Greenland Summer Time */
478
{"wgt", TZ, NEG(12)}, /* West Greenland Time */
479
{"wst", TZ, POS(32)}, /* West Australian Standard Time */
480
{"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
481
{"yakst", DTZ, POS(40)}, /* Yakutsk Summer Time */
482
{"yakt", TZ, POS(36)}, /* Yakutsk Time */
483
{"yapt", TZ, POS(40)}, /* Yap Time (Micronesia) */
484
{"ydt", DTZ, NEG(32)}, /* Yukon Daylight Time */
485
{"yekst", DTZ, POS(24)}, /* Yekaterinburg Summer Time */
486
{"yekt", TZ, POS(20)}, /* Yekaterinburg Time */
487
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
488
{"yst", TZ, NEG(36)}, /* Yukon Standard Time */
489
{"z", TZ, POS(0)}, /* time zone tag per ISO-8601 */
490
{"zp4", TZ, NEG(16)}, /* UTC +4 hours. */
491
{"zp5", TZ, NEG(20)}, /* UTC +5 hours. */
492
{"zp6", TZ, NEG(24)}, /* UTC +6 hours. */
493
{ZULU, TZ, POS(0)}, /* UTC */
496
static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
498
/* Used for SET australian_timezones to override North American ones */
499
static datetkn australian_datetktbl[] = {
500
{"acst", TZ, POS(38)}, /* Cent. Australia */
501
{"cst", TZ, POS(42)}, /* Australia Central Std Time */
502
{"east", TZ, POS(40)}, /* East Australian Std Time */
503
{"est", TZ, POS(40)}, /* Australia Eastern Std Time */
504
{"sat", TZ, POS(38)},
507
static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
508
sizeof australian_datetktbl[0];
510
static datetkn deltatktbl[] = {
511
/* text, token, lexval */
512
{"@", IGNORE_DTF, 0}, /* postgres relative prefix */
513
{DAGO, AGO, 0}, /* "ago" indicates negative time offset */
514
{"c", UNITS, DTK_CENTURY}, /* "century" relative */
515
{"cent", UNITS, DTK_CENTURY}, /* "century" relative */
516
{"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
517
{DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
518
{"d", UNITS, DTK_DAY}, /* "day" relative */
519
{DDAY, UNITS, DTK_DAY}, /* "day" relative */
520
{"days", UNITS, DTK_DAY}, /* "days" relative */
521
{"dec", UNITS, DTK_DECADE}, /* "decade" relative */
522
{DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
523
{"decades", UNITS, DTK_DECADE}, /* "decades" relative */
524
{"decs", UNITS, DTK_DECADE}, /* "decades" relative */
525
{"h", UNITS, DTK_HOUR}, /* "hour" relative */
526
{DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
527
{"hours", UNITS, DTK_HOUR}, /* "hours" relative */
528
{"hr", UNITS, DTK_HOUR}, /* "hour" relative */
529
{"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
530
{INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */
531
{"m", UNITS, DTK_MINUTE}, /* "minute" relative */
532
{"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
533
{"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
534
{"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
535
{DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
536
{"millisecon", UNITS, DTK_MILLISEC}, /* relative */
537
{"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
538
{"min", UNITS, DTK_MINUTE}, /* "minute" relative */
539
{"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
540
{DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
541
{"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
542
{"mon", UNITS, DTK_MONTH}, /* "months" relative */
543
{"mons", UNITS, DTK_MONTH}, /* "months" relative */
544
{DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
545
{"months", UNITS, DTK_MONTH},
546
{"ms", UNITS, DTK_MILLISEC},
547
{"msec", UNITS, DTK_MILLISEC},
548
{DMILLISEC, UNITS, DTK_MILLISEC},
549
{"mseconds", UNITS, DTK_MILLISEC},
550
{"msecs", UNITS, DTK_MILLISEC},
551
{"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
552
{DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
553
{"reltime", IGNORE_DTF, 0}, /* pre-v6.1 "Undefined Reltime" */
554
{"s", UNITS, DTK_SECOND},
555
{"sec", UNITS, DTK_SECOND},
556
{DSECOND, UNITS, DTK_SECOND},
557
{"seconds", UNITS, DTK_SECOND},
558
{"secs", UNITS, DTK_SECOND},
559
{DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
560
{"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
561
{"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
562
{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
563
{"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
564
{"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
565
{DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
566
{"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
567
{"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
568
{"w", UNITS, DTK_WEEK}, /* "week" relative */
569
{DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
570
{"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
571
{"y", UNITS, DTK_YEAR}, /* "year" relative */
572
{DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
573
{"years", UNITS, DTK_YEAR}, /* "years" relative */
574
{"yr", UNITS, DTK_YEAR}, /* "year" relative */
575
{"yrs", UNITS, DTK_YEAR}, /* "years" relative */
578
static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
580
static datetkn *datecache[MAXDATEFIELDS] = {NULL};
582
static datetkn *deltacache[MAXDATEFIELDS] = {NULL};
586
* Calendar time to Julian date conversions.
587
* Julian date is commonly used in astronomical applications,
588
* since it is numerically accurate and computationally simple.
589
* The algorithms here will accurately convert between Julian day
590
* and calendar date for all non-negative Julian days
591
* (i.e. from Nov 24, -4713 on).
593
* These routines will be used by other date/time packages
596
* Rewritten to eliminate overflow problems. This now allows the
597
* routines to work correctly for all Julian day counts from
598
* 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
599
* a 32-bit integer. Longer types should also work to the limits
600
* of their precision.
604
date2j(int y, int m, int d)
621
julian = y * 365 - 32167;
622
julian += y / 4 - century + century / 4;
623
julian += 7834 * m / 256 + d;
629
j2date(int jd, int *year, int *month, int *day)
638
quad = julian / 146097;
639
extra = (julian - quad * 146097) * 4 + 3;
640
julian += 60 + quad * 3 + extra / 146097;
641
quad = julian / 1461;
642
julian -= quad * 1461;
643
y = julian * 4 / 1461;
644
julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
648
quad = julian * 2141 / 65536;
649
*day = julian - 7834 * quad / 256;
650
*month = (quad + 10) % 12 + 1;
657
* j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
659
* Note: various places use the locution j2day(date - 1) to produce a
660
* result according to the convention 0..6 = Mon..Sun. This is a bit of
661
* a crock, but will work as long as the computation here is just a modulo.
677
/* TrimTrailingZeros()
678
* ... resulting from printing numbers with full precision.
681
TrimTrailingZeros(char *str)
683
int len = strlen(str);
686
/* chop off trailing one to cope with interval rounding */
687
if (strcmp((str + len - 4), "0001") == 0)
694
/* chop off trailing zeros... but leave at least 2 fractional digits */
695
while ((*(str + len - 1) == '0')
696
&& (*(str + len - 3) != '.'))
705
* Break string into tokens based on a date/time context.
706
* Returns 0 if successful, DTERR code if bogus input detected.
708
* timestr - the input string
709
* lowstr - workspace for field string storage (must be large enough for
710
* a copy of the input string, including trailing null)
711
* field[] - pointers to field strings are returned in this array
712
* ftype[] - field type indicators are returned in this array
713
* maxfields - dimensions of the above two arrays
714
* *numfields - set to the actual number of fields detected
716
* The fields extracted from the input are stored as separate, null-terminated
717
* strings in the workspace at lowstr. Any text is converted to lower case.
719
* Several field types are assigned:
720
* DTK_NUMBER - digits and (possibly) a decimal point
721
* DTK_DATE - digits and two delimiters, or digits and text
722
* DTK_TIME - digits, colon delimiters, and possibly a decimal point
723
* DTK_STRING - text (no digits)
724
* DTK_SPECIAL - leading "+" or "-" followed by text
725
* DTK_TZ - leading "+" or "-" followed by digits
727
* Note that some field types can hold unexpected items:
728
* DTK_NUMBER can hold date fields (yy.ddd)
729
* DTK_STRING can hold months (January) and time zones (PST)
730
* DTK_DATE can hold Posix time zones (GMT-8)
733
ParseDateTime(const char *timestr, char *lowstr,
734
char **field, int *ftype, int maxfields, int *numfields)
737
const char *cp = timestr;
740
/* outer loop through fields */
743
/* Ignore spaces between fields */
744
if (isspace((unsigned char) *cp))
750
/* Record start of current field */
752
return DTERR_BAD_FORMAT;
755
/* leading digit? then date or time */
756
if (isdigit((unsigned char) *cp))
759
while (isdigit((unsigned char) *cp))
765
ftype[nf] = DTK_TIME;
767
while (isdigit((unsigned char) *cp) ||
768
(*cp == ':') || (*cp == '.'))
771
/* date field? allow embedded text month */
772
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
774
/* save delimiting character to use later */
778
/* second field is all digits? then no embedded text month */
779
if (isdigit((unsigned char) *cp))
781
ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
782
while (isdigit((unsigned char) *cp))
786
* insist that the delimiters match to get a
791
ftype[nf] = DTK_DATE;
793
while (isdigit((unsigned char) *cp) || (*cp == delim))
799
ftype[nf] = DTK_DATE;
800
while (isalnum((unsigned char) *cp) || (*cp == delim))
801
*lp++ = pg_tolower((unsigned char) *cp++);
806
* otherwise, number only and will determine year, month, day,
807
* or concatenated fields later...
810
ftype[nf] = DTK_NUMBER;
812
/* Leading decimal point? Then fractional seconds... */
816
while (isdigit((unsigned char) *cp))
819
ftype[nf] = DTK_NUMBER;
823
* text? then date string, month, day of week, special, or
826
else if (isalpha((unsigned char) *cp))
828
ftype[nf] = DTK_STRING;
829
*lp++ = pg_tolower((unsigned char) *cp++);
830
while (isalpha((unsigned char) *cp))
831
*lp++ = pg_tolower((unsigned char) *cp++);
834
* Full date string with leading text month? Could also be a
837
if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
841
ftype[nf] = DTK_DATE;
843
while (isdigit((unsigned char) *cp) || (*cp == delim))
847
/* sign? then special or numeric timezone */
848
else if ((*cp == '+') || (*cp == '-'))
851
/* soak up leading whitespace */
852
while (isspace((unsigned char) *cp))
854
/* numeric timezone? */
855
if (isdigit((unsigned char) *cp))
859
while (isdigit((unsigned char) *cp) ||
860
(*cp == ':') || (*cp == '.'))
864
else if (isalpha((unsigned char) *cp))
866
ftype[nf] = DTK_SPECIAL;
867
*lp++ = pg_tolower((unsigned char) *cp++);
868
while (isalpha((unsigned char) *cp))
869
*lp++ = pg_tolower((unsigned char) *cp++);
871
/* otherwise something wrong... */
873
return DTERR_BAD_FORMAT;
875
/* ignore other punctuation but use as delimiter */
876
else if (ispunct((unsigned char) *cp))
881
/* otherwise, something is not right... */
883
return DTERR_BAD_FORMAT;
885
/* force in a delimiter after each field */
897
* Interpret previously parsed fields for general date and time.
898
* Return 0 if full date, 1 if only time, and negative DTERR code if problems.
899
* (Currently, all callers treat 1 as an error return too.)
901
* External format(s):
902
* "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
903
* "Fri Feb-7-1997 15:23:27"
904
* "Feb-7-1997 15:23:27"
905
* "2-7-1997 15:23:27"
906
* "1997-2-7 15:23:27"
907
* "1997.038 15:23:27" (day of year 1-366)
908
* Also supports input in compact time:
911
* "20011225T040506.789-07"
913
* Use the system-provided functions to get the current time zone
914
* if not specified in the input string.
915
* If the date is outside the time_t system-supported time range,
916
* then assume UTC time zone. - thomas 1997-05-27
919
DecodeDateTime(char **field, int *ftype, int nf,
920
int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
925
int ptype = 0; /* "prefix type" for ISO y2001m02d04
931
bool haveTextMonth = FALSE;
932
int is2digits = FALSE;
936
* We'll insist on at least all of the date fields, but initialize the
937
* remaining fields in case they are not set later...
944
/* don't know daylight savings time status apriori */
949
for (i = 0; i < nf; i++)
955
* Integral julian day with attached time zone?
956
* All other forms with JD will be separated into
957
* distinct fields, so we handle just this case here.
959
if (ptype == DTK_JULIAN)
965
return DTERR_BAD_FORMAT;
967
val = strtol(field[i], &cp, 10);
969
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
970
/* Get the time zone from the end of the string */
971
dterr = DecodeTimezone(cp, tzp);
975
tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
980
* Already have a date? Then this might be a POSIX time
981
* zone with an embedded dash (e.g. "PST-3" == "EST") or
982
* a run-together time with trailing time zone (e.g. hhmmss-zz).
983
* - thomas 2001-12-25
985
else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
988
/* No time zone accepted? Then quit... */
990
return DTERR_BAD_FORMAT;
992
if (isdigit((unsigned char) *field[i]) || ptype != 0)
998
/* Sanity check; should not fail this test */
999
if (ptype != DTK_TIME)
1000
return DTERR_BAD_FORMAT;
1005
* Starts with a digit but we already have a time
1006
* field? Then we are in trouble with a date and
1009
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1010
return DTERR_BAD_FORMAT;
1012
if ((cp = strchr(field[i], '-')) == NULL)
1013
return DTERR_BAD_FORMAT;
1015
/* Get the time zone from the end of the string */
1016
dterr = DecodeTimezone(cp, tzp);
1022
* Then read the rest of the field as a
1025
dterr = DecodeNumberField(strlen(field[i]), field[i],
1034
* modify tmask after returning from
1035
* DecodeNumberField()
1041
dterr = DecodePosixTimezone(field[i], tzp);
1051
dterr = DecodeDate(field[i], fmask, &tmask, tm);
1058
dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
1063
* Check upper limit on hours; other limits checked in
1066
if (tm->tm_hour > 23)
1067
return DTERR_FIELD_OVERFLOW;
1075
return DTERR_BAD_FORMAT;
1077
dterr = DecodeTimezone(field[i], &tz);
1082
* Already have a time zone? Then maybe this is the
1083
* second field of a POSIX time: EST+3 (equivalent to
1086
if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1087
&& (ftype[i - 1] == DTK_TZ)
1088
&& (isalpha((unsigned char) *field[i - 1])))
1104
* Was this an "ISO date" with embedded field labels? An
1105
* example is "y2001m02d04" - thomas 2001-02-04
1112
val = strtol(field[i], &cp, 10);
1115
* only a few kinds are allowed to have an embedded
1126
return DTERR_BAD_FORMAT;
1129
else if (*cp != '\0')
1130
return DTERR_BAD_FORMAT;
1136
tmask = DTK_M(YEAR);
1142
* already have a month and hour? then assume
1145
if (((fmask & DTK_M(MONTH)) != 0)
1146
&& ((fmask & DTK_M(HOUR)) != 0))
1149
tmask = DTK_M(MINUTE);
1154
tmask = DTK_M(MONTH);
1165
tmask = DTK_M(HOUR);
1170
tmask = DTK_M(MINUTE);
1175
tmask = DTK_M(SECOND);
1180
frac = strtod(cp, &cp);
1182
return DTERR_BAD_FORMAT;
1183
#ifdef HAVE_INT64_TIMESTAMP
1184
*fsec = rint(frac * 1000000);
1193
dterr = DecodeTimezone(field[i], tzp);
1200
* previous field was a label for "julian date"?
1203
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1204
/* fractional Julian Day? */
1209
time = strtod(cp, &cp);
1211
return DTERR_BAD_FORMAT;
1213
tmask |= DTK_TIME_M;
1214
#ifdef HAVE_INT64_TIMESTAMP
1215
dt2time((time * INT64CONST(86400000000)),
1216
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1218
dt2time((time * 86400),
1219
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1225
/* previous field was "t" for ISO time */
1226
dterr = DecodeNumberField(strlen(field[i]), field[i],
1227
(fmask | DTK_DATE_M),
1234
if (tmask != DTK_TIME_M)
1235
return DTERR_BAD_FORMAT;
1239
return DTERR_BAD_FORMAT;
1251
flen = strlen(field[i]);
1252
cp = strchr(field[i], '.');
1254
/* Embedded decimal and no date yet? */
1255
if ((cp != NULL) && !(fmask & DTK_DATE_M))
1257
dterr = DecodeDate(field[i], fmask, &tmask, tm);
1261
/* embedded decimal and several digits before? */
1262
else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
1265
* Interpret as a concatenated date or time Set
1266
* the type field to allow decoding other fields
1267
* later. Example: 20011223 or 040506
1269
dterr = DecodeNumberField(flen, field[i], fmask,
1278
dterr = DecodeNumberField(flen, field[i], fmask,
1285
/* otherwise it is a single date/time field... */
1288
dterr = DecodeNumber(flen, field[i],
1289
haveTextMonth, fmask,
1300
type = DecodeSpecial(i, field[i], &val);
1301
if (type == IGNORE_DTF)
1304
tmask = DTK_M(type);
1312
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1313
errmsg("date/time value \"current\" is no longer supported")));
1315
return DTERR_BAD_FORMAT;
1319
tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1321
GetCurrentTimeUsec(tm, fsec, tzp);
1327
GetCurrentDateTime(tm);
1328
j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
1329
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1338
GetCurrentDateTime(tm);
1347
GetCurrentDateTime(tm);
1348
j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
1349
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1356
tmask = (DTK_TIME_M | DTK_M(TZ));
1374
* already have a (numeric) month? then see if we
1377
if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
1378
&& (!(fmask & DTK_M(DAY)))
1379
&& ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
1381
tm->tm_mday = tm->tm_mon;
1384
haveTextMonth = TRUE;
1391
* daylight savings time modifier (solves "MET
1394
tmask |= DTK_M(DTZ);
1397
return DTERR_BAD_FORMAT;
1404
* set mask for TZ here _or_ check for DTZ later
1405
* when getting default timezone
1410
return DTERR_BAD_FORMAT;
1418
return DTERR_BAD_FORMAT;
1446
* This is a filler field "t" indicating that the
1447
* next field is time. Try to verify that this is
1452
/* No preceding date? Then quit... */
1453
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1454
return DTERR_BAD_FORMAT;
1457
* We will need one of the following fields:
1458
* DTK_NUMBER should be hhmmss.fff
1459
* DTK_TIME should be hh:mm:ss.fff
1460
* DTK_DATE should be hhmmss-zz
1463
|| ((ftype[i + 1] != DTK_NUMBER)
1464
&& (ftype[i + 1] != DTK_TIME)
1465
&& (ftype[i + 1] != DTK_DATE)))
1466
return DTERR_BAD_FORMAT;
1472
return DTERR_BAD_FORMAT;
1477
return DTERR_BAD_FORMAT;
1481
return DTERR_BAD_FORMAT;
1485
if (fmask & DTK_M(YEAR))
1487
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1490
if (tm->tm_year > 0)
1491
tm->tm_year = -(tm->tm_year - 1);
1494
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1495
errmsg("inconsistent use of year %04d and \"BC\"",
1500
if (tm->tm_year < 70)
1501
tm->tm_year += 2000;
1502
else if (tm->tm_year < 100)
1503
tm->tm_year += 1900;
1507
/* now that we have correct year, decode DOY */
1508
if (fmask & DTK_M(DOY))
1510
j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
1511
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1514
/* check for valid month */
1515
if (fmask & DTK_M(MONTH))
1517
if (tm->tm_mon < 1 || tm->tm_mon > 12)
1518
return DTERR_MD_FIELD_OVERFLOW;
1521
/* minimal check for valid day */
1522
if (fmask & DTK_M(DAY))
1524
if (tm->tm_mday < 1 || tm->tm_mday > 31)
1525
return DTERR_MD_FIELD_OVERFLOW;
1528
if ((mer != HR24) && (tm->tm_hour > 12))
1529
return DTERR_FIELD_OVERFLOW;
1530
if ((mer == AM) && (tm->tm_hour == 12))
1532
else if ((mer == PM) && (tm->tm_hour != 12))
1535
/* do additional checking for full date specs... */
1536
if (*dtype == DTK_DATE)
1538
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1540
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1542
return DTERR_BAD_FORMAT;
1546
* Check for valid day of month, now that we know for sure the
1547
* month and year. Note we don't use MD_FIELD_OVERFLOW here,
1548
* since it seems unlikely that "Feb 29" is a YMD-order error.
1550
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
1551
return DTERR_FIELD_OVERFLOW;
1553
/* timezone not specified? then find local timezone if possible */
1554
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
1557
* daylight savings time modifier but no standard timezone?
1560
if (fmask & DTK_M(DTZMOD))
1561
return DTERR_BAD_FORMAT;
1563
*tzp = DetermineLocalTimeZone(tm);
1571
/* DetermineLocalTimeZone()
1573
* Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1574
* tm_sec fields are set, attempt to determine the applicable local zone
1575
* (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's
1576
* tm_isdst field accordingly, and return the actual timezone offset.
1578
* Note: it might seem that we should use mktime() for this, but bitter
1579
* experience teaches otherwise. This code is much faster than most versions
1580
* of mktime(), anyway.
1583
DetermineLocalTimeZone(struct pg_tm * tm)
1593
long int before_gmtoff,
1601
tm->tm_isdst = 0; /* for lack of a better idea */
1606
* First, generate the pg_time_t value corresponding to the given
1607
* y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide
1608
* the timezone is GMT. (We only need to worry about overflow on
1609
* machines where pg_time_t is 32 bits.)
1611
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1613
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
1615
day = ((pg_time_t) date) *86400;
1616
if (day / 86400 != date)
1618
sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * 60) * 60;
1620
/* since sec >= 0, overflow could only be from +day to -mytime */
1621
if (mytime < 0 && day > 0)
1625
* Find the DST time boundary just before or following the target time.
1626
* We assume that all zones have GMT offsets less than 24 hours, and
1627
* that DST boundaries can't be closer together than 48 hours, so
1628
* backing up 24 hours and finding the "next" boundary will work.
1630
prevtime = mytime - (24 * 60 * 60);
1631
if (mytime < 0 && prevtime > 0)
1634
res = pg_next_dst_boundary(&prevtime,
1635
&before_gmtoff, &before_isdst,
1637
&after_gmtoff, &after_isdst);
1639
goto overflow; /* failure? */
1643
/* Non-DST zone, life is simple */
1644
tm->tm_isdst = before_isdst;
1645
return - (int) before_gmtoff;
1649
* Form the candidate pg_time_t values with local-time adjustment
1651
beforetime = mytime - before_gmtoff;
1652
if ((before_gmtoff > 0) ? (mytime < 0 && beforetime > 0) :
1653
(mytime > 0 && beforetime < 0))
1655
aftertime = mytime - after_gmtoff;
1656
if ((after_gmtoff > 0) ? (mytime < 0 && aftertime > 0) :
1657
(mytime > 0 && aftertime < 0))
1661
* If both before or both after the boundary time, we know what to do
1663
if (beforetime <= boundary && aftertime < boundary)
1665
tm->tm_isdst = before_isdst;
1666
return - (int) before_gmtoff;
1668
if (beforetime > boundary && aftertime >= boundary)
1670
tm->tm_isdst = after_isdst;
1671
return - (int) after_gmtoff;
1674
* It's an invalid or ambiguous time due to timezone transition.
1675
* Prefer the standard-time interpretation.
1677
if (after_isdst == 0)
1679
tm->tm_isdst = after_isdst;
1680
return - (int) after_gmtoff;
1682
tm->tm_isdst = before_isdst;
1683
return - (int) before_gmtoff;
1686
/* Given date is out of range, so assume UTC */
1693
* Interpret parsed string as time fields only.
1694
* Returns 0 if successful, DTERR code if bogus input detected.
1696
* Note that support for time zone is here for
1697
* SQL92 TIME WITH TIME ZONE, but it reveals
1698
* bogosity with SQL92 date/time standards, since
1699
* we must infer a time zone from current time.
1700
* - thomas 2000-03-10
1701
* Allow specifying date to get a better time zone,
1702
* if time zones are allowed. - thomas 2001-12-26
1705
DecodeTimeOnly(char **field, int *ftype, int nf,
1706
int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1711
int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */
1715
int is2digits = FALSE;
1723
/* don't know daylight savings time status apriori */
1729
for (i = 0; i < nf; i++)
1736
* Time zone not allowed? Then should not accept dates or
1737
* time zones no matter what else!
1740
return DTERR_BAD_FORMAT;
1742
/* Under limited circumstances, we will accept a date... */
1743
if ((i == 0) && (nf >= 2)
1744
&& ((ftype[nf - 1] == DTK_DATE)
1745
|| (ftype[1] == DTK_TIME)))
1747
dterr = DecodeDate(field[i], fmask, &tmask, tm);
1751
/* otherwise, this is a time and/or time zone */
1754
if (isdigit((unsigned char) *field[i]))
1759
* Starts with a digit but we already have a time
1760
* field? Then we are in trouble with time
1763
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1764
return DTERR_BAD_FORMAT;
1767
* Should not get here and fail. Sanity check
1770
if ((cp = strchr(field[i], '-')) == NULL)
1771
return DTERR_BAD_FORMAT;
1773
/* Get the time zone from the end of the string */
1774
dterr = DecodeTimezone(cp, tzp);
1780
* Then read the rest of the field as a
1783
dterr = DecodeNumberField(strlen(field[i]), field[i],
1784
(fmask | DTK_DATE_M),
1795
dterr = DecodePosixTimezone(field[i], tzp);
1806
dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
1817
return DTERR_BAD_FORMAT;
1819
dterr = DecodeTimezone(field[i], &tz);
1824
* Already have a time zone? Then maybe this is the
1825
* second field of a POSIX time: EST+3 (equivalent to
1828
if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1829
&& (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1])))
1845
* Was this an "ISO time" with embedded field labels? An
1846
* example is "h04m05s06" - thomas 2001-02-04
1853
/* Only accept a date under limited circumstances */
1861
return DTERR_BAD_FORMAT;
1866
val = strtol(field[i], &cp, 10);
1869
* only a few kinds are allowed to have an embedded
1880
return DTERR_BAD_FORMAT;
1883
else if (*cp != '\0')
1884
return DTERR_BAD_FORMAT;
1890
tmask = DTK_M(YEAR);
1896
* already have a month and hour? then assume
1899
if (((fmask & DTK_M(MONTH)) != 0)
1900
&& ((fmask & DTK_M(HOUR)) != 0))
1903
tmask = DTK_M(MINUTE);
1908
tmask = DTK_M(MONTH);
1919
tmask = DTK_M(HOUR);
1924
tmask = DTK_M(MINUTE);
1929
tmask = DTK_M(SECOND);
1934
frac = strtod(cp, &cp);
1936
return DTERR_BAD_FORMAT;
1937
#ifdef HAVE_INT64_TIMESTAMP
1938
*fsec = rint(frac * 1000000);
1947
dterr = DecodeTimezone(field[i], tzp);
1954
* previous field was a label for "julian date"?
1957
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1962
time = strtod(cp, &cp);
1964
return DTERR_BAD_FORMAT;
1966
tmask |= DTK_TIME_M;
1967
#ifdef HAVE_INT64_TIMESTAMP
1968
dt2time((time * INT64CONST(86400000000)),
1969
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1971
dt2time((time * 86400),
1972
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1978
/* previous field was "t" for ISO time */
1979
dterr = DecodeNumberField(strlen(field[i]), field[i],
1980
(fmask | DTK_DATE_M),
1987
if (tmask != DTK_TIME_M)
1988
return DTERR_BAD_FORMAT;
1992
return DTERR_BAD_FORMAT;
2004
flen = strlen(field[i]);
2005
cp = strchr(field[i], '.');
2007
/* Embedded decimal? */
2011
* Under limited circumstances, we will accept a
2014
if ((i == 0) && ((nf >= 2) && (ftype[nf - 1] == DTK_DATE)))
2016
dterr = DecodeDate(field[i], fmask, &tmask, tm);
2020
/* embedded decimal and several digits before? */
2021
else if ((flen - strlen(cp)) > 2)
2024
* Interpret as a concatenated date or time
2025
* Set the type field to allow decoding other
2026
* fields later. Example: 20011223 or 040506
2028
dterr = DecodeNumberField(flen, field[i],
2029
(fmask | DTK_DATE_M),
2037
return DTERR_BAD_FORMAT;
2041
dterr = DecodeNumberField(flen, field[i],
2042
(fmask | DTK_DATE_M),
2049
/* otherwise it is a single date/time field... */
2052
dterr = DecodeNumber(flen, field[i],
2054
(fmask | DTK_DATE_M),
2065
type = DecodeSpecial(i, field[i], &val);
2066
if (type == IGNORE_DTF)
2069
tmask = DTK_M(type);
2077
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2078
errmsg("date/time value \"current\" is no longer supported")));
2079
return DTERR_BAD_FORMAT;
2085
GetCurrentTimeUsec(tm, fsec, NULL);
2089
tmask = (DTK_TIME_M | DTK_M(TZ));
2098
return DTERR_BAD_FORMAT;
2106
* daylight savings time modifier (solves "MET
2109
tmask |= DTK_M(DTZ);
2112
return DTERR_BAD_FORMAT;
2119
* set mask for TZ here _or_ check for DTZ later
2120
* when getting default timezone
2125
return DTERR_BAD_FORMAT;
2133
return DTERR_BAD_FORMAT;
2154
* We will need one of the following fields:
2155
* DTK_NUMBER should be hhmmss.fff
2156
* DTK_TIME should be hh:mm:ss.fff
2157
* DTK_DATE should be hhmmss-zz
2160
|| ((ftype[i + 1] != DTK_NUMBER)
2161
&& (ftype[i + 1] != DTK_TIME)
2162
&& (ftype[i + 1] != DTK_DATE)))
2163
return DTERR_BAD_FORMAT;
2169
return DTERR_BAD_FORMAT;
2174
return DTERR_BAD_FORMAT;
2178
return DTERR_BAD_FORMAT;
2182
if ((mer != HR24) && (tm->tm_hour > 12))
2183
return DTERR_FIELD_OVERFLOW;
2184
if ((mer == AM) && (tm->tm_hour == 12))
2186
else if ((mer == PM) && (tm->tm_hour != 12))
2189
#ifdef HAVE_INT64_TIMESTAMP
2190
if ((tm->tm_hour < 0) || (tm->tm_hour > 23)
2191
|| (tm->tm_min < 0) || (tm->tm_min > 59)
2192
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
2193
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
2194
return DTERR_FIELD_OVERFLOW;
2196
if ((tm->tm_hour < 0) || (tm->tm_hour > 23)
2197
|| (tm->tm_min < 0) || (tm->tm_min > 59)
2198
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
2199
|| (*fsec < 0) || (*fsec >= 1))
2200
return DTERR_FIELD_OVERFLOW;
2203
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2204
return DTERR_BAD_FORMAT;
2206
/* timezone not specified? then find local timezone if possible */
2207
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
2213
* daylight savings time modifier but no standard timezone? then
2216
if (fmask & DTK_M(DTZMOD))
2217
return DTERR_BAD_FORMAT;
2219
if ((fmask & DTK_DATE_M) == 0)
2220
GetCurrentDateTime(tmp);
2223
tmp->tm_year = tm->tm_year;
2224
tmp->tm_mon = tm->tm_mon;
2225
tmp->tm_mday = tm->tm_mday;
2227
tmp->tm_hour = tm->tm_hour;
2228
tmp->tm_min = tm->tm_min;
2229
tmp->tm_sec = tm->tm_sec;
2230
*tzp = DetermineLocalTimeZone(tmp);
2231
tm->tm_isdst = tmp->tm_isdst;
2238
* Decode date string which includes delimiters.
2239
* Return 0 if okay, a DTERR code if not.
2241
* Insist on a complete set of fields.
2244
DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
2251
bool haveTextMonth = FALSE;
2253
int is2digits = FALSE;
2257
char *field[MAXDATEFIELDS];
2259
/* parse this string... */
2260
while ((*str != '\0') && (nf < MAXDATEFIELDS))
2262
/* skip field separators */
2263
while (!isalnum((unsigned char) *str))
2267
if (isdigit((unsigned char) *str))
2269
while (isdigit((unsigned char) *str))
2272
else if (isalpha((unsigned char) *str))
2274
while (isalpha((unsigned char) *str))
2278
/* Just get rid of any non-digit, non-alpha characters... */
2285
/* don't allow too many fields */
2287
return DTERR_BAD_FORMAT;
2292
/* look first for text fields, since that will be unambiguous month */
2293
for (i = 0; i < nf; i++)
2295
if (isalpha((unsigned char) *field[i]))
2297
type = DecodeSpecial(i, field[i], &val);
2298
if (type == IGNORE_DTF)
2301
dmask = DTK_M(type);
2306
haveTextMonth = TRUE;
2314
return DTERR_BAD_FORMAT;
2317
return DTERR_BAD_FORMAT;
2322
/* mark this field as being completed */
2327
/* now pick up remaining numeric fields */
2328
for (i = 0; i < nf; i++)
2330
if (field[i] == NULL)
2333
if ((len = strlen(field[i])) <= 0)
2334
return DTERR_BAD_FORMAT;
2336
dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
2343
return DTERR_BAD_FORMAT;
2349
if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2350
return DTERR_BAD_FORMAT;
2352
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2355
if (tm->tm_year > 0)
2356
tm->tm_year = -(tm->tm_year - 1);
2359
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2360
errmsg("inconsistent use of year %04d and \"BC\"",
2365
if (tm->tm_year < 70)
2366
tm->tm_year += 2000;
2367
else if (tm->tm_year < 100)
2368
tm->tm_year += 1900;
2371
/* now that we have correct year, decode DOY */
2372
if (fmask & DTK_M(DOY))
2374
j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
2375
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2378
/* check for valid month */
2379
if (tm->tm_mon < 1 || tm->tm_mon > 12)
2380
return DTERR_MD_FIELD_OVERFLOW;
2382
/* check for valid day */
2383
if (tm->tm_mday < 1 || tm->tm_mday > 31)
2384
return DTERR_MD_FIELD_OVERFLOW;
2386
/* We don't want to hint about DateStyle for Feb 29 */
2387
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2388
return DTERR_FIELD_OVERFLOW;
2395
* Decode time string which includes delimiters.
2396
* Return 0 if okay, a DTERR code if not.
2398
* Only check the lower limit on hours, since this same code
2399
* can be used to represent time spans.
2402
DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec)
2406
*tmask = DTK_TIME_M;
2408
tm->tm_hour = strtol(str, &cp, 10);
2410
return DTERR_BAD_FORMAT;
2412
tm->tm_min = strtol(str, &cp, 10);
2418
else if (*cp != ':')
2419
return DTERR_BAD_FORMAT;
2423
tm->tm_sec = strtol(str, &cp, 10);
2426
else if (*cp == '.')
2431
frac = strtod(str, &cp);
2433
return DTERR_BAD_FORMAT;
2434
#ifdef HAVE_INT64_TIMESTAMP
2435
*fsec = rint(frac * 1000000);
2441
return DTERR_BAD_FORMAT;
2444
/* do a sanity check */
2445
#ifdef HAVE_INT64_TIMESTAMP
2446
if ((tm->tm_hour < 0)
2447
|| (tm->tm_min < 0) || (tm->tm_min > 59)
2448
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
2449
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
2450
return DTERR_FIELD_OVERFLOW;
2452
if ((tm->tm_hour < 0)
2453
|| (tm->tm_min < 0) || (tm->tm_min > 59)
2454
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
2455
|| (*fsec < 0) || (*fsec >= 1))
2456
return DTERR_FIELD_OVERFLOW;
2464
* Interpret plain numeric field as a date value in context.
2465
* Return 0 if okay, a DTERR code if not.
2468
DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
2469
int *tmask, struct pg_tm * tm, fsec_t *fsec, int *is2digits)
2477
val = strtol(str, &cp, 10);
2479
return DTERR_BAD_FORMAT;
2486
* More than two digits before decimal point? Then could be a date
2487
* or a run-together time: 2001.360 20011225 040506.789
2491
dterr = DecodeNumberField(flen, str,
2492
(fmask | DTK_DATE_M),
2500
frac = strtod(cp, &cp);
2502
return DTERR_BAD_FORMAT;
2503
#ifdef HAVE_INT64_TIMESTAMP
2504
*fsec = rint(frac * 1000000);
2509
else if (*cp != '\0')
2510
return DTERR_BAD_FORMAT;
2512
/* Special case for day of year */
2514
((fmask & DTK_DATE_M) == DTK_M(YEAR)) &&
2515
((val >= 1) && (val <= 366)))
2517
*tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2519
/* tm_mon and tm_mday can't actually be set yet ... */
2523
/* Switch based on what we have so far */
2524
switch (fmask & DTK_DATE_M)
2529
* Nothing so far; make a decision about what we think the
2530
* input is. There used to be lots of heuristics here, but
2531
* the consensus now is to be paranoid. It *must* be either
2532
* YYYY-MM-DD (with a more-than-two-digit year field), or the
2533
* field order defined by DateOrder.
2535
if (flen >= 3 || DateOrder == DATEORDER_YMD)
2537
*tmask = DTK_M(YEAR);
2540
else if (DateOrder == DATEORDER_DMY)
2542
*tmask = DTK_M(DAY);
2547
*tmask = DTK_M(MONTH);
2553
/* Must be at second field of YY-MM-DD */
2554
*tmask = DTK_M(MONTH);
2558
case (DTK_M(MONTH)):
2562
* We are at the first numeric field of a date that
2563
* included a textual month name. We want to support the
2564
* variants MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as
2565
* unambiguous inputs. We will also accept MON-DD-YY or
2566
* DD-MON-YY in either DMY or MDY modes, as well as
2567
* YY-MON-DD in YMD mode.
2569
if (flen >= 3 || DateOrder == DATEORDER_YMD)
2571
*tmask = DTK_M(YEAR);
2576
*tmask = DTK_M(DAY);
2582
/* Must be at second field of MM-DD-YY */
2583
*tmask = DTK_M(DAY);
2588
case (DTK_M(YEAR) | DTK_M(MONTH)):
2591
/* Need to accept DD-MON-YYYY even in YMD mode */
2592
if (flen >= 3 && *is2digits)
2594
/* Guess that first numeric field is day was wrong */
2595
*tmask = DTK_M(DAY); /* YEAR is already set */
2596
tm->tm_mday = tm->tm_year;
2602
*tmask = DTK_M(DAY);
2608
/* Must be at third field of YY-MM-DD */
2609
*tmask = DTK_M(DAY);
2615
/* Must be at second field of DD-MM-YY */
2616
*tmask = DTK_M(MONTH);
2620
case (DTK_M(MONTH) | DTK_M(DAY)):
2621
/* Must be at third field of DD-MM-YY or MM-DD-YY */
2622
*tmask = DTK_M(YEAR);
2626
case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
2627
/* we have all the date, so it must be a time field */
2628
dterr = DecodeNumberField(flen, str, fmask,
2636
/* Anything else is bogus input */
2637
return DTERR_BAD_FORMAT;
2641
* When processing a year field, mark it for adjustment if it's only
2642
* one or two digits.
2644
if (*tmask == DTK_M(YEAR))
2645
*is2digits = (flen <= 2);
2651
/* DecodeNumberField()
2652
* Interpret numeric string as a concatenated date or time field.
2653
* Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2655
* Use the context of previously decoded fields to help with
2656
* the interpretation.
2659
DecodeNumberField(int len, char *str, int fmask,
2660
int *tmask, struct pg_tm * tm, fsec_t *fsec, int *is2digits)
2665
* Have a decimal point? Then this is a date or something with a
2668
if ((cp = strchr(str, '.')) != NULL)
2672
frac = strtod(cp, NULL);
2673
#ifdef HAVE_INT64_TIMESTAMP
2674
*fsec = rint(frac * 1000000);
2681
/* No decimal point and no complete date yet? */
2682
else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2687
*tmask = DTK_DATE_M;
2689
tm->tm_mday = atoi(str + 6);
2691
tm->tm_mon = atoi(str + 4);
2693
tm->tm_year = atoi(str + 0);
2700
*tmask = DTK_DATE_M;
2701
tm->tm_mday = atoi(str + 4);
2703
tm->tm_mon = atoi(str + 2);
2705
tm->tm_year = atoi(str + 0);
2712
/* not all time fields are specified? */
2713
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2718
*tmask = DTK_TIME_M;
2719
tm->tm_sec = atoi(str + 4);
2721
tm->tm_min = atoi(str + 2);
2723
tm->tm_hour = atoi(str + 0);
2730
*tmask = DTK_TIME_M;
2732
tm->tm_min = atoi(str + 2);
2734
tm->tm_hour = atoi(str + 0);
2740
return DTERR_BAD_FORMAT;
2745
* Interpret string as a numeric timezone.
2747
* Return 0 if okay (and set *tzp), a DTERR code if not okay.
2749
* NB: this must *not* ereport on failure; see commands/variable.c.
2751
* Note: we allow timezone offsets up to 13:59. There are places that
2752
* use +1300 summer time.
2755
DecodeTimezone(char *str, int *tzp)
2762
/* leading character must be "+" or "-" */
2763
if (*str != '+' && *str != '-')
2764
return DTERR_BAD_FORMAT;
2766
hr = strtol((str + 1), &cp, 10);
2768
/* explicit delimiter? */
2770
min = strtol((cp + 1), &cp, 10);
2771
/* otherwise, might have run things together... */
2772
else if ((*cp == '\0') && (strlen(str) > 3))
2780
if ((hr < 0) || (hr > 13))
2781
return DTERR_TZDISP_OVERFLOW;
2782
if ((min < 0) || (min >= 60))
2783
return DTERR_TZDISP_OVERFLOW;
2785
tz = (hr * 60 + min) * 60;
2792
return DTERR_BAD_FORMAT;
2798
/* DecodePosixTimezone()
2799
* Interpret string as a POSIX-compatible timezone:
2803
* - thomas 2000-03-15
2805
* Return 0 if okay (and set *tzp), a DTERR code if not okay.
2808
DecodePosixTimezone(char *str, int *tzp)
2817
/* advance over name part */
2819
while (*cp && isalpha((unsigned char) *cp))
2822
/* decode offset, if present */
2825
dterr = DecodeTimezone(cp, &tz);
2832
/* decode name part. We must temporarily scribble on the input! */
2835
type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
2842
*tzp = (val * 60) - tz;
2846
return DTERR_BAD_FORMAT;
2854
* Decode text string using lookup table.
2856
* Implement a cache lookup since it is likely that dates
2857
* will be related in format.
2859
* NB: this must *not* ereport on failure;
2860
* see commands/variable.c.
2863
DecodeSpecial(int field, char *lowtoken, int *val)
2868
if ((datecache[field] != NULL)
2869
&& (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
2870
tp = datecache[field];
2874
if (Australian_timezones)
2875
tp = datebsearch(lowtoken, australian_datetktbl,
2876
australian_szdatetktbl);
2878
tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
2880
datecache[field] = tp;
2883
type = UNKNOWN_FIELD;
2908
* Interpret previously parsed fields for general time interval.
2909
* Returns 0 if successful, DTERR code if bogus input detected.
2911
* Allow "date" field DTK_DATE since this could be just
2912
* an unsigned floating point number. - thomas 1997-11-16
2914
* Allow ISO-style time span, with implicit units on number of days
2915
* preceding an hh:mm:ss field. - thomas 1998-04-30
2918
DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, fsec_t *fsec)
2920
int is_before = FALSE;
2941
/* read through list backwards to pick up units before values */
2942
for (i = nf - 1; i >= 0; i--)
2947
dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
2956
* Timezone is a token with a leading sign character and
2957
* otherwise the same as a non-signed time field
2959
Assert((*field[i] == '-') || (*field[i] == '+'));
2962
* A single signed number ends up here, but will be
2963
* rejected by DecodeTime(). So, work this out to drop
2964
* through to DTK_NUMBER, which *can* tolerate this.
2967
while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
2970
(DecodeTime(field[i] + 1, fmask, &tmask, tm, fsec) == 0))
2972
if (*field[i] == '-')
2974
/* flip the sign on all fields */
2975
tm->tm_hour = -tm->tm_hour;
2976
tm->tm_min = -tm->tm_min;
2977
tm->tm_sec = -tm->tm_sec;
2982
* Set the next type to be a day, if units are not
2983
* specified. This handles the case of '1 +02:03'
2984
* since we are reading right to left.
2990
else if (type == IGNORE_DTF)
2995
* Got a decimal point? Then assume some sort of
2996
* seconds specification
3000
else if (*cp == '\0')
3003
* Only a signed integer? Then must assume a
3004
* timezone-like usage
3013
val = strtol(field[i], &cp, 10);
3015
if (type == IGNORE_DTF)
3020
fval = strtod(cp, &cp);
3022
return DTERR_BAD_FORMAT;
3024
if (*field[i] == '-')
3027
else if (*cp == '\0')
3030
return DTERR_BAD_FORMAT;
3032
tmask = 0; /* DTK_M(type); */
3037
#ifdef HAVE_INT64_TIMESTAMP
3038
*fsec += (val + fval);
3040
*fsec += ((val + fval) * 1e-6);
3045
#ifdef HAVE_INT64_TIMESTAMP
3046
*fsec += ((val + fval) * 1000);
3048
*fsec += ((val + fval) * 1e-3);
3054
#ifdef HAVE_INT64_TIMESTAMP
3055
*fsec += (fval * 1000000);
3059
tmask = DTK_M(SECOND);
3071
#ifdef HAVE_INT64_TIMESTAMP
3072
*fsec += ((fval - sec) * 1000000);
3074
*fsec += (fval - sec);
3077
tmask = DTK_M(MINUTE);
3089
#ifdef HAVE_INT64_TIMESTAMP
3090
*fsec += ((fval - sec) * 1000000);
3092
*fsec += (fval - sec);
3095
tmask = DTK_M(HOUR);
3107
#ifdef HAVE_INT64_TIMESTAMP
3108
*fsec += ((fval - sec) * 1000000);
3110
*fsec += (fval - sec);
3113
tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
3117
tm->tm_mday += val * 7;
3122
fval *= (7 * 86400);
3125
#ifdef HAVE_INT64_TIMESTAMP
3126
*fsec += ((fval - sec) * 1000000);
3128
*fsec += (fval - sec);
3131
tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
3140
fval *= (30 * 86400);
3143
#ifdef HAVE_INT64_TIMESTAMP
3144
*fsec += ((fval - sec) * 1000000);
3146
*fsec += (fval - sec);
3149
tmask = DTK_M(MONTH);
3155
tm->tm_mon += (fval * 12);
3156
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
3160
tm->tm_year += val * 10;
3162
tm->tm_mon += (fval * 120);
3163
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
3167
tm->tm_year += val * 100;
3169
tm->tm_mon += (fval * 1200);
3170
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
3173
case DTK_MILLENNIUM:
3174
tm->tm_year += val * 1000;
3176
tm->tm_mon += (fval * 12000);
3177
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
3181
return DTERR_BAD_FORMAT;
3187
type = DecodeUnits(i, field[i], &val);
3188
if (type == IGNORE_DTF)
3191
tmask = 0; /* DTK_M(type); */
3204
tmask = (DTK_DATE_M || DTK_TIME_M);
3209
return DTERR_BAD_FORMAT;
3214
return DTERR_BAD_FORMAT;
3218
return DTERR_BAD_FORMAT;
3226
#ifdef HAVE_INT64_TIMESTAMP
3227
sec = (*fsec / INT64CONST(1000000));
3228
*fsec -= (sec * INT64CONST(1000000));
3230
TMODULO(*fsec, sec, 1e0);
3238
tm->tm_sec = -(tm->tm_sec);
3239
tm->tm_min = -(tm->tm_min);
3240
tm->tm_hour = -(tm->tm_hour);
3241
tm->tm_mday = -(tm->tm_mday);
3242
tm->tm_mon = -(tm->tm_mon);
3243
tm->tm_year = -(tm->tm_year);
3246
/* ensure that at least one time field has been found */
3248
return DTERR_BAD_FORMAT;
3255
* Decode text string using lookup table.
3256
* This routine supports time interval decoding.
3259
DecodeUnits(int field, char *lowtoken, int *val)
3264
if ((deltacache[field] != NULL)
3265
&& (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
3266
tp = deltacache[field];
3268
tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3269
deltacache[field] = tp;
3272
type = UNKNOWN_FIELD;
3278
if ((type == TZ) || (type == DTZ))
3285
} /* DecodeUnits() */
3288
* Report an error detected by one of the datetime input processing routines.
3290
* dterr is the error code, str is the original input string, datatype is
3291
* the name of the datatype we were trying to accept.
3293
* Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
3294
* DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
3295
* separate SQLSTATE codes, so ...
3298
DateTimeParseError(int dterr, const char *str, const char *datatype)
3302
case DTERR_FIELD_OVERFLOW:
3304
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
3305
errmsg("date/time field value out of range: \"%s\"",
3308
case DTERR_MD_FIELD_OVERFLOW:
3309
/* <nanny>same as above, but add hint about DateStyle</nanny> */
3311
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
3312
errmsg("date/time field value out of range: \"%s\"",
3314
errhint("Perhaps you need a different \"datestyle\" setting.")));
3316
case DTERR_INTERVAL_OVERFLOW:
3318
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
3319
errmsg("interval field value out of range: \"%s\"",
3322
case DTERR_TZDISP_OVERFLOW:
3324
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
3325
errmsg("time zone displacement out of range: \"%s\"",
3328
case DTERR_BAD_FORMAT:
3331
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3332
errmsg("invalid input syntax for type %s: \"%s\"",
3339
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
3340
* is WAY faster than the generic bsearch().
3343
datebsearch(char *key, datetkn *base, unsigned int nel)
3345
datetkn *last = base + nel - 1,
3349
while (last >= base)
3351
position = base + ((last - base) >> 1);
3352
result = key[0] - position->token[0];
3355
result = strncmp(key, position->token, TOKMAXLEN);
3360
last = position - 1;
3362
base = position + 1;
3369
* Encode date as local time.
3372
EncodeDateOnly(struct pg_tm * tm, int style, char *str)
3374
if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
3380
/* compatible with ISO date formats */
3381
if (tm->tm_year > 0)
3382
sprintf(str, "%04d-%02d-%02d",
3383
tm->tm_year, tm->tm_mon, tm->tm_mday);
3385
sprintf(str, "%04d-%02d-%02d %s",
3386
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
3390
/* compatible with Oracle/Ingres date formats */
3391
if (DateOrder == DATEORDER_DMY)
3392
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3394
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3395
if (tm->tm_year > 0)
3396
sprintf((str + 5), "/%04d", tm->tm_year);
3398
sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
3401
case USE_GERMAN_DATES:
3402
/* German-style date format */
3403
sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3404
if (tm->tm_year > 0)
3405
sprintf((str + 5), ".%04d", tm->tm_year);
3407
sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
3410
case USE_POSTGRES_DATES:
3412
/* traditional date-only style for Postgres */
3413
if (DateOrder == DATEORDER_DMY)
3414
sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
3416
sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
3417
if (tm->tm_year > 0)
3418
sprintf((str + 5), "-%04d", tm->tm_year);
3420
sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
3425
} /* EncodeDateOnly() */
3429
* Encode time fields only.
3432
EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, int *tzp, int style, char *str)
3434
if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
3437
sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
3440
* Print fractional seconds if any. The field widths here should be
3441
* at least equal to the larger of MAX_TIME_PRECISION and
3442
* MAX_TIMESTAMP_PRECISION.
3446
#ifdef HAVE_INT64_TIMESTAMP
3447
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
3449
sprintf((str + strlen(str)), ":%013.10f", tm->tm_sec + fsec);
3451
/* chop off trailing pairs of zeros... */
3452
while ((strcmp((str + strlen(str) - 2), "00") == 0)
3453
&& (*(str + strlen(str) - 3) != '.'))
3454
*(str + strlen(str) - 2) = '\0';
3457
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3464
hour = -(*tzp / 3600);
3465
min = ((abs(*tzp) / 60) % 60);
3466
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3470
} /* EncodeTimeOnly() */
3474
* Encode date and time interpreted as local time.
3475
* Support several date styles:
3476
* Postgres - day mon hh:mm:ss yyyy tz
3477
* SQL - mm/dd/yyyy hh:mm:ss.ss tz
3478
* ISO - yyyy-mm-dd hh:mm:ss+/-tz
3479
* German - dd.mm.yyyy hh:mm:ss tz
3480
* Variants (affects order of month and day for Postgres and SQL styles):
3482
* European - dd/mm/yyyy
3485
EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str)
3492
* Why are we checking only the month field? Change this to an
3493
* assert... if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) return -1;
3495
Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12));
3500
/* Compatible with ISO-8601 date formats */
3502
sprintf(str, "%04d-%02d-%02d %02d:%02d",
3503
((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3504
tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
3507
* Print fractional seconds if any. The field widths here
3508
* should be at least equal to MAX_TIMESTAMP_PRECISION.
3510
* In float mode, don't print fractional seconds before 1 AD,
3511
* since it's unlikely there's any precision left ...
3513
#ifdef HAVE_INT64_TIMESTAMP
3516
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
3517
TrimTrailingZeros(str);
3520
if ((fsec != 0) && (tm->tm_year > 0))
3522
sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
3523
TrimTrailingZeros(str);
3527
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3530
* tzp == NULL indicates that we don't want *any* time zone
3531
* info in the output string. *tzn != NULL indicates that we
3532
* have alpha time zone info available. tm_isdst != -1
3533
* indicates that we have a valid time zone translation.
3535
if ((tzp != NULL) && (tm->tm_isdst >= 0))
3537
hour = -(*tzp / 3600);
3538
min = ((abs(*tzp) / 60) % 60);
3539
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3542
if (tm->tm_year <= 0)
3543
sprintf((str + strlen(str)), " BC");
3547
/* Compatible with Oracle/Ingres date formats */
3549
if (DateOrder == DATEORDER_DMY)
3550
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3552
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3554
sprintf((str + 5), "/%04d %02d:%02d",
3555
((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3556
tm->tm_hour, tm->tm_min);
3559
* Print fractional seconds if any. The field widths here
3560
* should be at least equal to MAX_TIMESTAMP_PRECISION.
3562
* In float mode, don't print fractional seconds before 1 AD,
3563
* since it's unlikely there's any precision left ...
3565
#ifdef HAVE_INT64_TIMESTAMP
3568
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
3569
TrimTrailingZeros(str);
3572
if ((fsec != 0) && (tm->tm_year > 0))
3574
sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
3575
TrimTrailingZeros(str);
3579
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3581
if ((tzp != NULL) && (tm->tm_isdst >= 0))
3584
sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3587
hour = -(*tzp / 3600);
3588
min = ((abs(*tzp) / 60) % 60);
3589
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3593
if (tm->tm_year <= 0)
3594
sprintf((str + strlen(str)), " BC");
3597
case USE_GERMAN_DATES:
3598
/* German variant on European style */
3600
sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3602
sprintf((str + 5), ".%04d %02d:%02d",
3603
((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3604
tm->tm_hour, tm->tm_min);
3607
* Print fractional seconds if any. The field widths here
3608
* should be at least equal to MAX_TIMESTAMP_PRECISION.
3610
* In float mode, don't print fractional seconds before 1 AD,
3611
* since it's unlikely there's any precision left ...
3613
#ifdef HAVE_INT64_TIMESTAMP
3616
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
3617
TrimTrailingZeros(str);
3620
if ((fsec != 0) && (tm->tm_year > 0))
3622
sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
3623
TrimTrailingZeros(str);
3627
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3629
if ((tzp != NULL) && (tm->tm_isdst >= 0))
3632
sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3635
hour = -(*tzp / 3600);
3636
min = ((abs(*tzp) / 60) % 60);
3637
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3641
if (tm->tm_year <= 0)
3642
sprintf((str + strlen(str)), " BC");
3645
case USE_POSTGRES_DATES:
3647
/* Backward-compatible with traditional Postgres abstime dates */
3649
day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3650
tm->tm_wday = j2day(day);
3652
strncpy(str, days[tm->tm_wday], 3);
3653
strcpy((str + 3), " ");
3655
if (DateOrder == DATEORDER_DMY)
3656
sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
3658
sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
3660
sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
3663
* Print fractional seconds if any. The field widths here
3664
* should be at least equal to MAX_TIMESTAMP_PRECISION.
3666
* In float mode, don't print fractional seconds before 1 AD,
3667
* since it's unlikely there's any precision left ...
3669
#ifdef HAVE_INT64_TIMESTAMP
3672
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
3673
TrimTrailingZeros(str);
3676
if ((fsec != 0) && (tm->tm_year > 0))
3678
sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
3679
TrimTrailingZeros(str);
3683
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3685
sprintf((str + strlen(str)), " %04d",
3686
((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)));
3688
if ((tzp != NULL) && (tm->tm_isdst >= 0))
3691
sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3695
* We have a time zone, but no string version. Use the
3696
* numeric form, but be sure to include a leading
3697
* space to avoid formatting something which would be
3698
* rejected by the date/time parser later. - thomas
3701
hour = -(*tzp / 3600);
3702
min = ((abs(*tzp) / 60) % 60);
3703
sprintf((str + strlen(str)), ((min != 0) ? " %+03d:%02d" : " %+03d"), hour, min);
3707
if (tm->tm_year <= 0)
3708
sprintf((str + strlen(str)), " BC");
3717
* Interpret time structure as a delta time and convert to string.
3719
* Support "traditional Postgres" and ISO-8601 styles.
3720
* Actually, afaik ISO does not address time interval formatting,
3721
* but this looks similar to the spec for absolute date/time.
3722
* - thomas 1998-04-30
3725
EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
3727
int is_before = FALSE;
3728
int is_nonzero = FALSE;
3732
* The sign of year and month are guaranteed to match, since they are
3733
* stored internally as "month". But we'll need to check for is_before
3734
* and is_nonzero when determining the signs of hour/minute/seconds
3739
/* compatible with ISO date formats */
3741
if (tm->tm_year != 0)
3743
sprintf(cp, "%d year%s",
3744
tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
3746
is_before = (tm->tm_year < 0);
3750
if (tm->tm_mon != 0)
3752
sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""),
3753
((is_before && (tm->tm_mon > 0)) ? "+" : ""),
3754
tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
3756
is_before = (tm->tm_mon < 0);
3760
if (tm->tm_mday != 0)
3762
sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""),
3763
((is_before && (tm->tm_mday > 0)) ? "+" : ""),
3764
tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
3766
is_before = (tm->tm_mday < 0);
3770
if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0)
3771
|| (tm->tm_sec != 0) || (fsec != 0))
3773
int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
3774
|| (tm->tm_sec < 0) || (fsec < 0));
3776
sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
3777
(minus ? "-" : (is_before ? "+" : "")),
3778
abs(tm->tm_hour), abs(tm->tm_min));
3780
/* Mark as "non-zero" since the fields are now filled in */
3783
/* need fractional seconds? */
3786
#ifdef HAVE_INT64_TIMESTAMP
3787
sprintf(cp, ":%02d", abs(tm->tm_sec));
3789
sprintf(cp, ".%06d", ((fsec >= 0) ? fsec : -(fsec)));
3792
sprintf(cp, ":%013.10f", fabs(fsec));
3794
TrimTrailingZeros(cp);
3799
sprintf(cp, ":%02d", abs(tm->tm_sec));
3805
case USE_POSTGRES_DATES:
3810
if (tm->tm_year != 0)
3812
int year = tm->tm_year;
3814
if (tm->tm_year < 0)
3817
sprintf(cp, "%d year%s", year,
3818
((year != 1) ? "s" : ""));
3820
is_before = (tm->tm_year < 0);
3824
if (tm->tm_mon != 0)
3826
int mon = tm->tm_mon;
3828
if (is_before || ((!is_nonzero) && (tm->tm_mon < 0)))
3831
sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon,
3832
((mon != 1) ? "s" : ""));
3835
is_before = (tm->tm_mon < 0);
3839
if (tm->tm_mday != 0)
3841
int day = tm->tm_mday;
3843
if (is_before || ((!is_nonzero) && (tm->tm_mday < 0)))
3846
sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day,
3847
((day != 1) ? "s" : ""));
3850
is_before = (tm->tm_mday < 0);
3853
if (tm->tm_hour != 0)
3855
int hour = tm->tm_hour;
3857
if (is_before || ((!is_nonzero) && (tm->tm_hour < 0)))
3860
sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour,
3861
((hour != 1) ? "s" : ""));
3864
is_before = (tm->tm_hour < 0);
3868
if (tm->tm_min != 0)
3870
int min = tm->tm_min;
3872
if (is_before || ((!is_nonzero) && (tm->tm_min < 0)))
3875
sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min,
3876
((min != 1) ? "s" : ""));
3879
is_before = (tm->tm_min < 0);
3883
/* fractional seconds? */
3888
#ifdef HAVE_INT64_TIMESTAMP
3890
if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3892
tm->tm_sec = -tm->tm_sec;
3896
else if ((!is_nonzero) && (tm->tm_sec == 0) && (fsec < 0))
3901
sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""),
3902
tm->tm_sec, (((int) sec) / 10000));
3907
if (is_before || ((!is_nonzero) && (fsec < 0)))
3910
sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec);
3913
is_before = (fsec < 0);
3917
/* otherwise, integer seconds only? */
3918
else if (tm->tm_sec != 0)
3920
int sec = tm->tm_sec;
3922
if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3925
sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec,
3926
((sec != 1) ? "s" : ""));
3929
is_before = (tm->tm_sec < 0);
3935
/* identically zero? then put in a unitless zero... */
3942
if (is_before && (style != USE_ISO_DATES))
3949
} /* EncodeInterval() */
3952
/* GUC assign_hook for australian_timezones */
3954
ClearDateCache(bool newval, bool doit, GucSource source)
3960
for (i = 0; i < MAXDATEFIELDS; i++)
3961
datecache[i] = NULL;
3968
* We've been burnt by stupid errors in the ordering of the datetkn tables
3969
* once too often. Arrange to check them during postmaster start.
3972
CheckDateTokenTable(const char *tablename, datetkn *base, unsigned int nel)
3977
for (i = 1; i < nel; i++)
3979
if (strncmp(base[i - 1].token, base[i].token, TOKMAXLEN) >= 0)
3981
elog(LOG, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
3983
TOKMAXLEN, base[i - 1].token,
3984
TOKMAXLEN, base[i].token);
3992
CheckDateTokenTables(void)
3996
Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
3997
Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
3999
ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
4000
ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4001
ok &= CheckDateTokenTable("australian_datetktbl",
4002
australian_datetktbl,
4003
australian_szdatetktbl);