]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/datetime.c
Allow +1300 as a numeric timezone specifier; we already accept FJST as meaning +1300.
[postgresql] / src / backend / utils / adt / datetime.c
1 /*-------------------------------------------------------------------------
2  *
3  * datetime.c
4  *        Support functions for date/time types.
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.97 2002/11/13 17:24:05 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18 #include <math.h>
19 #include <errno.h>
20 #include <float.h>
21 #include <limits.h>
22
23 #include "miscadmin.h"
24 #include "utils/guc.h"
25 #include "utils/datetime.h"
26
27
28 static int DecodeNumber(int flen, char *field,
29                          int fmask, int *tmask,
30                          struct tm * tm, fsec_t *fsec, int *is2digits);
31 static int DecodeNumberField(int len, char *str,
32                                   int fmask, int *tmask,
33                                   struct tm * tm, fsec_t *fsec, int *is2digits);
34 static int DecodeTime(char *str, int fmask, int *tmask,
35                    struct tm * tm, fsec_t *fsec);
36 static int      DecodeTimezone(char *str, int *tzp);
37 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
38 static int      DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
39 static int      DecodePosixTimezone(char *str, int *val);
40 void            TrimTrailingZeros(char *str);
41
42
43 int                     day_tab[2][13] = {
44         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
45 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
46
47 char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
48 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
49
50 char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
51 "Thursday", "Friday", "Saturday", NULL};
52
53
54 /*****************************************************************************
55  *       PRIVATE ROUTINES                                                                                                                *
56  *****************************************************************************/
57
58 /*
59  * Definitions for squeezing values into "value"
60  * We set aside a high bit for a sign, and scale the timezone offsets
61  * in minutes by a factor of 15 (so can represent quarter-hour increments).
62  */
63 #define ABS_SIGNBIT             ((char) 0200)
64 #define VALMASK                 ((char) 0177)
65 #define POS(n)                  (n)
66 #define NEG(n)                  ((n)|ABS_SIGNBIT)
67 #define SIGNEDCHAR(c)   ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
68 #define FROMVAL(tp)             (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
69 #define TOVAL(tp, v)    ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
70
71 /*
72  * datetktbl holds date/time keywords. Note that this table must be strictly
73  * ordered to allow an O(ln(N)) search algorithm.
74  *
75  * The text field is not guaranteed to be NULL-terminated.
76  *
77  * To keep this table reasonably small, we divide the lexval for TZ and DTZ
78  * entries by 15 (so they are on 15 minute boundaries) and truncate the text
79  * field at MAXTOKLEN characters.
80  * Formerly, we divided by 10 rather than 15 but there are a few time zones
81  * which are 30 or 45 minutes away from an even hour, most are on an hour
82  * boundary, and none on other boundaries.
83  *
84  * Let's include all strings from my current zinc time zone database.
85  * Not all of them are unique, or even very understandable, so we will
86  * leave some commented out for now.
87  */
88 static datetkn datetktbl[] = {
89 /*      text, token, lexval */
90         {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
91         {"acsst", DTZ, POS(42)},        /* Cent. Australia */
92         {"acst", DTZ, NEG(16)},         /* Atlantic/Porto Acre */
93         {"act", TZ, NEG(20)},           /* Atlantic/Porto Acre */
94         {DA_D, ADBC, AD},                       /* "ad" for years >= 0 */
95         {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
96         {"adt", DTZ, NEG(12)},          /* Atlantic Daylight Time */
97         {"aesst", DTZ, POS(44)},        /* E. Australia */
98         {"aest", TZ, POS(40)},          /* Australia Eastern Std Time */
99         {"aft", TZ, POS(18)},           /* Kabul */
100         {"ahst", TZ, NEG(40)},          /* Alaska-Hawaii Std Time */
101         {"akdt", DTZ, NEG(32)},         /* Alaska Daylight Time */
102         {"akst", DTZ, NEG(36)},         /* Alaska Standard Time */
103         {"allballs", RESERV, DTK_ZULU},         /* 00:00:00 */
104         {"almt", TZ, POS(24)},          /* Almaty Time */
105         {"almst", TZ, POS(28)},         /* Almaty Savings Time */
106         {"am", AMPM, AM},
107         {"amst", DTZ, POS(20)},         /* Armenia Summer Time (Yerevan) */
108         {"amt", TZ, POS(16)},           /* Armenia Time (Yerevan) */
109 #if 0
110         {"amst", DTZ, NEG(12)},         /* Porto Velho */
111 #endif
112         {"anast", DTZ, POS(52)},        /* Anadyr Summer Time (Russia) */
113         {"anat", TZ, POS(48)},          /* Anadyr Time (Russia) */
114 #if 0
115         aqtst
116         aqtt
117         arst
118 #endif
119         {"art", TZ, NEG(12)},           /* Argentina Time */
120 #if 0
121         ashst
122         ast                                                     /* Atlantic Standard Time, Arabia Standard
123                                                                  * Time, Acre Standard Time */
124 #endif
125         {"apr", MONTH, 4},
126         {"april", MONTH, 4},
127         {"ast", TZ, NEG(16)},           /* Atlantic Std Time (Canada) */
128         {"at", IGNORE_DTF, 0},          /* "at" (throwaway) */
129         {"aug", MONTH, 8},
130         {"august", MONTH, 8},
131         {"awsst", DTZ, POS(36)},        /* W. Australia */
132         {"awst", TZ, POS(32)},          /* W. Australia */
133         {"awt", DTZ, NEG(12)},
134         {"azost", DTZ, POS(0)},         /* Azores Summer Time */
135         {"azot", TZ, NEG(4)},           /* Azores Time */
136         {"azst", DTZ, POS(20)},         /* Azerbaijan Summer Time */
137         {"azt", TZ, POS(16)},           /* Azerbaijan Time */
138         {DB_C, ADBC, BC},                       /* "bc" for years < 0 */
139         {"bdst", TZ, POS(8)},           /* British Double Summer Time */
140         {"bdt", TZ, POS(24)},           /* Dacca */
141         {"bnt", TZ, POS(32)},           /* Brunei Darussalam Time */
142         {"bort", TZ, POS(32)},          /* Borneo Time (Indonesia) */
143 #if 0
144         bortst
145         bost
146 #endif
147         {"bot", TZ, NEG(16)},           /* Bolivia Time */
148         {"bra", TZ, NEG(12)},           /* Brazil Time */
149 #if 0
150         brst
151         brt
152 #endif
153         {"bst", DTZ, POS(4)},           /* British Summer Time */
154 #if 0
155         {"bst", TZ, NEG(12)},           /* Brazil Standard Time */
156         {"bst", DTZ, NEG(44)},          /* Bering Summer Time */
157 #endif
158         {"bt", TZ, POS(12)},            /* Baghdad Time */
159         {"btt", TZ, POS(24)},           /* Bhutan Time */
160         {"cadt", DTZ, POS(42)},         /* Central Australian DST */
161         {"cast", TZ, POS(38)},          /* Central Australian ST */
162         {"cat", TZ, NEG(40)},           /* Central Alaska Time */
163         {"cct", TZ, POS(32)},           /* China Coast Time */
164 #if 0
165         {"cct", TZ, POS(26)},           /* Indian Cocos (Island) Time */
166 #endif
167         {"cdt", DTZ, NEG(20)},          /* Central Daylight Time */
168         {"cest", DTZ, POS(8)},          /* Central European Dayl.Time */
169         {"cet", TZ, POS(4)},            /* Central European Time */
170         {"cetdst", DTZ, POS(8)},        /* Central European Dayl.Time */
171         {"chadt", DTZ, POS(55)},        /* Chatham Island Daylight Time (13:45) */
172         {"chast", TZ, POS(51)},         /* Chatham Island Time (12:45) */
173 #if 0
174         ckhst
175 #endif
176         {"ckt", TZ, POS(48)},           /* Cook Islands Time */
177         {"clst", DTZ, NEG(12)},         /* Chile Summer Time */
178         {"clt", TZ, NEG(16)},           /* Chile Time */
179 #if 0
180         cost
181 #endif
182         {"cot", TZ, NEG(20)},           /* Columbia Time */
183         {"cst", TZ, NEG(24)},           /* Central Standard Time */
184 #if 0
185         cvst
186 #endif
187         {"cvt", TZ, POS(28)},           /* Christmas Island Time (Indian Ocean) */
188         {"cxt", TZ, POS(28)},           /* Christmas Island Time (Indian Ocean) */
189         {DCURRENT, RESERV, DTK_CURRENT},        /* "current" is always now */
190         {"d", UNITS, DTK_DAY},          /* "day of month" for ISO input */
191         {"davt", TZ, POS(28)},          /* Davis Time (Antarctica) */
192         {"ddut", TZ, POS(40)},          /* Dumont-d'Urville Time (Antarctica) */
193         {"dec", MONTH, 12},
194         {"december", MONTH, 12},
195         {"dnt", TZ, POS(4)},            /* Dansk Normal Tid */
196         {"dow", RESERV, DTK_DOW},       /* day of week */
197         {"doy", RESERV, DTK_DOY},       /* day of year */
198         {"dst", DTZMOD, 6},
199 #if 0
200         {"dusst", DTZ, POS(24)},        /* Dushanbe Summer Time */
201 #endif
202         {"easst", DTZ, NEG(20)},        /* Easter Island Summer Time */
203         {"east", TZ, NEG(24)},          /* Easter Island Time */
204         {"eat", TZ, POS(12)},           /* East Africa Time */
205 #if 0
206         {"east", DTZ, POS(16)},         /* Indian Antananarivo Savings Time */
207         {"eat", TZ, POS(12)},           /* Indian Antananarivo Time */
208         {"ect", TZ, NEG(16)},           /* Eastern Caribbean Time */
209         {"ect", TZ, NEG(20)},           /* Ecuador Time */
210 #endif
211         {"edt", DTZ, NEG(16)},          /* Eastern Daylight Time */
212         {"eest", DTZ, POS(12)},         /* Eastern Europe Summer Time */
213         {"eet", TZ, POS(8)},            /* East. Europe, USSR Zone 1 */
214         {"eetdst", DTZ, POS(12)},       /* Eastern Europe Daylight Time */
215         {"egst", DTZ, POS(0)},          /* East Greenland Summer Time */
216         {"egt", TZ, NEG(4)},            /* East Greenland Time */
217 #if 0
218         ehdt
219 #endif
220         {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
221         {"est", TZ, NEG(20)},           /* Eastern Standard Time */
222         {"feb", MONTH, 2},
223         {"february", MONTH, 2},
224         {"fjst", DTZ, NEG(52)},         /* Fiji Summer Time (13 hour offset!) */
225         {"fjt", TZ, NEG(48)},           /* Fiji Time */
226         {"fkst", DTZ, NEG(12)},         /* Falkland Islands Summer Time */
227         {"fkt", TZ, NEG(8)},            /* Falkland Islands Time */
228 #if 0
229         fnst
230         fnt
231 #endif
232         {"fri", DOW, 5},
233         {"friday", DOW, 5},
234         {"fst", TZ, POS(4)},            /* French Summer Time */
235         {"fwt", DTZ, POS(8)},           /* French Winter Time  */
236         {"galt", TZ, NEG(24)},          /* Galapagos Time */
237         {"gamt", TZ, NEG(36)},          /* Gambier Time */
238         {"gest", DTZ, POS(20)},         /* Georgia Summer Time */
239         {"get", TZ, POS(16)},           /* Georgia Time */
240         {"gft", TZ, NEG(12)},           /* French Guiana Time */
241 #if 0
242         ghst
243 #endif
244         {"gilt", TZ, POS(48)},          /* Gilbert Islands Time */
245         {"gmt", TZ, POS(0)},            /* Greenwish Mean Time */
246         {"gst", TZ, POS(40)},           /* Guam Std Time, USSR Zone 9 */
247         {"gyt", TZ, NEG(16)},           /* Guyana Time */
248         {"h", UNITS, DTK_HOUR},         /* "hour" */
249 #if 0
250         hadt
251         hast
252 #endif
253         {"hdt", DTZ, NEG(36)},          /* Hawaii/Alaska Daylight Time */
254 #if 0
255         hkst
256 #endif
257         {"hkt", TZ, POS(32)},           /* Hong Kong Time */
258 #if 0
259         {"hmt", TZ, POS(12)},           /* Hellas ? ? */
260         hovst
261         hovt
262 #endif
263         {"hst", TZ, NEG(40)},           /* Hawaii Std Time */
264 #if 0
265         hwt
266 #endif
267         {"ict", TZ, POS(28)},           /* Indochina Time */
268         {"idle", TZ, POS(48)},          /* Intl. Date Line, East */
269         {"idlw", TZ, NEG(48)},          /* Intl. Date Line, West */
270 #if 0
271         idt                                                     /* Israeli, Iran, Indian Daylight Time */
272 #endif
273         {LATE, RESERV, DTK_LATE},       /* "infinity" reserved for "late time" */
274         {INVALID, RESERV, DTK_INVALID},         /* "invalid" reserved for bad time */
275         {"iot", TZ, POS(20)},           /* Indian Chagos Time */
276         {"irkst", DTZ, POS(36)},        /* Irkutsk Summer Time */
277         {"irkt", TZ, POS(32)},          /* Irkutsk Time */
278         {"irt", TZ, POS(14)},           /* Iran Time */
279 #if 0
280         isst
281 #endif
282         {"ist", TZ, POS(8)},            /* Israel */
283         {"it", TZ, POS(14)},            /* Iran Time */
284         {"j", UNITS, DTK_JULIAN},
285         {"jan", MONTH, 1},
286         {"january", MONTH, 1},
287         {"javt", TZ, POS(28)},          /* Java Time (07:00? see JT) */
288         {"jayt", TZ, POS(36)},          /* Jayapura Time (Indonesia) */
289         {"jd", UNITS, DTK_JULIAN},
290         {"jst", TZ, POS(36)},           /* Japan Std Time,USSR Zone 8 */
291         {"jt", TZ, POS(30)},            /* Java Time (07:30? see JAVT) */
292         {"jul", MONTH, 7},
293         {"julian", UNITS, DTK_JULIAN},
294         {"july", MONTH, 7},
295         {"jun", MONTH, 6},
296         {"june", MONTH, 6},
297         {"kdt", DTZ, POS(40)},          /* Korea Daylight Time */
298         {"kgst", DTZ, POS(24)},         /* Kyrgyzstan Summer Time */
299         {"kgt", TZ, POS(20)},           /* Kyrgyzstan Time */
300         {"kost", TZ, POS(48)},          /* Kosrae Time */
301         {"krast", DTZ, POS(28)},        /* Krasnoyarsk Summer Time */
302         {"krat", TZ, POS(32)},          /* Krasnoyarsk Standard Time */
303         {"kst", TZ, POS(36)},           /* Korea Standard Time */
304         {"lhdt", DTZ, POS(44)},         /* Lord Howe Daylight Time, Australia */
305         {"lhst", TZ, POS(42)},          /* Lord Howe Standard Time, Australia */
306         {"ligt", TZ, POS(40)},          /* From Melbourne, Australia */
307         {"lint", TZ, POS(56)},          /* Line Islands Time (Kiribati; +14
308                                                                  * hours!) */
309         {"lkt", TZ, POS(24)},           /* Lanka Time */
310         {"m", UNITS, DTK_MONTH},        /* "month" for ISO input */
311         {"magst", DTZ, POS(48)},        /* Magadan Summer Time */
312         {"magt", TZ, POS(44)},          /* Magadan Time */
313         {"mar", MONTH, 3},
314         {"march", MONTH, 3},
315         {"mart", TZ, NEG(38)},          /* Marquesas Time */
316         {"mawt", TZ, POS(24)},          /* Mawson, Antarctica */
317         {"may", MONTH, 5},
318         {"mdt", DTZ, NEG(24)},          /* Mountain Daylight Time */
319         {"mest", DTZ, POS(8)},          /* Middle Europe Summer Time */
320         {"met", TZ, POS(4)},            /* Middle Europe Time */
321         {"metdst", DTZ, POS(8)},        /* Middle Europe Daylight Time */
322         {"mewt", TZ, POS(4)},           /* Middle Europe Winter Time */
323         {"mez", TZ, POS(4)},            /* Middle Europe Zone */
324         {"mht", TZ, POS(48)},           /* Kwajalein */
325         {"mm", UNITS, DTK_MINUTE},      /* "minute" for ISO input */
326         {"mmt", TZ, POS(26)},           /* Myannar Time */
327         {"mon", DOW, 1},
328         {"monday", DOW, 1},
329 #if 0
330         most
331 #endif
332         {"mpt", TZ, POS(40)},           /* North Mariana Islands Time */
333         {"msd", DTZ, POS(16)},          /* Moscow Summer Time */
334         {"msk", TZ, POS(12)},           /* Moscow Time */
335         {"mst", TZ, NEG(28)},           /* Mountain Standard Time */
336         {"mt", TZ, POS(34)},            /* Moluccas Time */
337         {"mut", TZ, POS(16)},           /* Mauritius Island Time */
338         {"mvt", TZ, POS(20)},           /* Maldives Island Time */
339         {"myt", TZ, POS(32)},           /* Malaysia Time */
340 #if 0
341         ncst
342 #endif
343         {"nct", TZ, POS(44)},           /* New Caledonia Time */
344         {"ndt", DTZ, NEG(10)},          /* Nfld. Daylight Time */
345         {"nft", TZ, NEG(14)},           /* Newfoundland Standard Time */
346         {"nor", TZ, POS(4)},            /* Norway Standard Time */
347         {"nov", MONTH, 11},
348         {"november", MONTH, 11},
349         {"novst", DTZ, POS(28)},        /* Novosibirsk Summer Time */
350         {"novt", TZ, POS(24)},          /* Novosibirsk Standard Time */
351         {NOW, RESERV, DTK_NOW},         /* current transaction time */
352         {"npt", TZ, POS(23)},           /* Nepal Standard Time (GMT-5:45) */
353         {"nst", TZ, NEG(14)},           /* Nfld. Standard Time */
354         {"nt", TZ, NEG(44)},            /* Nome Time */
355         {"nut", TZ, NEG(44)},           /* Niue Time */
356         {"nzdt", DTZ, POS(52)},         /* New Zealand Daylight Time */
357         {"nzst", TZ, POS(48)},          /* New Zealand Standard Time */
358         {"nzt", TZ, POS(48)},           /* New Zealand Time */
359         {"oct", MONTH, 10},
360         {"october", MONTH, 10},
361         {"omsst", DTZ, POS(28)},        /* Omsk Summer Time */
362         {"omst", TZ, POS(24)},          /* Omsk Time */
363         {"on", IGNORE_DTF, 0},          /* "on" (throwaway) */
364         {"pdt", DTZ, NEG(28)},          /* Pacific Daylight Time */
365 #if 0
366         pest
367 #endif
368         {"pet", TZ, NEG(20)},           /* Peru Time */
369         {"petst", DTZ, POS(52)},        /* Petropavlovsk-Kamchatski Summer Time */
370         {"pett", TZ, POS(48)},          /* Petropavlovsk-Kamchatski Time */
371         {"pgt", TZ, POS(40)},           /* Papua New Guinea Time */
372         {"phot", TZ, POS(52)},          /* Phoenix Islands (Kiribati) Time */
373 #if 0
374         phst
375 #endif
376         {"pht", TZ, POS(32)},           /* Phillipine Time */
377         {"pkt", TZ, POS(20)},           /* Pakistan Time */
378         {"pm", AMPM, PM},
379         {"pmdt", DTZ, NEG(8)},          /* Pierre & Miquelon Daylight Time */
380 #if 0
381         pmst
382 #endif
383         {"pont", TZ, POS(44)},          /* Ponape Time (Micronesia) */
384         {"pst", TZ, NEG(32)},           /* Pacific Standard Time */
385         {"pwt", TZ, POS(36)},           /* Palau Time */
386         {"pyst", DTZ, NEG(12)},         /* Paraguay Summer Time */
387         {"pyt", TZ, NEG(16)},           /* Paraguay Time */
388         {"ret", DTZ, POS(16)},          /* Reunion Island Time */
389         {"s", UNITS, DTK_SECOND},       /* "seconds" for ISO input */
390         {"sadt", DTZ, POS(42)},         /* S. Australian Dayl. Time */
391 #if 0
392         samst
393         samt
394 #endif
395         {"sast", TZ, POS(38)},          /* South Australian Std Time */
396         {"sat", DOW, 6},
397         {"saturday", DOW, 6},
398 #if 0
399         sbt
400 #endif
401         {"sct", DTZ, POS(16)},          /* Mahe Island Time */
402         {"sep", MONTH, 9},
403         {"sept", MONTH, 9},
404         {"september", MONTH, 9},
405         {"set", TZ, NEG(4)},            /* Seychelles Time ?? */
406 #if 0
407         sgt
408 #endif
409         {"sst", DTZ, POS(8)},           /* Swedish Summer Time */
410         {"sun", DOW, 0},
411         {"sunday", DOW, 0},
412         {"swt", TZ, POS(4)},            /* Swedish Winter Time */
413 #if 0
414         syot
415 #endif
416         {"t", ISOTIME, DTK_TIME},       /* Filler for ISO time fields */
417         {"that", TZ, NEG(40)},          /* Tahiti Time */
418         {"tft", TZ, POS(20)},           /* Kerguelen Time */
419         {"thu", DOW, 4},
420         {"thur", DOW, 4},
421         {"thurs", DOW, 4},
422         {"thursday", DOW, 4},
423         {"tjt", TZ, POS(20)},           /* Tajikistan Time */
424         {"tkt", TZ, NEG(40)},           /* Tokelau Time */
425         {"tmt", TZ, POS(20)},           /* Turkmenistan Time */
426         {TODAY, RESERV, DTK_TODAY}, /* midnight */
427         {TOMORROW, RESERV, DTK_TOMORROW},       /* tomorrow midnight */
428 #if 0
429         tost
430 #endif
431         {"tot", TZ, POS(52)},           /* Tonga Time */
432 #if 0
433         tpt
434 #endif
435         {"truk", TZ, POS(40)},          /* Truk Time */
436         {"tue", DOW, 2},
437         {"tues", DOW, 2},
438         {"tuesday", DOW, 2},
439         {"tvt", TZ, POS(48)},           /* Tuvalu Time */
440 #if 0
441         uct
442 #endif
443         {"ulast", DTZ, POS(36)},        /* Ulan Bator Summer Time */
444         {"ulat", TZ, POS(32)},          /* Ulan Bator Time */
445         {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
446         {"ut", TZ, POS(0)},
447         {"utc", TZ, POS(0)},
448         {"uyst", DTZ, NEG(8)},          /* Uruguay Summer Time */
449         {"uyt", TZ, NEG(12)},           /* Uruguay Time */
450         {"uzst", DTZ, POS(24)},         /* Uzbekistan Summer Time */
451         {"uzt", TZ, POS(20)},           /* Uzbekistan Time */
452         {"vet", TZ, NEG(16)},           /* Venezuela Time */
453         {"vlast", DTZ, POS(44)},        /* Vladivostok Summer Time */
454         {"vlat", TZ, POS(40)},          /* Vladivostok Time */
455 #if 0
456         vust
457 #endif
458         {"vut", TZ, POS(44)},           /* Vanuata Time */
459         {"wadt", DTZ, POS(32)},         /* West Australian DST */
460         {"wakt", TZ, POS(48)},          /* Wake Time */
461 #if 0
462         warst
463 #endif
464         {"wast", TZ, POS(28)},          /* West Australian Std Time */
465         {"wat", TZ, NEG(4)},            /* West Africa Time */
466         {"wdt", DTZ, POS(36)},          /* West Australian DST */
467         {"wed", DOW, 3},
468         {"wednesday", DOW, 3},
469         {"weds", DOW, 3},
470         {"west", DTZ, POS(4)},          /* Western Europe Summer Time */
471         {"wet", TZ, POS(0)},            /* Western Europe */
472         {"wetdst", DTZ, POS(4)},        /* Western Europe Daylight Savings Time */
473         {"wft", TZ, POS(48)},           /* Wallis and Futuna Time */
474         {"wgst", DTZ, NEG(8)},          /* West Greenland Summer Time */
475         {"wgt", TZ, NEG(12)},           /* West Greenland Time */
476         {"wst", TZ, POS(32)},           /* West Australian Standard Time */
477         {"y", UNITS, DTK_YEAR},         /* "year" for ISO input */
478         {"yakst", DTZ, POS(40)},        /* Yakutsk Summer Time */
479         {"yakt", TZ, POS(36)},          /* Yakutsk Time */
480         {"yapt", TZ, POS(40)},          /* Yap Time (Micronesia) */
481         {"ydt", DTZ, NEG(32)},          /* Yukon Daylight Time */
482         {"yekst", DTZ, POS(24)},        /* Yekaterinburg Summer Time */
483         {"yekt", TZ, POS(20)},          /* Yekaterinburg Time */
484         {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
485         {"yst", TZ, NEG(36)},           /* Yukon Standard Time */
486         {"z", TZ, POS(0)},                      /* time zone tag per ISO-8601 */
487         {"zp4", TZ, NEG(16)},           /* UTC +4  hours. */
488         {"zp5", TZ, NEG(20)},           /* UTC +5  hours. */
489         {"zp6", TZ, NEG(24)},           /* UTC +6  hours. */
490         {ZULU, TZ, POS(0)},                     /* UTC */
491 };
492
493 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
494
495 /* Used for SET australian_timezones to override North American ones */
496 static datetkn australian_datetktbl[] = {
497         {"acst", TZ, POS(38)},          /* Cent. Australia */
498         {"cst", TZ, POS(42)},           /* Australia Central Std Time */
499         {"east", TZ, POS(40)},          /* East Australian Std Time */
500         {"est", TZ, POS(40)},           /* Australia Eastern Std Time */
501         {"sat", TZ, POS(38)},
502 };
503
504 static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
505 sizeof australian_datetktbl[0];
506
507 static datetkn deltatktbl[] = {
508         /* text, token, lexval */
509         {"@", IGNORE_DTF, 0},           /* postgres relative prefix */
510         {DAGO, AGO, 0},                         /* "ago" indicates negative time offset */
511         {"c", UNITS, DTK_CENTURY},      /* "century" relative */
512         {"cent", UNITS, DTK_CENTURY},           /* "century" relative */
513         {"centuries", UNITS, DTK_CENTURY},      /* "centuries" relative */
514         {DCENTURY, UNITS, DTK_CENTURY},         /* "century" relative */
515         {"d", UNITS, DTK_DAY},          /* "day" relative */
516         {DDAY, UNITS, DTK_DAY},         /* "day" relative */
517         {"days", UNITS, DTK_DAY},       /* "days" relative */
518         {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
519         {"decs", UNITS, DTK_DECADE},    /* "decades" relative */
520         {DDECADE, UNITS, DTK_DECADE},           /* "decade" relative */
521         {"decades", UNITS, DTK_DECADE},         /* "decades" relative */
522         {"h", UNITS, DTK_HOUR},         /* "hour" relative */
523         {DHOUR, UNITS, DTK_HOUR},       /* "hour" relative */
524         {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
525         {"hr", UNITS, DTK_HOUR},        /* "hour" relative */
526         {"hrs", UNITS, DTK_HOUR},       /* "hours" relative */
527         {INVALID, RESERV, DTK_INVALID},         /* reserved for invalid time */
528         {"m", UNITS, DTK_MINUTE},       /* "minute" relative */
529         {"microsecon", UNITS, DTK_MICROSEC},            /* "microsecond" relative */
530         {"mil", UNITS, DTK_MILLENNIUM},         /* "millennium" relative */
531         {"millennia", UNITS, DTK_MILLENNIUM},           /* "millennia" relative */
532         {DMILLENNIUM, UNITS, DTK_MILLENNIUM},           /* "millennium" relative */
533         {"millisecon", UNITS, DTK_MILLISEC},            /* relative */
534         {"mils", UNITS, DTK_MILLENNIUM},        /* "millennia" relative */
535         {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
536         {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
537         {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
538         {DMINUTE, UNITS, DTK_MINUTE},           /* "minute" relative */
539         {"minutes", UNITS, DTK_MINUTE},         /* "minutes" relative */
540         {"mon", UNITS, DTK_MONTH},      /* "months" relative */
541         {"mons", UNITS, DTK_MONTH}, /* "months" relative */
542         {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
543         {"months", UNITS, DTK_MONTH},
544         {"ms", UNITS, DTK_MILLISEC},
545         {"msec", UNITS, DTK_MILLISEC},
546         {DMILLISEC, UNITS, DTK_MILLISEC},
547         {"mseconds", UNITS, DTK_MILLISEC},
548         {"msecs", UNITS, DTK_MILLISEC},
549         {"qtr", UNITS, DTK_QUARTER},    /* "quarter" relative */
550         {DQUARTER, UNITS, DTK_QUARTER},         /* "quarter" relative */
551         {"reltime", IGNORE_DTF, 0}, /* pre-v6.1 "Undefined Reltime" */
552         {"s", UNITS, DTK_SECOND},
553         {"sec", UNITS, DTK_SECOND},
554         {DSECOND, UNITS, DTK_SECOND},
555         {"seconds", UNITS, DTK_SECOND},
556         {"secs", UNITS, DTK_SECOND},
557         {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
558         {"timezone", UNITS, DTK_TZ},    /* "timezone" time offset */
559         {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
560         {"timezone_m", UNITS, DTK_TZ_MINUTE},           /* timezone minutes units */
561         {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
562         {"us", UNITS, DTK_MICROSEC},    /* "microsecond" relative */
563         {"usec", UNITS, DTK_MICROSEC},          /* "microsecond" relative */
564         {DMICROSEC, UNITS, DTK_MICROSEC},       /* "microsecond" relative */
565         {"useconds", UNITS, DTK_MICROSEC},      /* "microseconds" relative */
566         {"usecs", UNITS, DTK_MICROSEC},         /* "microseconds" relative */
567         {"w", UNITS, DTK_WEEK},         /* "week" relative */
568         {DWEEK, UNITS, DTK_WEEK},       /* "week" relative */
569         {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
570         {"y", UNITS, DTK_YEAR},         /* "year" relative */
571         {DYEAR, UNITS, DTK_YEAR},       /* "year" relative */
572         {"years", UNITS, DTK_YEAR}, /* "years" relative */
573         {"yr", UNITS, DTK_YEAR},        /* "year" relative */
574         {"yrs", UNITS, DTK_YEAR},       /* "years" relative */
575 };
576
577 static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
578
579 datetkn    *datecache[MAXDATEFIELDS] = {NULL};
580
581 datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
582
583
584 /*
585  * Calendar time to Julian date conversions.
586  * Julian date is commonly used in astronomical applications,
587  *      since it is numerically accurate and computationally simple.
588  * The algorithms here will accurately convert between Julian day
589  *      and calendar date for all non-negative Julian days
590  *      (i.e. from Nov 23, -4713 on).
591  *
592  * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
593  *      University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
594  *
595  * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
596  *      now at Aerospace Corp. (hi, Henry!)
597  *
598  * These routines will be used by other date/time packages
599  * - thomas 97/02/25
600  */
601
602 int
603 date2j(int y, int m, int d)
604 {
605         int                     m12 = (m - 14) / 12;
606
607         return ((1461 * (y + 4800 + m12)) / 4
608                         + (367 * (m - 2 - 12 * (m12))) / 12
609                         - (3 * ((y + 4900 + m12) / 100)) / 4
610                         + d - 32075);
611 }       /* date2j() */
612
613 void
614 j2date(int jd, int *year, int *month, int *day)
615 {
616         int                     j,
617                                 y,
618                                 m,
619                                 d;
620
621         int                     i,
622                                 l,
623                                 n;
624
625         l = jd + 68569;
626         n = (4 * l) / 146097;
627         l -= (146097 * n + 3) / 4;
628         i = (4000 * (l + 1)) / 1461001;
629         l += 31 - (1461 * i) / 4;
630         j = (80 * l) / 2447;
631         d = l - (2447 * j) / 80;
632         l = j / 11;
633         m = (j + 2) - (12 * l);
634         y = 100 * (n - 49) + i + l;
635
636         *year = y;
637         *month = m;
638         *day = d;
639         return;
640 }       /* j2date() */
641
642 int
643 j2day(int date)
644 {
645         int                     day;
646
647         day = (date + 1) % 7;
648
649         return day;
650 }       /* j2day() */
651
652
653 /* TrimTrailingZeros()
654  * ... resulting from printing numbers with full precision.
655  */
656 void
657 TrimTrailingZeros(char *str)
658 {
659         int                     len = strlen(str);
660
661 #if 0
662         /* chop off trailing one to cope with interval rounding */
663         if (strcmp((str + len - 4), "0001") == 0)
664         {
665                 len -= 4;
666                 *(str + len) = '\0';
667         }
668 #endif
669
670         /* chop off trailing zeros... */
671         while ((*(str + len - 1) == '0')
672                    && (*(str + len - 3) != '.'))
673         {
674                 len--;
675                 *(str + len) = '\0';
676         }
677         return;
678 }
679
680
681 /* ParseDateTime()
682  * Break string into tokens based on a date/time context.
683  * Several field types are assigned:
684  *      DTK_NUMBER - digits and (possibly) a decimal point
685  *      DTK_DATE - digits and two delimiters, or digits and text
686  *      DTK_TIME - digits, colon delimiters, and possibly a decimal point
687  *      DTK_STRING - text (no digits)
688  *      DTK_SPECIAL - leading "+" or "-" followed by text
689  *      DTK_TZ - leading "+" or "-" followed by digits
690  * Note that some field types can hold unexpected items:
691  *      DTK_NUMBER can hold date fields (yy.ddd)
692  *      DTK_STRING can hold months (January) and time zones (PST)
693  *      DTK_DATE can hold Posix time zones (GMT-8)
694  */
695 int
696 ParseDateTime(char *timestr, char *lowstr,
697                           char **field, int *ftype, int maxfields, int *numfields)
698 {
699         int                     nf = 0;
700         char       *cp = timestr;
701         char       *lp = lowstr;
702
703         /* outer loop through fields */
704         while (*cp != '\0')
705         {
706                 field[nf] = lp;
707
708                 /* leading digit? then date or time */
709                 if (isdigit((unsigned char) *cp))
710                 {
711                         *lp++ = *cp++;
712                         while (isdigit((unsigned char) *cp))
713                                 *lp++ = *cp++;
714
715                         /* time field? */
716                         if (*cp == ':')
717                         {
718                                 ftype[nf] = DTK_TIME;
719                                 *lp++ = *cp++;
720                                 while (isdigit((unsigned char) *cp) ||
721                                            (*cp == ':') || (*cp == '.'))
722                                         *lp++ = *cp++;
723                         }
724                         /* date field? allow embedded text month */
725                         else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
726                         {
727                                 /* save delimiting character to use later */
728                                 char       *dp = cp;
729
730                                 *lp++ = *cp++;
731                                 /* second field is all digits? then no embedded text month */
732                                 if (isdigit((unsigned char) *cp))
733                                 {
734                                         ftype[nf] = ((*dp == '.') ? DTK_NUMBER : DTK_DATE);
735                                         while (isdigit((unsigned char) *cp))
736                                                 *lp++ = *cp++;
737
738                                         /*
739                                          * insist that the delimiters match to get a
740                                          * three-field date.
741                                          */
742                                         if (*cp == *dp)
743                                         {
744                                                 ftype[nf] = DTK_DATE;
745                                                 *lp++ = *cp++;
746                                                 while (isdigit((unsigned char) *cp) || (*cp == *dp))
747                                                         *lp++ = *cp++;
748                                         }
749                                 }
750                                 else
751                                 {
752                                         ftype[nf] = DTK_DATE;
753                                         while (isalnum((unsigned char) *cp) || (*cp == *dp))
754                                                 *lp++ = tolower((unsigned char) *cp++);
755                                 }
756                         }
757
758                         /*
759                          * otherwise, number only and will determine year, month, day,
760                          * or concatenated fields later...
761                          */
762                         else
763                                 ftype[nf] = DTK_NUMBER;
764                 }
765                 /* Leading decimal point? Then fractional seconds... */
766                 else if (*cp == '.')
767                 {
768                         *lp++ = *cp++;
769                         while (isdigit((unsigned char) *cp))
770                                 *lp++ = *cp++;
771
772                         ftype[nf] = DTK_NUMBER;
773                 }
774
775                 /*
776                  * text? then date string, month, day of week, special, or
777                  * timezone
778                  */
779                 else if (isalpha((unsigned char) *cp))
780                 {
781                         ftype[nf] = DTK_STRING;
782                         *lp++ = tolower((unsigned char) *cp++);
783                         while (isalpha((unsigned char) *cp))
784                                 *lp++ = tolower((unsigned char) *cp++);
785
786                         /*
787                          * Full date string with leading text month? Could also be a
788                          * POSIX time zone...
789                          */
790                         if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
791                         {
792                                 char       *dp = cp;
793
794                                 ftype[nf] = DTK_DATE;
795                                 *lp++ = *cp++;
796                                 while (isdigit((unsigned char) *cp) || (*cp == *dp))
797                                         *lp++ = *cp++;
798                         }
799                 }
800                 /* skip leading spaces */
801                 else if (isspace((unsigned char) *cp))
802                 {
803                         cp++;
804                         continue;
805                 }
806                 /* sign? then special or numeric timezone */
807                 else if ((*cp == '+') || (*cp == '-'))
808                 {
809                         *lp++ = *cp++;
810                         /* soak up leading whitespace */
811                         while (isspace((unsigned char) *cp))
812                                 cp++;
813                         /* numeric timezone? */
814                         if (isdigit((unsigned char) *cp))
815                         {
816                                 ftype[nf] = DTK_TZ;
817                                 *lp++ = *cp++;
818                                 while (isdigit((unsigned char) *cp) ||
819                                            (*cp == ':') || (*cp == '.'))
820                                         *lp++ = *cp++;
821                         }
822                         /* special? */
823                         else if (isalpha((unsigned char) *cp))
824                         {
825                                 ftype[nf] = DTK_SPECIAL;
826                                 *lp++ = tolower((unsigned char) *cp++);
827                                 while (isalpha((unsigned char) *cp))
828                                         *lp++ = tolower((unsigned char) *cp++);
829                         }
830                         /* otherwise something wrong... */
831                         else
832                                 return -1;
833                 }
834                 /* ignore punctuation but use as delimiter */
835                 else if (ispunct((unsigned char) *cp))
836                 {
837                         cp++;
838                         continue;
839
840                 }
841                 /* otherwise, something is not right... */
842                 else
843                         return -1;
844
845                 /* force in a delimiter after each field */
846                 *lp++ = '\0';
847                 nf++;
848                 if (nf > MAXDATEFIELDS)
849                         return -1;
850         }
851
852         *numfields = nf;
853
854         return 0;
855 }       /* ParseDateTime() */
856
857
858 /* DecodeDateTime()
859  * Interpret previously parsed fields for general date and time.
860  * Return 0 if full date, 1 if only time, and -1 if problems.
861  *              External format(s):
862  *                              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
863  *                              "Fri Feb-7-1997 15:23:27"
864  *                              "Feb-7-1997 15:23:27"
865  *                              "2-7-1997 15:23:27"
866  *                              "1997-2-7 15:23:27"
867  *                              "1997.038 15:23:27"             (day of year 1-366)
868  *              Also supports input in compact time:
869  *                              "970207 152327"
870  *                              "97038 152327"
871  *                              "20011225T040506.789-07"
872  *
873  * Use the system-provided functions to get the current time zone
874  *      if not specified in the input string.
875  * If the date is outside the time_t system-supported time range,
876  *      then assume UTC time zone. - thomas 1997-05-27
877  */
878 int
879 DecodeDateTime(char **field, int *ftype, int nf,
880                            int *dtype, struct tm * tm, fsec_t *fsec, int *tzp)
881 {
882         int                     fmask = 0,
883                                 tmask,
884                                 type;
885         int                     ptype = 0;              /* "prefix type" for ISO y2001m02d04
886                                                                  * format */
887         int                     i;
888         int                     val;
889         int                     mer = HR24;
890         int                     haveTextMonth = FALSE;
891         int                     is2digits = FALSE;
892         int                     bc = FALSE;
893
894         /***
895          * We'll insist on at least all of the date fields, but initialize the
896          * remaining fields in case they are not set later...
897          ***/
898         *dtype = DTK_DATE;
899         tm->tm_hour = 0;
900         tm->tm_min = 0;
901         tm->tm_sec = 0;
902         *fsec = 0;
903         /* don't know daylight savings time status apriori */
904         tm->tm_isdst = -1;
905         if (tzp != NULL)
906                 *tzp = 0;
907
908         for (i = 0; i < nf; i++)
909         {
910                 switch (ftype[i])
911                 {
912                         case DTK_DATE:
913                                 /***
914                                  * Integral julian day with attached time zone?
915                                  * All other forms with JD will be separated into
916                                  * distinct fields, so we handle just this case here.
917                                  ***/
918                                 if (ptype == DTK_JULIAN)
919                                 {
920                                         char       *cp;
921                                         int                     val;
922
923                                         if (tzp == NULL)
924                                                 return -1;
925
926                                         val = strtol(field[i], &cp, 10);
927                                         if (*cp != '-')
928                                                 return -1;
929
930                                         j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
931                                         /* Get the time zone from the end of the string */
932                                         if (DecodeTimezone(cp, tzp) != 0)
933                                                 return -1;
934
935                                         tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
936                                         ptype = 0;
937                                         break;
938                                 }
939                                 /***
940                                  * Already have a date? Then this might be a POSIX time
941                                  * zone with an embedded dash (e.g. "PST-3" == "EST") or
942                                  * a run-together time with trailing time zone (e.g. hhmmss-zz).
943                                  * - thomas 2001-12-25
944                                  ***/
945                                 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
946                                                  || (ptype != 0))
947                                 {
948                                         /* No time zone accepted? Then quit... */
949                                         if (tzp == NULL)
950                                                 return -1;
951
952                                         if (isdigit((unsigned char) *field[i]) || ptype != 0)
953                                         {
954                                                 char       *cp;
955
956                                                 if (ptype != 0)
957                                                 {
958                                                         /* Sanity check; should not fail this test */
959                                                         if (ptype != DTK_TIME)
960                                                                 return -1;
961                                                         ptype = 0;
962                                                 }
963
964                                                 /*
965                                                  * Starts with a digit but we already have a time
966                                                  * field? Then we are in trouble with a date and
967                                                  * time already...
968                                                  */
969                                                 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
970                                                         return -1;
971
972                                                 if ((cp = strchr(field[i], '-')) == NULL)
973                                                         return -1;
974
975                                                 /* Get the time zone from the end of the string */
976                                                 if (DecodeTimezone(cp, tzp) != 0)
977                                                         return -1;
978                                                 *cp = '\0';
979
980                                                 /*
981                                                  * Then read the rest of the field as a
982                                                  * concatenated time
983                                                  */
984                                                 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
985                                                                           &tmask, tm, fsec, &is2digits)) < 0)
986                                                         return -1;
987
988                                                 /*
989                                                  * modify tmask after returning from
990                                                  * DecodeNumberField()
991                                                  */
992                                                 tmask |= DTK_M(TZ);
993                                         }
994                                         else
995                                         {
996                                                 if (DecodePosixTimezone(field[i], tzp) != 0)
997                                                         return -1;
998
999                                                 ftype[i] = DTK_TZ;
1000                                                 tmask = DTK_M(TZ);
1001                                         }
1002                                 }
1003                                 else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1004                                         return -1;
1005                                 break;
1006
1007                         case DTK_TIME:
1008                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
1009                                         return -1;
1010
1011                                 /*
1012                                  * Check upper limit on hours; other limits checked in
1013                                  * DecodeTime()
1014                                  */
1015                                 if (tm->tm_hour > 23)
1016                                         return -1;
1017                                 break;
1018
1019                         case DTK_TZ:
1020                                 {
1021                                         int                     tz;
1022
1023                                         if (tzp == NULL)
1024                                                 return -1;
1025
1026                                         if (DecodeTimezone(field[i], &tz) != 0)
1027                                                 return -1;
1028
1029                                         /*
1030                                          * Already have a time zone? Then maybe this is the
1031                                          * second field of a POSIX time: EST+3 (equivalent to
1032                                          * PST)
1033                                          */
1034                                         if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1035                                                 && (ftype[i - 1] == DTK_TZ)
1036                                                 && (isalpha((unsigned char) *field[i - 1])))
1037                                         {
1038                                                 *tzp -= tz;
1039                                                 tmask = 0;
1040                                         }
1041                                         else
1042                                         {
1043                                                 *tzp = tz;
1044                                                 tmask = DTK_M(TZ);
1045                                         }
1046                                 }
1047                                 break;
1048
1049                         case DTK_NUMBER:
1050
1051                                 /*
1052                                  * Was this an "ISO date" with embedded field labels? An
1053                                  * example is "y2001m02d04" - thomas 2001-02-04
1054                                  */
1055                                 if (ptype != 0)
1056                                 {
1057                                         char       *cp;
1058                                         int                     val;
1059
1060                                         val = strtol(field[i], &cp, 10);
1061
1062                                         /*
1063                                          * only a few kinds are allowed to have an embedded
1064                                          * decimal
1065                                          */
1066                                         if (*cp == '.')
1067                                                 switch (ptype)
1068                                                 {
1069                                                         case DTK_JULIAN:
1070                                                         case DTK_TIME:
1071                                                         case DTK_SECOND:
1072                                                                 break;
1073                                                         default:
1074                                                                 return 1;
1075                                                                 break;
1076                                                 }
1077                                         else if (*cp != '\0')
1078                                                 return -1;
1079
1080                                         switch (ptype)
1081                                         {
1082                                                 case DTK_YEAR:
1083                                                         tm->tm_year = val;
1084                                                         tmask = DTK_M(YEAR);
1085                                                         break;
1086
1087                                                 case DTK_MONTH:
1088
1089                                                         /*
1090                                                          * already have a month and hour? then assume
1091                                                          * minutes
1092                                                          */
1093                                                         if (((fmask & DTK_M(MONTH)) != 0)
1094                                                                 && ((fmask & DTK_M(HOUR)) != 0))
1095                                                         {
1096                                                                 tm->tm_min = val;
1097                                                                 tmask = DTK_M(MINUTE);
1098                                                         }
1099                                                         else
1100                                                         {
1101                                                                 tm->tm_mon = val;
1102                                                                 tmask = DTK_M(MONTH);
1103                                                         }
1104                                                         break;
1105
1106                                                 case DTK_DAY:
1107                                                         tm->tm_mday = val;
1108                                                         tmask = DTK_M(DAY);
1109                                                         break;
1110
1111                                                 case DTK_HOUR:
1112                                                         tm->tm_hour = val;
1113                                                         tmask = DTK_M(HOUR);
1114                                                         break;
1115
1116                                                 case DTK_MINUTE:
1117                                                         tm->tm_min = val;
1118                                                         tmask = DTK_M(MINUTE);
1119                                                         break;
1120
1121                                                 case DTK_SECOND:
1122                                                         tm->tm_sec = val;
1123                                                         tmask = DTK_M(SECOND);
1124                                                         if (*cp == '.')
1125                                                         {
1126                                                                 double          frac;
1127
1128                                                                 frac = strtod(cp, &cp);
1129                                                                 if (*cp != '\0')
1130                                                                         return -1;
1131 #ifdef HAVE_INT64_TIMESTAMP
1132                                                                 *fsec = frac * 1000000;
1133 #else
1134                                                                 *fsec = frac;
1135 #endif
1136                                                         }
1137                                                         break;
1138
1139                                                 case DTK_TZ:
1140                                                         tmask = DTK_M(TZ);
1141                                                         if (DecodeTimezone(field[i], tzp) != 0)
1142                                                                 return -1;
1143                                                         break;
1144
1145                                                 case DTK_JULIAN:
1146                                                         /***
1147                                                          * previous field was a label for "julian date"?
1148                                                          ***/
1149                                                         tmask = DTK_DATE_M;
1150                                                         j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1151                                                         /* fractional Julian Day? */
1152                                                         if (*cp == '.')
1153                                                         {
1154                                                                 double          time;
1155
1156                                                                 time = strtod(cp, &cp);
1157                                                                 if (*cp != '\0')
1158                                                                         return -1;
1159
1160                                                                 tmask |= DTK_TIME_M;
1161 #ifdef HAVE_INT64_TIMESTAMP
1162                                                                 dt2time((time * 86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1163 #else
1164                                                                 dt2time((time * 86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1165 #endif
1166                                                         }
1167                                                         break;
1168
1169                                                 case DTK_TIME:
1170                                                         /* previous field was "t" for ISO time */
1171                                                         if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1172                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1173                                                                 return -1;
1174
1175                                                         if (tmask != DTK_TIME_M)
1176                                                                 return -1;
1177                                                         break;
1178
1179                                                 default:
1180                                                         return -1;
1181                                                         break;
1182                                         }
1183
1184                                         ptype = 0;
1185                                         *dtype = DTK_DATE;
1186                                 }
1187                                 else
1188                                 {
1189                                         char       *cp;
1190                                         int                     flen;
1191
1192                                         flen = strlen(field[i]);
1193                                         cp = strchr(field[i], '.');
1194
1195                                         /* Embedded decimal and no date yet? */
1196                                         if ((cp != NULL) && !(fmask & DTK_DATE_M))
1197                                         {
1198                                                 if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1199                                                         return -1;
1200                                         }
1201                                         /* embedded decimal and several digits before? */
1202                                         else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
1203                                         {
1204                                                 /*
1205                                                  * Interpret as a concatenated date or time Set
1206                                                  * the type field to allow decoding other fields
1207                                                  * later. Example: 20011223 or 040506
1208                                                  */
1209                                                 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1210                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1211                                                         return -1;
1212                                         }
1213                                         else if (flen > 4)
1214                                         {
1215                                                 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1216                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1217                                                         return -1;
1218                                         }
1219                                         /* otherwise it is a single date/time field... */
1220                                         else if (DecodeNumber(flen, field[i], fmask,
1221                                                                           &tmask, tm, fsec, &is2digits) != 0)
1222                                                 return -1;
1223                                 }
1224                                 break;
1225
1226                         case DTK_STRING:
1227                         case DTK_SPECIAL:
1228                                 type = DecodeSpecial(i, field[i], &val);
1229                                 if (type == IGNORE_DTF)
1230                                         continue;
1231
1232                                 tmask = DTK_M(type);
1233                                 switch (type)
1234                                 {
1235                                         case RESERV:
1236                                                 switch (val)
1237                                                 {
1238                                                         case DTK_CURRENT:
1239                                                                 elog(ERROR, "'CURRENT' is no longer supported");
1240                                                                 return -1;
1241                                                                 break;
1242
1243                                                         case DTK_NOW:
1244                                                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1245                                                                 *dtype = DTK_DATE;
1246                                                                 GetCurrentTimeUsec(tm, fsec);
1247                                                                 if (tzp != NULL)
1248                                                                         *tzp = CTimeZone;
1249                                                                 break;
1250
1251                                                         case DTK_YESTERDAY:
1252                                                                 tmask = DTK_DATE_M;
1253                                                                 *dtype = DTK_DATE;
1254                                                                 GetCurrentDateTime(tm);
1255                                                                 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
1256                                                                 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1257                                                                 tm->tm_hour = 0;
1258                                                                 tm->tm_min = 0;
1259                                                                 tm->tm_sec = 0;
1260                                                                 break;
1261
1262                                                         case DTK_TODAY:
1263                                                                 tmask = DTK_DATE_M;
1264                                                                 *dtype = DTK_DATE;
1265                                                                 GetCurrentDateTime(tm);
1266                                                                 tm->tm_hour = 0;
1267                                                                 tm->tm_min = 0;
1268                                                                 tm->tm_sec = 0;
1269                                                                 break;
1270
1271                                                         case DTK_TOMORROW:
1272                                                                 tmask = DTK_DATE_M;
1273                                                                 *dtype = DTK_DATE;
1274                                                                 GetCurrentDateTime(tm);
1275                                                                 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
1276                                                                 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1277                                                                 tm->tm_hour = 0;
1278                                                                 tm->tm_min = 0;
1279                                                                 tm->tm_sec = 0;
1280                                                                 break;
1281
1282                                                         case DTK_ZULU:
1283                                                                 tmask = (DTK_TIME_M | DTK_M(TZ));
1284                                                                 *dtype = DTK_DATE;
1285                                                                 tm->tm_hour = 0;
1286                                                                 tm->tm_min = 0;
1287                                                                 tm->tm_sec = 0;
1288                                                                 if (tzp != NULL)
1289                                                                         *tzp = 0;
1290                                                                 break;
1291
1292                                                         default:
1293                                                                 *dtype = val;
1294                                                 }
1295
1296                                                 break;
1297
1298                                         case MONTH:
1299
1300                                                 /*
1301                                                  * already have a (numeric) month? then see if we
1302                                                  * can substitute...
1303                                                  */
1304                                                 if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
1305                                                         && (!(fmask & DTK_M(DAY)))
1306                                                         && ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
1307                                                 {
1308                                                         tm->tm_mday = tm->tm_mon;
1309                                                         tmask = DTK_M(DAY);
1310                                                 }
1311                                                 haveTextMonth = TRUE;
1312                                                 tm->tm_mon = val;
1313                                                 break;
1314
1315                                         case DTZMOD:
1316
1317                                                 /*
1318                                                  * daylight savings time modifier (solves "MET
1319                                                  * DST" syntax)
1320                                                  */
1321                                                 tmask |= DTK_M(DTZ);
1322                                                 tm->tm_isdst = 1;
1323                                                 if (tzp == NULL)
1324                                                         return -1;
1325                                                 *tzp += val * 60;
1326                                                 break;
1327
1328                                         case DTZ:
1329
1330                                                 /*
1331                                                  * set mask for TZ here _or_ check for DTZ later
1332                                                  * when getting default timezone
1333                                                  */
1334                                                 tmask |= DTK_M(TZ);
1335                                                 tm->tm_isdst = 1;
1336                                                 if (tzp == NULL)
1337                                                         return -1;
1338                                                 *tzp = val * 60;
1339                                                 ftype[i] = DTK_TZ;
1340                                                 break;
1341
1342                                         case TZ:
1343                                                 tm->tm_isdst = 0;
1344                                                 if (tzp == NULL)
1345                                                         return -1;
1346                                                 *tzp = val * 60;
1347                                                 ftype[i] = DTK_TZ;
1348                                                 break;
1349
1350                                         case IGNORE_DTF:
1351                                                 break;
1352
1353                                         case AMPM:
1354                                                 mer = val;
1355                                                 break;
1356
1357                                         case ADBC:
1358                                                 bc = (val == BC);
1359                                                 break;
1360
1361                                         case DOW:
1362                                                 tm->tm_wday = val;
1363                                                 break;
1364
1365                                         case UNITS:
1366                                                 tmask = 0;
1367                                                 ptype = val;
1368                                                 break;
1369
1370                                         case ISOTIME:
1371
1372                                                 /*
1373                                                  * This is a filler field "t" indicating that the
1374                                                  * next field is time. Try to verify that this is
1375                                                  * sensible.
1376                                                  */
1377                                                 tmask = 0;
1378
1379                                                 /* No preceeding date? Then quit... */
1380                                                 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1381                                                         return -1;
1382
1383                                                 /***
1384                                                  * We will need one of the following fields:
1385                                                  *      DTK_NUMBER should be hhmmss.fff
1386                                                  *      DTK_TIME should be hh:mm:ss.fff
1387                                                  *      DTK_DATE should be hhmmss-zz
1388                                                  ***/
1389                                                 if ((i >= (nf - 1))
1390                                                         || ((ftype[i + 1] != DTK_NUMBER)
1391                                                                 && (ftype[i + 1] != DTK_TIME)
1392                                                                 && (ftype[i + 1] != DTK_DATE)))
1393                                                         return -1;
1394
1395                                                 ptype = val;
1396                                                 break;
1397
1398                                         default:
1399                                                 return -1;
1400                                 }
1401                                 break;
1402
1403                         default:
1404                                 return -1;
1405                 }
1406
1407                 if (tmask & fmask)
1408                         return -1;
1409                 fmask |= tmask;
1410         }
1411
1412         /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1413         if (bc)
1414         {
1415                 if (tm->tm_year > 0)
1416                         tm->tm_year = -(tm->tm_year - 1);
1417                 else
1418                         elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year);
1419         }
1420         else if (is2digits)
1421         {
1422                 if (tm->tm_year < 70)
1423                         tm->tm_year += 2000;
1424                 else if (tm->tm_year < 100)
1425                         tm->tm_year += 1900;
1426         }
1427
1428         if ((mer != HR24) && (tm->tm_hour > 12))
1429                 return -1;
1430         if ((mer == AM) && (tm->tm_hour == 12))
1431                 tm->tm_hour = 0;
1432         else if ((mer == PM) && (tm->tm_hour != 12))
1433                 tm->tm_hour += 12;
1434
1435         /* do additional checking for full date specs... */
1436         if (*dtype == DTK_DATE)
1437         {
1438                 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1439                         return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
1440
1441                 /*
1442                  * check for valid day of month, now that we know for sure the
1443                  * month and year...
1444                  */
1445                 if ((tm->tm_mday < 1)
1446                  || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
1447                         return -1;
1448
1449                 /* timezone not specified? then find local timezone if possible */
1450                 if (((fmask & DTK_DATE_M) == DTK_DATE_M)
1451                         && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
1452                 {
1453                         /*
1454                          * daylight savings time modifier but no standard timezone?
1455                          * then error
1456                          */
1457                         if (fmask & DTK_M(DTZMOD))
1458                                 return -1;
1459
1460                         *tzp = DetermineLocalTimeZone(tm);
1461                 }
1462         }
1463
1464         return 0;
1465 }       /* DecodeDateTime() */
1466
1467
1468 /* DetermineLocalTimeZone()
1469  *
1470  * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1471  * tm_sec fields are set, attempt to determine the applicable local zone
1472  * (ie, regular or daylight-savings time) at that time.  Set the struct tm's
1473  * tm_isdst field accordingly, and return the actual timezone offset.
1474  *
1475  * This subroutine exists to centralize uses of mktime() and defend against
1476  * mktime() bugs/restrictions on various platforms.  This should be
1477  * the *only* call of mktime() in the backend.
1478  */
1479 int
1480 DetermineLocalTimeZone(struct tm * tm)
1481 {
1482         int                     tz;
1483
1484         if (HasCTZSet)
1485         {
1486                 tm->tm_isdst = 0;               /* for lack of a better idea */
1487                 tz = CTimeZone;
1488         }
1489         else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
1490         {
1491 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
1492
1493                 /*
1494                  * Some buggy mktime() implementations may change the
1495                  * year/month/day when given a time right at a DST boundary.  To
1496                  * prevent corruption of the caller's data, give mktime() a
1497                  * copy...
1498                  */
1499                 struct tm       tt,
1500                                    *tmp = &tt;
1501
1502                 *tmp = *tm;
1503                 /* change to Unix conventions for year/month */
1504                 tmp->tm_year -= 1900;
1505                 tmp->tm_mon -= 1;
1506
1507                 /* indicate timezone unknown */
1508                 tmp->tm_isdst = -1;
1509
1510                 if (mktime(tmp) != ((time_t) -1) &&
1511                         tmp->tm_isdst >= 0)
1512                 {
1513                         /* mktime() succeeded, trust its result */
1514                         tm->tm_isdst = tmp->tm_isdst;
1515
1516 #if defined(HAVE_TM_ZONE)
1517                         /* tm_gmtoff is Sun/DEC-ism */
1518                         tz = -(tmp->tm_gmtoff);
1519 #elif defined(HAVE_INT_TIMEZONE)
1520                         tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
1521 #endif   /* HAVE_INT_TIMEZONE */
1522                 }
1523                 else
1524                 {
1525                         /*
1526                          * We have a buggy (not to say deliberately brain damaged)
1527                          * mktime().  Work around it by using localtime() instead.
1528                          *
1529                          * First, generate the time_t value corresponding to the given
1530                          * y/m/d/h/m/s taken as GMT time.  This will not overflow (at
1531                          * least not for time_t taken as signed) because of the range
1532                          * check we did above.
1533                          */
1534                         long            day,
1535                                                 mysec,
1536                                                 locsec,
1537                                                 delta1,
1538                                                 delta2;
1539                         time_t          mytime;
1540
1541                         day = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) -
1542                                    date2j(1970, 1, 1));
1543                         mysec = tm->tm_sec + (tm->tm_min + (day * 24 + tm->tm_hour) * 60) * 60;
1544                         mytime = (time_t) mysec;
1545
1546                         /*
1547                          * Use localtime to convert that time_t to broken-down time,
1548                          * and reassemble to get a representation of local time.
1549                          */
1550                         tmp = localtime(&mytime);
1551                         day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1552                                    date2j(1970, 1, 1));
1553                         locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1554
1555                         /*
1556                          * The local time offset corresponding to that GMT time is now
1557                          * computable as mysec - locsec.
1558                          */
1559                         delta1 = mysec - locsec;
1560
1561                         /*
1562                          * However, if that GMT time and the local time we are
1563                          * actually interested in are on opposite sides of a
1564                          * daylight-savings-time transition, then this is not the time
1565                          * offset we want.      So, adjust the time_t to be what we think
1566                          * the GMT time corresponding to our target local time is, and
1567                          * repeat the localtime() call and delta calculation.  We may
1568                          * have to do it twice before we have a trustworthy delta.
1569                          *
1570                          * Note: think not to put a loop here, since if we've been given
1571                          * an "impossible" local time (in the gap during a
1572                          * spring-forward transition) we'd never get out of the loop.
1573                          * Twice is enough to give the behavior we want, which is that
1574                          * "impossible" times are taken as standard time, while at a
1575                          * fall-back boundary ambiguous times are also taken as
1576                          * standard.
1577                          */
1578                         mysec += delta1;
1579                         mytime = (time_t) mysec;
1580                         tmp = localtime(&mytime);
1581                         day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1582                                    date2j(1970, 1, 1));
1583                         locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1584                         delta2 = mysec - locsec;
1585                         if (delta2 != delta1)
1586                         {
1587                                 mysec += (delta2 - delta1);
1588                                 mytime = (time_t) mysec;
1589                                 tmp = localtime(&mytime);
1590                                 day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
1591                                            date2j(1970, 1, 1));
1592                                 locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
1593                                 delta2 = mysec - locsec;
1594                         }
1595                         tm->tm_isdst = tmp->tm_isdst;
1596                         tz = (int) delta2;
1597                 }
1598 #else                                                   /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1599                 tm->tm_isdst = 0;
1600                 tz = CTimeZone;
1601 #endif
1602         }
1603         else
1604         {
1605                 /* Given date is out of range, so assume UTC */
1606                 tm->tm_isdst = 0;
1607                 tz = 0;
1608         }
1609
1610         return tz;
1611 }
1612
1613
1614 /* DecodeTimeOnly()
1615  * Interpret parsed string as time fields only.
1616  * Note that support for time zone is here for
1617  * SQL92 TIME WITH TIME ZONE, but it reveals
1618  * bogosity with SQL92 date/time standards, since
1619  * we must infer a time zone from current time.
1620  * - thomas 2000-03-10
1621  * Allow specifying date to get a better time zone,
1622  * if time zones are allowed. - thomas 2001-12-26
1623  */
1624 int
1625 DecodeTimeOnly(char **field, int *ftype, int nf,
1626                            int *dtype, struct tm * tm, fsec_t *fsec, int *tzp)
1627 {
1628         int                     fmask = 0,
1629                                 tmask,
1630                                 type;
1631         int                     ptype = 0;              /* "prefix type" for ISO h04mm05s06 format */
1632         int                     i;
1633         int                     val;
1634         int                     is2digits = FALSE;
1635         int                     mer = HR24;
1636
1637         *dtype = DTK_TIME;
1638         tm->tm_hour = 0;
1639         tm->tm_min = 0;
1640         tm->tm_sec = 0;
1641         *fsec = 0;
1642         /* don't know daylight savings time status apriori */
1643         tm->tm_isdst = -1;
1644
1645         if (tzp != NULL)
1646                 *tzp = 0;
1647
1648         for (i = 0; i < nf; i++)
1649         {
1650                 switch (ftype[i])
1651                 {
1652                         case DTK_DATE:
1653
1654                                 /*
1655                                  * Time zone not allowed? Then should not accept dates or
1656                                  * time zones no matter what else!
1657                                  */
1658                                 if (tzp == NULL)
1659                                         return -1;
1660
1661                                 /* Under limited circumstances, we will accept a date... */
1662                                 if ((i == 0) && (nf >= 2)
1663                                         && ((ftype[nf - 1] == DTK_DATE)
1664                                                 || (ftype[1] == DTK_TIME)))
1665                                 {
1666                                         if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1667                                                 return -1;
1668                                 }
1669                                 /* otherwise, this is a time and/or time zone */
1670                                 else
1671                                 {
1672                                         if (isdigit((unsigned char) *field[i]))
1673                                         {
1674                                                 char       *cp;
1675
1676                                                 /*
1677                                                  * Starts with a digit but we already have a time
1678                                                  * field? Then we are in trouble with time
1679                                                  * already...
1680                                                  */
1681                                                 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1682                                                         return -1;
1683
1684                                                 /*
1685                                                  * Should not get here and fail. Sanity check
1686                                                  * only...
1687                                                  */
1688                                                 if ((cp = strchr(field[i], '-')) == NULL)
1689                                                         return -1;
1690
1691                                                 /* Get the time zone from the end of the string */
1692                                                 if (DecodeTimezone(cp, tzp) != 0)
1693                                                         return -1;
1694                                                 *cp = '\0';
1695
1696                                                 /*
1697                                                  * Then read the rest of the field as a
1698                                                  * concatenated time
1699                                                  */
1700                                                 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1701                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1702                                                         return -1;
1703
1704                                                 tmask |= DTK_M(TZ);
1705                                         }
1706                                         else
1707                                         {
1708                                                 if (DecodePosixTimezone(field[i], tzp) != 0)
1709                                                         return -1;
1710
1711                                                 ftype[i] = DTK_TZ;
1712                                                 tmask = DTK_M(TZ);
1713                                         }
1714                                 }
1715                                 break;
1716
1717                         case DTK_TIME:
1718                                 if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0)
1719                                         return -1;
1720                                 break;
1721
1722                         case DTK_TZ:
1723                                 if (tzp == NULL)
1724                                         return -1;
1725
1726                                 {
1727                                         int                     tz;
1728
1729                                         if (DecodeTimezone(field[i], &tz) != 0)
1730                                                 return -1;
1731
1732                                         /*
1733                                          * Already have a time zone? Then maybe this is the
1734                                          * second field of a POSIX time: EST+3 (equivalent to
1735                                          * PST)
1736                                          */
1737                                         if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1738                                                 && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1])))
1739                                         {
1740                                                 *tzp -= tz;
1741                                                 tmask = 0;
1742                                         }
1743                                         else
1744                                         {
1745                                                 *tzp = tz;
1746                                                 tmask = DTK_M(TZ);
1747                                         }
1748                                 }
1749                                 break;
1750
1751                         case DTK_NUMBER:
1752
1753                                 /*
1754                                  * Was this an "ISO time" with embedded field labels? An
1755                                  * example is "h04m05s06" - thomas 2001-02-04
1756                                  */
1757                                 if (ptype != 0)
1758                                 {
1759                                         char       *cp;
1760                                         int                     val;
1761
1762                                         /* Only accept a date under limited circumstances */
1763                                         switch (ptype)
1764                                         {
1765                                                 case DTK_JULIAN:
1766                                                 case DTK_YEAR:
1767                                                 case DTK_MONTH:
1768                                                 case DTK_DAY:
1769                                                         if (tzp == NULL)
1770                                                                 return -1;
1771                                                 default:
1772                                                         break;
1773                                         }
1774
1775                                         val = strtol(field[i], &cp, 10);
1776
1777                                         /*
1778                                          * only a few kinds are allowed to have an embedded
1779                                          * decimal
1780                                          */
1781                                         if (*cp == '.')
1782                                                 switch (ptype)
1783                                                 {
1784                                                         case DTK_JULIAN:
1785                                                         case DTK_TIME:
1786                                                         case DTK_SECOND:
1787                                                                 break;
1788                                                         default:
1789                                                                 return 1;
1790                                                                 break;
1791                                                 }
1792                                         else if (*cp != '\0')
1793                                                 return -1;
1794
1795                                         switch (ptype)
1796                                         {
1797                                                 case DTK_YEAR:
1798                                                         tm->tm_year = val;
1799                                                         tmask = DTK_M(YEAR);
1800                                                         break;
1801
1802                                                 case DTK_MONTH:
1803
1804                                                         /*
1805                                                          * already have a month and hour? then assume
1806                                                          * minutes
1807                                                          */
1808                                                         if (((fmask & DTK_M(MONTH)) != 0)
1809                                                                 && ((fmask & DTK_M(HOUR)) != 0))
1810                                                         {
1811                                                                 tm->tm_min = val;
1812                                                                 tmask = DTK_M(MINUTE);
1813                                                         }
1814                                                         else
1815                                                         {
1816                                                                 tm->tm_mon = val;
1817                                                                 tmask = DTK_M(MONTH);
1818                                                         }
1819                                                         break;
1820
1821                                                 case DTK_DAY:
1822                                                         tm->tm_mday = val;
1823                                                         tmask = DTK_M(DAY);
1824                                                         break;
1825
1826                                                 case DTK_HOUR:
1827                                                         tm->tm_hour = val;
1828                                                         tmask = DTK_M(HOUR);
1829                                                         break;
1830
1831                                                 case DTK_MINUTE:
1832                                                         tm->tm_min = val;
1833                                                         tmask = DTK_M(MINUTE);
1834                                                         break;
1835
1836                                                 case DTK_SECOND:
1837                                                         tm->tm_sec = val;
1838                                                         tmask = DTK_M(SECOND);
1839                                                         if (*cp == '.')
1840                                                         {
1841                                                                 *fsec = strtod(cp, &cp);
1842                                                                 if (*cp != '\0')
1843                                                                         return -1;
1844                                                         }
1845                                                         break;
1846
1847                                                 case DTK_TZ:
1848                                                         tmask = DTK_M(TZ);
1849                                                         if (DecodeTimezone(field[i], tzp) != 0)
1850                                                                 return -1;
1851                                                         break;
1852
1853                                                 case DTK_JULIAN:
1854                                                         /***
1855                                                          * previous field was a label for "julian date"?
1856                                                          ***/
1857                                                         tmask = DTK_DATE_M;
1858                                                         j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1859                                                         if (*cp == '.')
1860                                                         {
1861                                                                 double          time;
1862
1863                                                                 time = strtod(cp, &cp);
1864                                                                 if (*cp != '\0')
1865                                                                         return -1;
1866
1867                                                                 tmask |= DTK_TIME_M;
1868 #ifdef HAVE_INT64_TIMESTAMP
1869                                                                 dt2time((time * 86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1870 #else
1871                                                                 dt2time((time * 86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1872 #endif
1873                                                         }
1874                                                         break;
1875
1876                                                 case DTK_TIME:
1877                                                         /* previous field was "t" for ISO time */
1878                                                         if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
1879                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1880                                                                 return -1;
1881
1882                                                         if (tmask != DTK_TIME_M)
1883                                                                 return -1;
1884                                                         break;
1885
1886                                                 default:
1887                                                         return -1;
1888                                                         break;
1889                                         }
1890
1891                                         ptype = 0;
1892                                         *dtype = DTK_DATE;
1893                                 }
1894                                 else
1895                                 {
1896                                         char       *cp;
1897                                         int                     flen;
1898
1899                                         flen = strlen(field[i]);
1900                                         cp = strchr(field[i], '.');
1901
1902                                         /* Embedded decimal? */
1903                                         if (cp != NULL)
1904                                         {
1905                                                 /*
1906                                                  * Under limited circumstances, we will accept a
1907                                                  * date...
1908                                                  */
1909                                                 if ((i == 0) && ((nf >= 2) && (ftype[nf - 1] == DTK_DATE)))
1910                                                 {
1911                                                         if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
1912                                                                 return -1;
1913                                                 }
1914                                                 /* embedded decimal and several digits before? */
1915                                                 else if ((flen - strlen(cp)) > 2)
1916                                                 {
1917                                                         /*
1918                                                          * Interpret as a concatenated date or time
1919                                                          * Set the type field to allow decoding other
1920                                                          * fields later. Example: 20011223 or 040506
1921                                                          */
1922                                                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1923                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1924                                                                 return -1;
1925                                                 }
1926                                                 else
1927                                                         return -1;
1928                                         }
1929                                         else if (flen > 4)
1930                                         {
1931                                                 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
1932                                                                           &tmask, tm, fsec, &is2digits)) < 0)
1933                                                         return -1;
1934                                         }
1935                                         /* otherwise it is a single date/time field... */
1936                                         else if (DecodeNumber(flen, field[i], fmask,
1937                                                                           &tmask, tm, fsec, &is2digits) != 0)
1938                                                 return -1;
1939                                 }
1940                                 break;
1941
1942                         case DTK_STRING:
1943                         case DTK_SPECIAL:
1944                                 type = DecodeSpecial(i, field[i], &val);
1945                                 if (type == IGNORE_DTF)
1946                                         continue;
1947
1948                                 tmask = DTK_M(type);
1949                                 switch (type)
1950                                 {
1951                                         case RESERV:
1952                                                 switch (val)
1953                                                 {
1954                                                         case DTK_CURRENT:
1955                                                                 elog(ERROR, "'CURRENT' is no longer supported");
1956                                                                 return -1;
1957                                                                 break;
1958
1959                                                         case DTK_NOW:
1960                                                                 tmask = DTK_TIME_M;
1961                                                                 *dtype = DTK_TIME;
1962                                                                 GetCurrentTimeUsec(tm, fsec);
1963                                                                 break;
1964
1965                                                         case DTK_ZULU:
1966                                                                 tmask = (DTK_TIME_M | DTK_M(TZ));
1967                                                                 *dtype = DTK_TIME;
1968                                                                 tm->tm_hour = 0;
1969                                                                 tm->tm_min = 0;
1970                                                                 tm->tm_sec = 0;
1971                                                                 tm->tm_isdst = 0;
1972                                                                 break;
1973
1974                                                         default:
1975                                                                 return -1;
1976                                                 }
1977
1978                                                 break;
1979
1980                                         case DTZMOD:
1981
1982                                                 /*
1983                                                  * daylight savings time modifier (solves "MET
1984                                                  * DST" syntax)
1985                                                  */
1986                                                 tmask |= DTK_M(DTZ);
1987                                                 tm->tm_isdst = 1;
1988                                                 if (tzp == NULL)
1989                                                         return -1;
1990                                                 *tzp += val * 60;
1991                                                 break;
1992
1993                                         case DTZ:
1994
1995                                                 /*
1996                                                  * set mask for TZ here _or_ check for DTZ later
1997                                                  * when getting default timezone
1998                                                  */
1999                                                 tmask |= DTK_M(TZ);
2000                                                 tm->tm_isdst = 1;
2001                                                 if (tzp == NULL)
2002                                                         return -1;
2003                                                 *tzp = val * 60;
2004                                                 ftype[i] = DTK_TZ;
2005                                                 break;
2006
2007                                         case TZ:
2008                                                 tm->tm_isdst = 0;
2009                                                 if (tzp == NULL)
2010                                                         return -1;
2011                                                 *tzp = val * 60;
2012                                                 ftype[i] = DTK_TZ;
2013                                                 break;
2014
2015                                         case IGNORE_DTF:
2016                                                 break;
2017
2018                                         case AMPM:
2019                                                 mer = val;
2020                                                 break;
2021
2022                                         case UNITS:
2023                                                 tmask = 0;
2024                                                 ptype = val;
2025                                                 break;
2026
2027                                         case ISOTIME:
2028                                                 tmask = 0;
2029
2030                                                 /***
2031                                                  * We will need one of the following fields:
2032                                                  *      DTK_NUMBER should be hhmmss.fff
2033                                                  *      DTK_TIME should be hh:mm:ss.fff
2034                                                  *      DTK_DATE should be hhmmss-zz
2035                                                  ***/
2036                                                 if ((i >= (nf - 1))
2037                                                         || ((ftype[i + 1] != DTK_NUMBER)
2038                                                                 && (ftype[i + 1] != DTK_TIME)
2039                                                                 && (ftype[i + 1] != DTK_DATE)))
2040                                                         return -1;
2041
2042                                                 ptype = val;
2043                                                 break;
2044
2045                                         default:
2046                                                 return -1;
2047                                 }
2048                                 break;
2049
2050                         default:
2051                                 return -1;
2052                 }
2053
2054                 if (tmask & fmask)
2055                         return -1;
2056                 fmask |= tmask;
2057         }
2058
2059         if ((mer != HR24) && (tm->tm_hour > 12))
2060                 return -1;
2061         if ((mer == AM) && (tm->tm_hour == 12))
2062                 tm->tm_hour = 0;
2063         else if ((mer == PM) && (tm->tm_hour != 12))
2064                 tm->tm_hour += 12;
2065
2066 #ifdef HAVE_INT64_TIMESTAMP
2067         if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
2068                 || ((tm->tm_min < 0) || (tm->tm_min > 59))
2069                 || ((tm->tm_sec < 0) || (tm->tm_sec > 60))
2070                 || (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
2071                 return -1;
2072 #else
2073         if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
2074                 || ((tm->tm_min < 0) || (tm->tm_min > 59))
2075                 || ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60)))
2076                 return -1;
2077 #endif
2078
2079         if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2080                 return -1;
2081
2082         /* timezone not specified? then find local timezone if possible */
2083         if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
2084         {
2085                 struct tm       tt,
2086                                    *tmp = &tt;
2087
2088                 /*
2089                  * daylight savings time modifier but no standard timezone? then
2090                  * error
2091                  */
2092                 if (fmask & DTK_M(DTZMOD))
2093                         return -1;
2094
2095                 if ((fmask & DTK_DATE_M) == 0)
2096                         GetCurrentDateTime(tmp);
2097                 else
2098                 {
2099                         tmp->tm_year = tm->tm_year;
2100                         tmp->tm_mon = tm->tm_mon;
2101                         tmp->tm_mday = tm->tm_mday;
2102                 }
2103                 tmp->tm_hour = tm->tm_hour;
2104                 tmp->tm_min = tm->tm_min;
2105                 tmp->tm_sec = tm->tm_sec;
2106                 *tzp = DetermineLocalTimeZone(tmp);
2107                 tm->tm_isdst = tmp->tm_isdst;
2108         }
2109
2110         return 0;
2111 }       /* DecodeTimeOnly() */
2112
2113 /* DecodeDate()
2114  * Decode date string which includes delimiters.
2115  * Insist on a complete set of fields.
2116  */
2117 static int
2118 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
2119 {
2120         fsec_t          fsec;
2121
2122         int                     nf = 0;
2123         int                     i,
2124                                 len;
2125         int                     bc = FALSE;
2126         int                     is2digits = FALSE;
2127         int                     type,
2128                                 val,
2129                                 dmask = 0;
2130         char       *field[MAXDATEFIELDS];
2131
2132         /* parse this string... */
2133         while ((*str != '\0') && (nf < MAXDATEFIELDS))
2134         {
2135                 /* skip field separators */
2136                 while (!isalnum((unsigned char) *str))
2137                         str++;
2138
2139                 field[nf] = str;
2140                 if (isdigit((unsigned char) *str))
2141                 {
2142                         while (isdigit((unsigned char) *str))
2143                                 str++;
2144                 }
2145                 else if (isalpha((unsigned char) *str))
2146                 {
2147                         while (isalpha((unsigned char) *str))
2148                                 str++;
2149                 }
2150
2151                 /* Just get rid of any non-digit, non-alpha characters... */
2152                 if (*str != '\0')
2153                         *str++ = '\0';
2154                 nf++;
2155         }
2156
2157 #if 0
2158         /* don't allow too many fields */
2159         if (nf > 3)
2160                 return -1;
2161 #endif
2162
2163         *tmask = 0;
2164
2165         /* look first for text fields, since that will be unambiguous month */
2166         for (i = 0; i < nf; i++)
2167         {
2168                 if (isalpha((unsigned char) *field[i]))
2169                 {
2170                         type = DecodeSpecial(i, field[i], &val);
2171                         if (type == IGNORE_DTF)
2172                                 continue;
2173
2174                         dmask = DTK_M(type);
2175                         switch (type)
2176                         {
2177                                 case MONTH:
2178                                         tm->tm_mon = val;
2179                                         break;
2180
2181                                 case ADBC:
2182                                         bc = (val == BC);
2183                                         break;
2184
2185                                 default:
2186                                         return -1;
2187                         }
2188                         if (fmask & dmask)
2189                                 return -1;
2190
2191                         fmask |= dmask;
2192                         *tmask |= dmask;
2193
2194                         /* mark this field as being completed */
2195                         field[i] = NULL;
2196                 }
2197         }
2198
2199         /* now pick up remaining numeric fields */
2200         for (i = 0; i < nf; i++)
2201         {
2202                 if (field[i] == NULL)
2203                         continue;
2204
2205                 if ((len = strlen(field[i])) <= 0)
2206                         return -1;
2207
2208                 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
2209                         return -1;
2210
2211                 if (fmask & dmask)
2212                         return -1;
2213
2214                 fmask |= dmask;
2215                 *tmask |= dmask;
2216         }
2217
2218         if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2219                 return -1;
2220
2221         /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2222         if (bc)
2223         {
2224                 if (tm->tm_year > 0)
2225                         tm->tm_year = -(tm->tm_year - 1);
2226                 else
2227                         elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year);
2228         }
2229         else if (is2digits)
2230         {
2231                 if (tm->tm_year < 70)
2232                         tm->tm_year += 2000;
2233                 else if (tm->tm_year < 100)
2234                         tm->tm_year += 1900;
2235         }
2236
2237         return 0;
2238 }       /* DecodeDate() */
2239
2240
2241 /* DecodeTime()
2242  * Decode time string which includes delimiters.
2243  * Only check the lower limit on hours, since this same code
2244  *      can be used to represent time spans.
2245  */
2246 static int
2247 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
2248 {
2249         char       *cp;
2250
2251         *tmask = DTK_TIME_M;
2252
2253         tm->tm_hour = strtol(str, &cp, 10);
2254         if (*cp != ':')
2255                 return -1;
2256         str = cp + 1;
2257         tm->tm_min = strtol(str, &cp, 10);
2258         if (*cp == '\0')
2259         {
2260                 tm->tm_sec = 0;
2261                 *fsec = 0;
2262         }
2263         else if (*cp != ':')
2264                 return -1;
2265         else
2266         {
2267                 str = cp + 1;
2268                 tm->tm_sec = strtol(str, &cp, 10);
2269                 if (*cp == '\0')
2270                         *fsec = 0;
2271                 else if (*cp == '.')
2272                 {
2273 #ifdef HAVE_INT64_TIMESTAMP
2274                         char            fstr[MAXDATELEN + 1];
2275
2276                         /*
2277                          * OK, we have at most six digits to work with. Let's
2278                          * construct a string and then do the conversion to an
2279                          * integer.
2280                          */
2281                         strncpy(fstr, (cp + 1), 7);
2282                         strcpy((fstr + strlen(fstr)), "000000");
2283                         *(fstr + 6) = '\0';
2284                         *fsec = strtol(fstr, &cp, 10);
2285 #else
2286                         str = cp;
2287                         *fsec = strtod(str, &cp);
2288 #endif
2289                         if (*cp != '\0')
2290                                 return -1;
2291                 }
2292                 else
2293                         return -1;
2294         }
2295
2296         /* do a sanity check */
2297 #ifdef HAVE_INT64_TIMESTAMP
2298         if ((tm->tm_hour < 0)
2299                 || (tm->tm_min < 0) || (tm->tm_min > 59)
2300                 || (tm->tm_sec < 0) || (tm->tm_sec > 59)
2301                 || (*fsec >= INT64CONST(1000000)))
2302                 return -1;
2303 #else
2304         if ((tm->tm_hour < 0)
2305                 || (tm->tm_min < 0) || (tm->tm_min > 59)
2306                 || (tm->tm_sec < 0) || (tm->tm_sec > 59)
2307                 || (*fsec >= 1))
2308                 return -1;
2309 #endif
2310
2311         return 0;
2312 }       /* DecodeTime() */
2313
2314
2315 /* DecodeNumber()
2316  * Interpret plain numeric field as a date value in context.
2317  */
2318 static int
2319 DecodeNumber(int flen, char *str, int fmask,
2320                          int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
2321 {
2322         int                     val;
2323         char       *cp;
2324
2325         *tmask = 0;
2326
2327         val = strtol(str, &cp, 10);
2328         if (cp == str)
2329                 return -1;
2330
2331         if (*cp == '.')
2332         {
2333                 /*
2334                  * More than two digits? Then could be a date or a run-together
2335                  * time: 2001.360 20011225 040506.789
2336                  */
2337                 if ((cp - str) > 2)
2338                         return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
2339                                                                          tmask, tm, fsec, is2digits);
2340
2341                 *fsec = strtod(cp, &cp);
2342                 if (*cp != '\0')
2343                         return -1;
2344         }
2345         else if (*cp != '\0')
2346                 return -1;
2347
2348         /* Special case day of year? */
2349         if ((flen == 3) && (fmask & DTK_M(YEAR))
2350                 && ((val >= 1) && (val <= 366)))
2351         {
2352                 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2353                 tm->tm_yday = val;
2354                 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
2355                            &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2356         }
2357
2358         /***
2359          * Enough digits to be unequivocal year? Used to test for 4 digits or
2360          * more, but we now test first for a three-digit doy so anything
2361          * bigger than two digits had better be an explicit year.
2362          * - thomas 1999-01-09
2363          * Back to requiring a 4 digit year. We accept a two digit
2364          * year farther down. - thomas 2000-03-28
2365          ***/
2366         else if (flen >= 4)
2367         {
2368                 *tmask = DTK_M(YEAR);
2369
2370                 /* already have a year? then see if we can substitute... */
2371                 if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
2372                         && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
2373                 {
2374                         tm->tm_mday = tm->tm_year;
2375                         *tmask = DTK_M(DAY);
2376                 }
2377
2378                 tm->tm_year = val;
2379         }
2380
2381         /* already have year? then could be month */
2382         else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
2383                          && ((val >= 1) && (val <= 12)))
2384         {
2385                 *tmask = DTK_M(MONTH);
2386                 tm->tm_mon = val;
2387         }
2388         /* no year and EuroDates enabled? then could be day */
2389         else if ((EuroDates || (fmask & DTK_M(MONTH)))
2390                          && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
2391                          && ((val >= 1) && (val <= 31)))
2392         {
2393                 *tmask = DTK_M(DAY);
2394                 tm->tm_mday = val;
2395         }
2396         else if ((!(fmask & DTK_M(MONTH)))
2397                          && ((val >= 1) && (val <= 12)))
2398         {
2399                 *tmask = DTK_M(MONTH);
2400                 tm->tm_mon = val;
2401         }
2402         else if ((!(fmask & DTK_M(DAY)))
2403                          && ((val >= 1) && (val <= 31)))
2404         {
2405                 *tmask = DTK_M(DAY);
2406                 tm->tm_mday = val;
2407         }
2408
2409         /*
2410          * Check for 2 or 4 or more digits, but currently we reach here only
2411          * if two digits. - thomas 2000-03-28
2412          */
2413         else if (!(fmask & DTK_M(YEAR))
2414                          && ((flen >= 4) || (flen == 2)))
2415         {
2416                 *tmask = DTK_M(YEAR);
2417                 tm->tm_year = val;
2418
2419                 /* adjust ONLY if exactly two digits... */
2420                 *is2digits = (flen == 2);
2421         }
2422         else
2423                 return -1;
2424
2425         return 0;
2426 }       /* DecodeNumber() */
2427
2428
2429 /* DecodeNumberField()
2430  * Interpret numeric string as a concatenated date or time field.
2431  * Use the context of previously decoded fields to help with
2432  * the interpretation.
2433  */
2434 static int
2435 DecodeNumberField(int len, char *str, int fmask,
2436                                 int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
2437 {
2438         char       *cp;
2439
2440         /*
2441          * Have a decimal point? Then this is a date or something with a
2442          * seconds field...
2443          */
2444         if ((cp = strchr(str, '.')) != NULL)
2445         {
2446 #ifdef HAVE_INT64_TIMESTAMP
2447                 char            fstr[MAXDATELEN + 1];
2448
2449                 /*
2450                  * OK, we have at most six digits to care about. Let's construct a
2451                  * string and then do the conversion to an integer.
2452                  */
2453                 strcpy(fstr, (cp + 1));
2454                 strcpy((fstr + strlen(fstr)), "000000");
2455                 *(fstr + 6) = '\0';
2456                 *fsec = strtol(fstr, NULL, 10);
2457 #else
2458                 *fsec = strtod(cp, NULL);
2459 #endif
2460                 *cp = '\0';
2461                 len = strlen(str);
2462         }
2463         /* No decimal point and no complete date yet? */
2464         else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2465         {
2466                 /* yyyymmdd? */
2467                 if (len == 8)
2468                 {
2469                         *tmask = DTK_DATE_M;
2470
2471                         tm->tm_mday = atoi(str + 6);
2472                         *(str + 6) = '\0';
2473                         tm->tm_mon = atoi(str + 4);
2474                         *(str + 4) = '\0';
2475                         tm->tm_year = atoi(str + 0);
2476
2477                         return DTK_DATE;
2478                 }
2479                 /* yymmdd? */
2480                 else if (len == 6)
2481                 {
2482                         *tmask = DTK_DATE_M;
2483                         tm->tm_mday = atoi(str + 4);
2484                         *(str + 4) = '\0';
2485                         tm->tm_mon = atoi(str + 2);
2486                         *(str + 2) = '\0';
2487                         tm->tm_year = atoi(str + 0);
2488                         *is2digits = TRUE;
2489
2490                         return DTK_DATE;
2491                 }
2492                 /* yyddd? */
2493                 else if (len == 5)
2494                 {
2495                         *tmask = DTK_DATE_M;
2496                         tm->tm_mday = atoi(str + 2);
2497                         *(str + 2) = '\0';
2498                         tm->tm_mon = 1;
2499                         tm->tm_year = atoi(str + 0);
2500                         *is2digits = TRUE;
2501
2502                         return DTK_DATE;
2503                 }
2504         }
2505
2506         /* not all time fields are specified? */
2507         if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2508         {
2509                 /* hhmmss */
2510                 if (len == 6)
2511                 {
2512                         *tmask = DTK_TIME_M;
2513                         tm->tm_sec = atoi(str + 4);
2514                         *(str + 4) = '\0';
2515                         tm->tm_min = atoi(str + 2);
2516                         *(str + 2) = '\0';
2517                         tm->tm_hour = atoi(str + 0);
2518
2519                         return DTK_TIME;
2520                 }
2521                 /* hhmm? */
2522                 else if (len == 4)
2523                 {
2524                         *tmask = DTK_TIME_M;
2525                         tm->tm_sec = 0;
2526                         tm->tm_min = atoi(str + 2);
2527                         *(str + 2) = '\0';
2528                         tm->tm_hour = atoi(str + 0);
2529
2530                         return DTK_TIME;
2531                 }
2532         }
2533
2534         return -1;
2535 }       /* DecodeNumberField() */
2536
2537
2538 /* DecodeTimezone()
2539  * Interpret string as a numeric timezone.
2540  *
2541  * Note: we allow timezone offsets up to 13:59.  There are places that
2542  * use +1300 summer time.
2543  */
2544 static int
2545 DecodeTimezone(char *str, int *tzp)
2546 {
2547         int                     tz;
2548         int                     hr,
2549                                 min;
2550         char       *cp;
2551         int                     len;
2552
2553         /* assume leading character is "+" or "-" */
2554         hr = strtol((str + 1), &cp, 10);
2555
2556         /* explicit delimiter? */
2557         if (*cp == ':')
2558                 min = strtol((cp + 1), &cp, 10);
2559         /* otherwise, might have run things together... */
2560         else if ((*cp == '\0') && ((len = strlen(str)) > 3))
2561         {
2562                 min = strtol((str + len - 2), &cp, 10);
2563                 if ((min < 0) || (min >= 60))
2564                         return -1;
2565
2566                 *(str + len - 2) = '\0';
2567                 hr = strtol((str + 1), &cp, 10);
2568                 if ((hr < 0) || (hr > 13))
2569                         return -1;
2570         }
2571         else
2572                 min = 0;
2573
2574         tz = (hr * 60 + min) * 60;
2575         if (*str == '-')
2576                 tz = -tz;
2577
2578         *tzp = -tz;
2579         return *cp != '\0';
2580 }       /* DecodeTimezone() */
2581
2582
2583 /* DecodePosixTimezone()
2584  * Interpret string as a POSIX-compatible timezone:
2585  *      PST-hh:mm
2586  *      PST+h
2587  * - thomas 2000-03-15
2588  */
2589 static int
2590 DecodePosixTimezone(char *str, int *tzp)
2591 {
2592         int                     val,
2593                                 tz;
2594         int                     type;
2595         char       *cp;
2596         char            delim;
2597
2598         cp = str;
2599         while ((*cp != '\0') && isalpha((unsigned char) *cp))
2600                 cp++;
2601
2602         if (DecodeTimezone(cp, &tz) != 0)
2603                 return -1;
2604
2605         delim = *cp;
2606         *cp = '\0';
2607         type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
2608         *cp = delim;
2609
2610         switch (type)
2611         {
2612                 case DTZ:
2613                 case TZ:
2614                         *tzp = (val * 60) - tz;
2615                         break;
2616
2617                 default:
2618                         return -1;
2619         }
2620
2621         return 0;
2622 }       /* DecodePosixTimezone() */
2623
2624
2625 /* DecodeSpecial()
2626  * Decode text string using lookup table.
2627  * Implement a cache lookup since it is likely that dates
2628  *      will be related in format.
2629  */
2630 int
2631 DecodeSpecial(int field, char *lowtoken, int *val)
2632 {
2633         int                     type;
2634         datetkn    *tp;
2635
2636         if ((datecache[field] != NULL)
2637                 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
2638                 tp = datecache[field];
2639         else
2640         {
2641                 tp = NULL;
2642                 if (Australian_timezones)
2643                         tp = datebsearch(lowtoken, australian_datetktbl,
2644                                                          australian_szdatetktbl);
2645                 if (!tp)
2646                         tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
2647         }
2648         datecache[field] = tp;
2649         if (tp == NULL)
2650         {
2651                 type = UNKNOWN_FIELD;
2652                 *val = 0;
2653         }
2654         else
2655         {
2656                 type = tp->type;
2657                 switch (type)
2658                 {
2659                         case TZ:
2660                         case DTZ:
2661                         case DTZMOD:
2662                                 *val = FROMVAL(tp);
2663                                 break;
2664
2665                         default:
2666                                 *val = tp->value;
2667                                 break;
2668                 }
2669         }
2670
2671         return type;
2672 }       /* DecodeSpecial() */
2673
2674
2675 /* DecodeInterval()
2676  * Interpret previously parsed fields for general time interval.
2677  * Return 0 if decoded and -1 if problems.
2678  *
2679  * Allow "date" field DTK_DATE since this could be just
2680  *      an unsigned floating point number. - thomas 1997-11-16
2681  *
2682  * Allow ISO-style time span, with implicit units on number of days
2683  *      preceding an hh:mm:ss field. - thomas 1998-04-30
2684  */
2685 int
2686 DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
2687 {
2688         int                     is_before = FALSE;
2689
2690         char       *cp;
2691         int                     fmask = 0,
2692                                 tmask,
2693                                 type;
2694         int                     i;
2695         int                     val;
2696         double          fval;
2697
2698         *dtype = DTK_DELTA;
2699
2700         type = IGNORE_DTF;
2701         tm->tm_year = 0;
2702         tm->tm_mon = 0;
2703         tm->tm_mday = 0;
2704         tm->tm_hour = 0;
2705         tm->tm_min = 0;
2706         tm->tm_sec = 0;
2707         *fsec = 0;
2708
2709         /* read through list backwards to pick up units before values */
2710         for (i = nf - 1; i >= 0; i--)
2711         {
2712                 switch (ftype[i])
2713                 {
2714                         case DTK_TIME:
2715                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
2716                                         return -1;
2717                                 type = DTK_DAY;
2718                                 break;
2719
2720                         case DTK_TZ:
2721
2722                                 /*
2723                                  * Timezone is a token with a leading sign character and
2724                                  * otherwise the same as a non-signed time field
2725                                  */
2726                                 Assert((*field[i] == '-') || (*field[i] == '+'));
2727
2728                                 /*
2729                                  * A single signed number ends up here, but will be
2730                                  * rejected by DecodeTime(). So, work this out to drop
2731                                  * through to DTK_NUMBER, which *can* tolerate this.
2732                                  */
2733                                 cp = field[i] + 1;
2734                                 while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
2735                                         cp++;
2736                                 if ((*cp == ':')
2737                                         && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0))
2738                                 {
2739                                         if (*field[i] == '-')
2740                                         {
2741                                                 /* flip the sign on all fields */
2742                                                 tm->tm_hour = -tm->tm_hour;
2743                                                 tm->tm_min = -tm->tm_min;
2744                                                 tm->tm_sec = -tm->tm_sec;
2745                                                 *fsec = -(*fsec);
2746                                         }
2747
2748                                         /*
2749                                          * Set the next type to be a day, if units are not
2750                                          * specified. This handles the case of '1 +02:03'
2751                                          * since we are reading right to left.
2752                                          */
2753                                         type = DTK_DAY;
2754                                         tmask = DTK_M(TZ);
2755                                         break;
2756                                 }
2757                                 else if (type == IGNORE_DTF)
2758                                 {
2759                                         if (*cp == '.')
2760                                         {
2761                                                 /*
2762                                                  * Got a decimal point? Then assume some sort of
2763                                                  * seconds specification
2764                                                  */
2765                                                 type = DTK_SECOND;
2766                                         }
2767                                         else if (*cp == '\0')
2768                                         {
2769                                                 /*
2770                                                  * Only a signed integer? Then must assume a
2771                                                  * timezone-like usage
2772                                                  */
2773                                                 type = DTK_HOUR;
2774                                         }
2775                                 }
2776                                 /* DROP THROUGH */
2777
2778                         case DTK_DATE:
2779                         case DTK_NUMBER:
2780                                 val = strtol(field[i], &cp, 10);
2781
2782                                 if (type == IGNORE_DTF)
2783                                         type = DTK_SECOND;
2784
2785                                 if (*cp == '.')
2786                                 {
2787                                         fval = strtod(cp, &cp);
2788                                         if (*cp != '\0')
2789                                                 return -1;
2790
2791                                         if (val < 0)
2792                                                 fval = -(fval);
2793                                 }
2794                                 else if (*cp == '\0')
2795                                         fval = 0;
2796                                 else
2797                                         return -1;
2798
2799                                 tmask = 0;              /* DTK_M(type); */
2800
2801                                 switch (type)
2802                                 {
2803                                         case DTK_MICROSEC:
2804 #ifdef HAVE_INT64_TIMESTAMP
2805                                                 *fsec += (val + fval);
2806 #else
2807                                                 *fsec += ((val + fval) * 1e-6);
2808 #endif
2809                                                 break;
2810
2811                                         case DTK_MILLISEC:
2812 #ifdef HAVE_INT64_TIMESTAMP
2813                                                 *fsec += ((val + fval) * 1000);
2814 #else
2815                                                 *fsec += ((val + fval) * 1e-3);
2816 #endif
2817                                                 break;
2818
2819                                         case DTK_SECOND:
2820                                                 tm->tm_sec += val;
2821 #ifdef HAVE_INT64_TIMESTAMP
2822                                                 *fsec += (fval * 1000000);
2823 #else
2824                                                 *fsec += fval;
2825 #endif
2826                                                 tmask = DTK_M(SECOND);
2827                                                 break;
2828
2829                                         case DTK_MINUTE:
2830                                                 tm->tm_min += val;
2831                                                 if (fval != 0)
2832                                                 {
2833                                                         int                     sec;
2834
2835                                                         fval *= 60;
2836                                                         sec = fval;
2837                                                         tm->tm_sec += sec;
2838 #ifdef HAVE_INT64_TIMESTAMP
2839                                                         *fsec += ((fval - sec) * 1000000);
2840 #else
2841                                                         *fsec += (fval - sec);
2842 #endif
2843                                                 }
2844                                                 tmask = DTK_M(MINUTE);
2845                                                 break;
2846
2847                                         case DTK_HOUR:
2848                                                 tm->tm_hour += val;
2849                                                 if (fval != 0)
2850                                                 {
2851                                                         int                     sec;
2852
2853                                                         fval *= 3600;
2854                                                         sec = fval;
2855                                                         tm->tm_sec += sec;
2856 #ifdef HAVE_INT64_TIMESTAMP
2857                                                         *fsec += ((fval - sec) * 1000000);
2858 #else
2859                                                         *fsec += (fval - sec);
2860 #endif
2861                                                 }
2862                                                 tmask = DTK_M(HOUR);
2863                                                 break;
2864
2865                                         case DTK_DAY:
2866                                                 tm->tm_mday += val;
2867                                                 if (fval != 0)
2868                                                 {
2869                                                         int                     sec;
2870
2871                                                         fval *= 86400;
2872                                                         sec = fval;
2873                                                         tm->tm_sec += sec;
2874 #ifdef HAVE_INT64_TIMESTAMP
2875                                                         *fsec += ((fval - sec) * 1000000);
2876 #else
2877                                                         *fsec += (fval - sec);
2878 #endif
2879                                                 }
2880                                                 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
2881                                                 break;
2882
2883                                         case DTK_WEEK:
2884                                                 tm->tm_mday += val * 7;
2885                                                 if (fval != 0)
2886                                                 {
2887                                                         int                     sec;
2888
2889                                                         fval *= (7 * 86400);
2890                                                         sec = fval;
2891                                                         tm->tm_sec += sec;
2892 #ifdef HAVE_INT64_TIMESTAMP
2893                                                         *fsec += ((fval - sec) * 1000000);
2894 #else
2895                                                         *fsec += (fval - sec);
2896 #endif
2897                                                 }
2898                                                 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
2899                                                 break;
2900
2901                                         case DTK_MONTH:
2902                                                 tm->tm_mon += val;
2903                                                 if (fval != 0)
2904                                                 {
2905                                                         int                     sec;
2906
2907                                                         fval *= (30 * 86400);
2908                                                         sec = fval;
2909                                                         tm->tm_sec += sec;
2910 #ifdef HAVE_INT64_TIMESTAMP
2911                                                         *fsec += ((fval - sec) * 1000000);
2912 #else
2913                                                         *fsec += (fval - sec);
2914 #endif
2915                                                 }
2916                                                 tmask = DTK_M(MONTH);
2917                                                 break;
2918
2919                                         case DTK_YEAR:
2920                                                 tm->tm_year += val;
2921                                                 if (fval != 0)
2922                                                         tm->tm_mon += (fval * 12);
2923                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2924                                                 break;
2925
2926                                         case DTK_DECADE:
2927                                                 tm->tm_year += val * 10;
2928                                                 if (fval != 0)
2929                                                         tm->tm_mon += (fval * 120);
2930                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2931                                                 break;
2932
2933                                         case DTK_CENTURY:
2934                                                 tm->tm_year += val * 100;
2935                                                 if (fval != 0)
2936                                                         tm->tm_mon += (fval * 1200);
2937                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2938                                                 break;
2939
2940                                         case DTK_MILLENNIUM:
2941                                                 tm->tm_year += val * 1000;
2942                                                 if (fval != 0)
2943                                                         tm->tm_mon += (fval * 12000);
2944                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
2945                                                 break;
2946
2947                                         default:
2948                                                 return -1;
2949                                 }
2950                                 break;
2951
2952                         case DTK_STRING:
2953                         case DTK_SPECIAL:
2954                                 type = DecodeUnits(i, field[i], &val);
2955                                 if (type == IGNORE_DTF)
2956                                         continue;
2957
2958                                 tmask = 0;              /* DTK_M(type); */
2959                                 switch (type)
2960                                 {
2961                                         case UNITS:
2962                                                 type = val;
2963                                                 break;
2964
2965                                         case AGO:
2966                                                 is_before = TRUE;
2967                                                 type = val;
2968                                                 break;
2969
2970                                         case RESERV:
2971                                                 tmask = (DTK_DATE_M || DTK_TIME_M);
2972                                                 *dtype = val;
2973                                                 break;
2974
2975                                         default:
2976                                                 return -1;
2977                                 }
2978                                 break;
2979
2980                         default:
2981                                 return -1;
2982                 }
2983
2984                 if (tmask & fmask)
2985                         return -1;
2986                 fmask |= tmask;
2987         }
2988
2989         if (*fsec != 0)
2990         {
2991                 int                     sec;
2992
2993 #ifdef HAVE_INT64_TIMESTAMP
2994                 sec = (*fsec / INT64CONST(1000000));
2995                 *fsec -= (sec * INT64CONST(1000000));
2996 #else
2997                 TMODULO(*fsec, sec, 1e0);
2998 #endif
2999                 tm->tm_sec += sec;
3000         }
3001
3002         if (is_before)
3003         {
3004                 *fsec = -(*fsec);
3005                 tm->tm_sec = -(tm->tm_sec);
3006                 tm->tm_min = -(tm->tm_min);
3007                 tm->tm_hour = -(tm->tm_hour);
3008                 tm->tm_mday = -(tm->tm_mday);
3009                 tm->tm_mon = -(tm->tm_mon);
3010                 tm->tm_year = -(tm->tm_year);
3011         }
3012
3013         /* ensure that at least one time field has been found */
3014         return (fmask != 0) ? 0 : -1;
3015 }       /* DecodeInterval() */
3016
3017
3018 /* DecodeUnits()
3019  * Decode text string using lookup table.
3020  * This routine supports time interval decoding.
3021  */
3022 int
3023 DecodeUnits(int field, char *lowtoken, int *val)
3024 {
3025         int                     type;
3026         datetkn    *tp;
3027
3028         if ((deltacache[field] != NULL)
3029                 && (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
3030                 tp = deltacache[field];
3031         else
3032                 tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3033         deltacache[field] = tp;
3034         if (tp == NULL)
3035         {
3036                 type = UNKNOWN_FIELD;
3037                 *val = 0;
3038         }
3039         else
3040         {
3041                 type = tp->type;
3042                 if ((type == TZ) || (type == DTZ))
3043                         *val = FROMVAL(tp);
3044                 else
3045                         *val = tp->value;
3046         }
3047
3048         return type;
3049 }       /* DecodeUnits() */
3050
3051
3052 /* datebsearch()
3053  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
3054  * is WAY faster than the generic bsearch().
3055  */
3056 static datetkn *
3057 datebsearch(char *key, datetkn *base, unsigned int nel)
3058 {
3059         datetkn    *last = base + nel - 1,
3060                            *position;
3061         int                     result;
3062
3063         while (last >= base)
3064         {
3065                 position = base + ((last - base) >> 1);
3066                 result = key[0] - position->token[0];
3067                 if (result == 0)
3068                 {
3069                         result = strncmp(key, position->token, TOKMAXLEN);
3070                         if (result == 0)
3071                                 return position;
3072                 }
3073                 if (result < 0)
3074                         last = position - 1;
3075                 else
3076                         base = position + 1;
3077         }
3078         return NULL;
3079 }
3080
3081
3082 /* EncodeDateOnly()
3083  * Encode date as local time.
3084  */
3085 int
3086 EncodeDateOnly(struct tm * tm, int style, char *str)
3087 {
3088         if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
3089                 return -1;
3090
3091         switch (style)
3092         {
3093                 case USE_ISO_DATES:
3094                         /* compatible with ISO date formats */
3095                         if (tm->tm_year > 0)
3096                                 sprintf(str, "%04d-%02d-%02d",
3097                                                 tm->tm_year, tm->tm_mon, tm->tm_mday);
3098                         else
3099                                 sprintf(str, "%04d-%02d-%02d %s",
3100                                           -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
3101                         break;
3102
3103                 case USE_SQL_DATES:
3104                         /* compatible with Oracle/Ingres date formats */
3105                         if (EuroDates)
3106                                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3107                         else
3108                                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3109                         if (tm->tm_year > 0)
3110                                 sprintf((str + 5), "/%04d", tm->tm_year);
3111                         else
3112                                 sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
3113                         break;
3114
3115                 case USE_GERMAN_DATES:
3116                         /* German-style date format */
3117                         sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3118                         if (tm->tm_year > 0)
3119                                 sprintf((str + 5), ".%04d", tm->tm_year);
3120                         else
3121                                 sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
3122                         break;
3123
3124                 case USE_POSTGRES_DATES:
3125                 default:
3126                         /* traditional date-only style for Postgres */
3127                         if (EuroDates)
3128                                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
3129                         else
3130                                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
3131                         if (tm->tm_year > 0)
3132                                 sprintf((str + 5), "-%04d", tm->tm_year);
3133                         else
3134                                 sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
3135                         break;
3136         }
3137
3138         return TRUE;
3139 }       /* EncodeDateOnly() */
3140
3141
3142 /* EncodeTimeOnly()
3143  * Encode time fields only.
3144  */
3145 int
3146 EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str)
3147 {
3148 #ifndef HAVE_INT64_TIMESTAMP
3149         fsec_t          sec;
3150 #endif
3151
3152         if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
3153                 return -1;
3154
3155 #ifndef HAVE_INT64_TIMESTAMP
3156         sec = (tm->tm_sec + fsec);
3157 #endif
3158
3159         sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
3160
3161         /*
3162          * If we have fractional seconds, then include a decimal point We will
3163          * do up to 6 fractional digits, and we have rounded any inputs to
3164          * eliminate anything to the right of 6 digits anyway. If there are no
3165          * fractional seconds, then do not bother printing a decimal point at
3166          * all. - thomas 2001-09-29
3167          */
3168         if (fsec != 0)
3169         {
3170 #ifdef HAVE_INT64_TIMESTAMP
3171                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3172                 sprintf((str + strlen(str)), ".%06d", fsec);
3173 #else
3174                 sprintf((str + strlen(str)), ":%013.10f", sec);
3175 #endif
3176                 /* chop off trailing pairs of zeros... */
3177                 while ((strcmp((str + strlen(str) - 2), "00") == 0)
3178                            && (*(str + strlen(str) - 3) != '.'))
3179                         *(str + strlen(str) - 2) = '\0';
3180         }
3181         else
3182 #ifdef HAVE_INT64_TIMESTAMP
3183                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3184 #else
3185                 sprintf((str + strlen(str)), ":%02.0f", sec);
3186 #endif
3187
3188         if (tzp != NULL)
3189         {
3190                 int                     hour,
3191                                         min;
3192
3193                 hour = -(*tzp / 3600);
3194                 min = ((abs(*tzp) / 60) % 60);
3195                 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3196         }
3197
3198         return TRUE;
3199 }       /* EncodeTimeOnly() */
3200
3201
3202 /* EncodeDateTime()
3203  * Encode date and time interpreted as local time.
3204  * Support several date styles:
3205  *      Postgres - day mon hh:mm:ss yyyy tz
3206  *      SQL - mm/dd/yyyy hh:mm:ss.ss tz
3207  *      ISO - yyyy-mm-dd hh:mm:ss+/-tz
3208  *      German - dd.mm.yyyy hh:mm:ss tz
3209  * Variants (affects order of month and day for Postgres and SQL styles):
3210  *      US - mm/dd/yyyy
3211  *      European - dd/mm/yyyy
3212  */
3213 int
3214 EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str)
3215 {
3216         int                     day,
3217                                 hour,
3218                                 min;
3219
3220 #ifndef HAVE_INT64_TIMESTAMP
3221         fsec_t          sec;
3222 #endif
3223
3224         /*
3225          * Why are we checking only the month field? Change this to an
3226          * assert... if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) return -1;
3227          */
3228         Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12));
3229
3230 #ifndef HAVE_INT64_TIMESTAMP
3231         sec = (tm->tm_sec + fsec);
3232 #endif
3233
3234         switch (style)
3235         {
3236                 case USE_ISO_DATES:
3237                         /* Compatible with ISO-8601 date formats */
3238
3239                         sprintf(str, "%04d-%02d-%02d %02d:%02d",
3240                                   ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3241                                         tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
3242
3243                         /*
3244                          * If we have fractional seconds, then include a decimal point
3245                          * We will do up to 6 fractional digits, and we have rounded
3246                          * any inputs to eliminate anything to the right of 6 digits
3247                          * anyway. If there are no fractional seconds, then do not
3248                          * bother printing a decimal point at all. - thomas 2001-09-29
3249                          */
3250 #ifdef HAVE_INT64_TIMESTAMP
3251                         if (fsec != 0)
3252                         {
3253                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3254                                 sprintf((str + strlen(str)), ".%06d", fsec);
3255 #else
3256                         if ((fsec != 0) && (tm->tm_year > 0))
3257                         {
3258                                 sprintf((str + strlen(str)), ":%013.10f", sec);
3259 #endif
3260                                 TrimTrailingZeros(str);
3261                         }
3262                         else
3263                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3264
3265                         if (tm->tm_year <= 0)
3266                                 sprintf((str + strlen(str)), " BC");
3267
3268                         /*
3269                          * tzp == NULL indicates that we don't want *any* time zone
3270                          * info in the output string. *tzn != NULL indicates that we
3271                          * have alpha time zone info available. tm_isdst != -1
3272                          * indicates that we have a valid time zone translation.
3273                          */
3274                         if ((tzp != NULL) && (tm->tm_isdst >= 0))
3275                         {
3276                                 hour = -(*tzp / 3600);
3277                                 min = ((abs(*tzp) / 60) % 60);
3278                                 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3279                         }
3280                         break;
3281
3282                 case USE_SQL_DATES:
3283                         /* Compatible with Oracle/Ingres date formats */
3284
3285                         if (EuroDates)
3286                                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
3287                         else
3288                                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
3289
3290                         sprintf((str + 5), "/%04d %02d:%02d",
3291                                   ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3292                                         tm->tm_hour, tm->tm_min);
3293
3294                         /*
3295                          * If we have fractional seconds, then include a decimal point
3296                          * We will do up to 6 fractional digits, and we have rounded
3297                          * any inputs to eliminate anything to the right of 6 digits
3298                          * anyway. If there are no fractional seconds, then do not
3299                          * bother printing a decimal point at all. - thomas 2001-09-29
3300                          */
3301 #ifdef HAVE_INT64_TIMESTAMP
3302                         if (fsec != 0)
3303                         {
3304                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3305                                 sprintf((str + strlen(str)), ".%06d", fsec);
3306 #else
3307                         if ((fsec != 0) && (tm->tm_year > 0))
3308                         {
3309                                 sprintf((str + strlen(str)), ":%013.10f", sec);
3310 #endif
3311                                 TrimTrailingZeros(str);
3312                         }
3313                         else
3314                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3315
3316                         if (tm->tm_year <= 0)
3317                                 sprintf((str + strlen(str)), " BC");
3318
3319                         if ((tzp != NULL) && (tm->tm_isdst >= 0))
3320                         {
3321                                 if (*tzn != NULL)
3322                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3323                                 else
3324                                 {
3325                                         hour = -(*tzp / 3600);
3326                                         min = ((abs(*tzp) / 60) % 60);
3327                                         sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3328                                 }
3329                         }
3330                         break;
3331
3332                 case USE_GERMAN_DATES:
3333                         /* German variant on European style */
3334
3335                         sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
3336
3337                         sprintf((str + 5), ".%04d %02d:%02d",
3338                                   ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
3339                                         tm->tm_hour, tm->tm_min);
3340
3341                         /*
3342                          * If we have fractional seconds, then include a decimal point
3343                          * We will do up to 6 fractional digits, and we have rounded
3344                          * any inputs to eliminate anything to the right of 6 digits
3345                          * anyway. If there are no fractional seconds, then do not
3346                          * bother printing a decimal point at all. - thomas 2001-09-29
3347                          */
3348 #ifdef HAVE_INT64_TIMESTAMP
3349                         if (fsec != 0)
3350                         {
3351                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3352                                 sprintf((str + strlen(str)), ".%06d", fsec);
3353 #else
3354                         if ((fsec != 0) && (tm->tm_year > 0))
3355                         {
3356                                 sprintf((str + strlen(str)), ":%013.10f", sec);
3357 #endif
3358                                 TrimTrailingZeros(str);
3359                         }
3360                         else
3361                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3362
3363                         if (tm->tm_year <= 0)
3364                                 sprintf((str + strlen(str)), " BC");
3365
3366                         if ((tzp != NULL) && (tm->tm_isdst >= 0))
3367                         {
3368                                 if (*tzn != NULL)
3369                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3370                                 else
3371                                 {
3372                                         hour = -(*tzp / 3600);
3373                                         min = ((abs(*tzp) / 60) % 60);
3374                                         sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
3375                                 }
3376                         }
3377                         break;
3378
3379                 case USE_POSTGRES_DATES:
3380                 default:
3381                         /* Backward-compatible with traditional Postgres abstime dates */
3382
3383                         day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3384                         tm->tm_wday = j2day(day);
3385
3386                         strncpy(str, days[tm->tm_wday], 3);
3387                         strcpy((str + 3), " ");
3388
3389                         if (EuroDates)
3390                                 sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
3391                         else
3392                                 sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
3393
3394                         sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
3395
3396                         /*
3397                          * If we have fractional seconds, then include a decimal point
3398                          * We will do up to 6 fractional digits, and we have rounded
3399                          * any inputs to eliminate anything to the right of 6 digits
3400                          * anyway. If there are no fractional seconds, then do not
3401                          * bother printing a decimal point at all. - thomas 2001-09-29
3402                          */
3403 #ifdef HAVE_INT64_TIMESTAMP
3404                         if (fsec != 0)
3405                         {
3406                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3407                                 sprintf((str + strlen(str)), ".%06d", fsec);
3408 #else
3409                         if ((fsec != 0) && (tm->tm_year > 0))
3410                         {
3411                                 sprintf((str + strlen(str)), ":%013.10f", sec);
3412 #endif
3413                                 TrimTrailingZeros(str);
3414                         }
3415                         else
3416                                 sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
3417
3418                         sprintf((str + strlen(str)), " %04d",
3419                                  ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)));
3420                         if (tm->tm_year <= 0)
3421                                 sprintf((str + strlen(str)), " BC");
3422
3423                         if ((tzp != NULL) && (tm->tm_isdst >= 0))
3424                         {
3425                                 if (*tzn != NULL)
3426                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
3427                                 else
3428                                 {
3429                                         /*
3430                                          * We have a time zone, but no string version. Use the
3431                                          * numeric form, but be sure to include a leading
3432                                          * space to avoid formatting something which would be
3433                                          * rejected by the date/time parser later. - thomas
3434                                          * 2001-10-19
3435                                          */
3436                                         hour = -(*tzp / 3600);
3437                                         min = ((abs(*tzp) / 60) % 60);
3438                                         sprintf((str + strlen(str)), ((min != 0) ? " %+03d:%02d" : " %+03d"), hour, min);
3439                                 }
3440                         }
3441                         break;
3442         }
3443
3444         return TRUE;
3445 }       /* EncodeDateTime() */
3446
3447
3448 /* EncodeInterval()
3449  * Interpret time structure as a delta time and convert to string.
3450  *
3451  * Support "traditional Postgres" and ISO-8601 styles.
3452  * Actually, afaik ISO does not address time interval formatting,
3453  *      but this looks similar to the spec for absolute date/time.
3454  * - thomas 1998-04-30
3455  */
3456 int
3457 EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
3458 {
3459         int                     is_before = FALSE;
3460         int                     is_nonzero = FALSE;
3461         char       *cp = str;
3462
3463         /*
3464          * The sign of year and month are guaranteed to match, since they are
3465          * stored internally as "month". But we'll need to check for is_before
3466          * and is_nonzero when determining the signs of hour/minute/seconds
3467          * fields.
3468          */
3469         switch (style)
3470         {
3471                         /* compatible with ISO date formats */
3472                 case USE_ISO_DATES:
3473                         if (tm->tm_year != 0)
3474                         {
3475                                 sprintf(cp, "%d year%s",
3476                                                 tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
3477                                 cp += strlen(cp);
3478                                 is_before = (tm->tm_year < 0);
3479                                 is_nonzero = TRUE;
3480                         }
3481
3482                         if (tm->tm_mon != 0)
3483                         {
3484                                 sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""),
3485                                                 ((is_before && (tm->tm_mon > 0)) ? "+" : ""),
3486                                                 tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
3487                                 cp += strlen(cp);
3488                                 is_before = (tm->tm_mon < 0);
3489                                 is_nonzero = TRUE;
3490                         }
3491
3492                         if (tm->tm_mday != 0)
3493                         {
3494                                 sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""),
3495                                                 ((is_before && (tm->tm_mday > 0)) ? "+" : ""),
3496                                                 tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
3497                                 cp += strlen(cp);
3498                                 is_before = (tm->tm_mday < 0);
3499                                 is_nonzero = TRUE;
3500                         }
3501                         if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0)
3502                                 || (tm->tm_sec != 0) || (fsec != 0))
3503                         {
3504                                 int                     minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
3505                                                                          || (tm->tm_sec < 0) || (fsec < 0));
3506
3507                                 sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
3508                                                 (minus ? "-" : (is_before ? "+" : "")),
3509                                                 abs(tm->tm_hour), abs(tm->tm_min));
3510                                 cp += strlen(cp);
3511                                 /* Mark as "non-zero" since the fields are now filled in */
3512                                 is_nonzero = TRUE;
3513
3514                                 /* fractional seconds? */
3515                                 if (fsec != 0)
3516                                 {
3517 #ifdef HAVE_INT64_TIMESTAMP
3518                                         sprintf(cp, ":%02d", abs(tm->tm_sec));
3519                                         cp += strlen(cp);
3520                                         sprintf(cp, ".%06d", ((fsec >= 0) ? fsec : -(fsec)));
3521 #else
3522                                         fsec += tm->tm_sec;
3523                                         sprintf(cp, ":%013.10f", fabs(fsec));
3524 #endif
3525                                         TrimTrailingZeros(cp);
3526                                         cp += strlen(cp);
3527                                         is_nonzero = TRUE;
3528                                 }
3529                                 /* otherwise, integer seconds only? */
3530                                 else if (tm->tm_sec != 0)
3531                                 {
3532                                         sprintf(cp, ":%02d", abs(tm->tm_sec));
3533                                         cp += strlen(cp);
3534                                         is_nonzero = TRUE;
3535                                 }
3536                         }
3537                         break;
3538
3539                 case USE_POSTGRES_DATES:
3540                 default:
3541                         strcpy(cp, "@ ");
3542                         cp += strlen(cp);
3543
3544                         if (tm->tm_year != 0)
3545                         {
3546                                 int                     year = tm->tm_year;
3547
3548                                 if (tm->tm_year < 0)
3549                                         year = -year;
3550
3551                                 sprintf(cp, "%d year%s", year,
3552                                                 ((year != 1) ? "s" : ""));
3553                                 cp += strlen(cp);
3554                                 is_before = (tm->tm_year < 0);
3555                                 is_nonzero = TRUE;
3556                         }
3557
3558                         if (tm->tm_mon != 0)
3559                         {
3560                                 int                     mon = tm->tm_mon;
3561
3562                                 if (is_before || ((!is_nonzero) && (tm->tm_mon < 0)))
3563                                         mon = -mon;
3564
3565                                 sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon,
3566                                                 ((mon != 1) ? "s" : ""));
3567                                 cp += strlen(cp);
3568                                 if (!is_nonzero)
3569                                         is_before = (tm->tm_mon < 0);
3570                                 is_nonzero = TRUE;
3571                         }
3572
3573                         if (tm->tm_mday != 0)
3574                         {
3575                                 int                     day = tm->tm_mday;
3576
3577                                 if (is_before || ((!is_nonzero) && (tm->tm_mday < 0)))
3578                                         day = -day;
3579
3580                                 sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day,
3581                                                 ((day != 1) ? "s" : ""));
3582                                 cp += strlen(cp);
3583                                 if (!is_nonzero)
3584                                         is_before = (tm->tm_mday < 0);
3585                                 is_nonzero = TRUE;
3586                         }
3587                         if (tm->tm_hour != 0)
3588                         {
3589                                 int                     hour = tm->tm_hour;
3590
3591                                 if (is_before || ((!is_nonzero) && (tm->tm_hour < 0)))
3592                                         hour = -hour;
3593
3594                                 sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour,
3595                                                 ((hour != 1) ? "s" : ""));
3596                                 cp += strlen(cp);
3597                                 if (!is_nonzero)
3598                                         is_before = (tm->tm_hour < 0);
3599                                 is_nonzero = TRUE;
3600                         }
3601
3602                         if (tm->tm_min != 0)
3603                         {
3604                                 int                     min = tm->tm_min;
3605
3606                                 if (is_before || ((!is_nonzero) && (tm->tm_min < 0)))
3607                                         min = -min;
3608
3609                                 sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min,
3610                                                 ((min != 1) ? "s" : ""));
3611                                 cp += strlen(cp);
3612                                 if (!is_nonzero)
3613                                         is_before = (tm->tm_min < 0);
3614                                 is_nonzero = TRUE;
3615                         }
3616
3617                         /* fractional seconds? */
3618                         if (fsec != 0)
3619                         {
3620 #ifdef HAVE_INT64_TIMESTAMP
3621                                 if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3622                                         tm->tm_sec = -tm->tm_sec;
3623                                 sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""),
3624                                                 tm->tm_sec, (((int) fsec) / 10000));
3625                                 cp += strlen(cp);
3626                                 if (!is_nonzero)
3627                                         is_before = (fsec < 0);
3628 #else
3629                                 fsec_t          sec;
3630
3631                                 fsec += tm->tm_sec;
3632                                 sec = fsec;
3633                                 if (is_before || ((!is_nonzero) && (fsec < 0)))
3634                                         sec = -sec;
3635
3636                                 sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec);
3637                                 cp += strlen(cp);
3638                                 if (!is_nonzero)
3639                                         is_before = (fsec < 0);
3640 #endif
3641                                 is_nonzero = TRUE;
3642
3643                                 /* otherwise, integer seconds only? */
3644                         }
3645                         else if (tm->tm_sec != 0)
3646                         {
3647                                 int                     sec = tm->tm_sec;
3648
3649                                 if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
3650                                         sec = -sec;
3651
3652                                 sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec,
3653                                                 ((sec != 1) ? "s" : ""));
3654                                 cp += strlen(cp);
3655                                 if (!is_nonzero)
3656                                         is_before = (tm->tm_sec < 0);
3657                                 is_nonzero = TRUE;
3658                         }
3659                         break;
3660         }
3661
3662         /* identically zero? then put in a unitless zero... */
3663         if (!is_nonzero)
3664         {
3665                 strcat(cp, "0");
3666                 cp += strlen(cp);
3667         }
3668
3669         if (is_before && (style == USE_POSTGRES_DATES))
3670         {
3671                 strcat(cp, " ago");
3672                 cp += strlen(cp);
3673         }
3674
3675         return 0;
3676 }       /* EncodeInterval() */
3677
3678
3679 /* GUC assign_hook for australian_timezones */
3680 bool
3681 ClearDateCache(bool newval, bool doit, bool interactive)
3682 {
3683         int                     i;
3684
3685         if (doit)
3686         {
3687                 for (i = 0; i < MAXDATEFIELDS; i++)
3688                         datecache[i] = NULL;
3689         }
3690
3691         return true;
3692 }