1 /* src/interfaces/ecpg/pgtypeslib/dt_common.c */
2
3 #include "postgres_fe.h"
4
5 #include <time.h>
6 #include <ctype.h>
7 #include <math.h>
8
9 #include "extern.h"
10 #include "dt.h"
11 #include "pgtypes_timestamp.h"
12
13 int day_tab[2][13] = {
14 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
15 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
16
17 typedef long AbsoluteTime;
18
19 static datetkn datetktbl[] = {
20 /* text, token, lexval */
21 {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
22 {"acsst", DTZ, 37800}, /* Cent. Australia */
23 {"acst", DTZ, -14400}, /* Atlantic/Porto Acre */
24 {"act", TZ, -18000}, /* Atlantic/Porto Acre */
25 {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
26 {"adt", DTZ, -10800}, /* Atlantic Daylight Time */
27 {"aesst", DTZ, 39600}, /* E. Australia */
28 {"aest", TZ, 36000}, /* Australia Eastern Std Time */
29 {"aft", TZ, 16200}, /* Kabul */
30 {"ahst", TZ, -36000}, /* Alaska-Hawaii Std Time */
31 {"akdt", DTZ, -28800}, /* Alaska Daylight Time */
32 {"akst", DTZ, -32400}, /* Alaska Standard Time */
33 {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
34 {"almst", TZ, 25200}, /* Almaty Savings Time */
35 {"almt", TZ, 21600}, /* Almaty Time */
36 {"am", AMPM, AM},
37 {"amst", DTZ, 18000}, /* Armenia Summer Time (Yerevan) */
38 #if 0
39 {"amst", DTZ, -10800}, /* Porto Velho */
40 #endif
41 {"amt", TZ, 14400}, /* Armenia Time (Yerevan) */
42 {"anast", DTZ, 46800}, /* Anadyr Summer Time (Russia) */
43 {"anat", TZ, 43200}, /* Anadyr Time (Russia) */
44 {"apr", MONTH, 4},
45 {"april", MONTH, 4},
46 #if 0
47 aqtst
48 aqtt
49 arst
50 #endif
51 {"art", TZ, -10800}, /* Argentina Time */
52 #if 0
53 ashst
54 ast /* Atlantic Standard Time, Arabia Standard
55 * Time, Acre Standard Time */
56 #endif
57 {"ast", TZ, -14400}, /* Atlantic Std Time (Canada) */
58 {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
59 {"aug", MONTH, 8},
60 {"august", MONTH, 8},
61 {"awsst", DTZ, 32400}, /* W. Australia */
62 {"awst", TZ, 28800}, /* W. Australia */
63 {"awt", DTZ, -10800},
64 {"azost", DTZ, 0}, /* Azores Summer Time */
65 {"azot", TZ, -3600}, /* Azores Time */
66 {"azst", DTZ, 18000}, /* Azerbaijan Summer Time */
67 {"azt", TZ, 14400}, /* Azerbaijan Time */
68 {DB_C, ADBC, BC}, /* "bc" for years < 0 */
69 {"bdst", TZ, 7200}, /* British Double Summer Time */
70 {"bdt", TZ, 21600}, /* Dacca */
71 {"bnt", TZ, 28800}, /* Brunei Darussalam Time */
72 {"bort", TZ, 28800}, /* Borneo Time (Indonesia) */
73 #if 0
74 bortst
75 bost
76 #endif
77 {"bot", TZ, -14400}, /* Bolivia Time */
78 {"bra", TZ, -10800}, /* Brazil Time */
79 #if 0
80 brst
81 brt
82 #endif
83 {"bst", DTZ, 3600}, /* British Summer Time */
84 #if 0
85 {"bst", TZ, -10800}, /* Brazil Standard Time */
86 {"bst", DTZ, -39600}, /* Bering Summer Time */
87 #endif
88 {"bt", TZ, 10800}, /* Baghdad Time */
89 {"btt", TZ, 21600}, /* Bhutan Time */
90 {"cadt", DTZ, 37800}, /* Central Australian DST */
91 {"cast", TZ, 34200}, /* Central Australian ST */
92 {"cat", TZ, -36000}, /* Central Alaska Time */
93 {"cct", TZ, 28800}, /* China Coast Time */
94 #if 0
95 {"cct", TZ, 23400}, /* Indian Cocos (Island) Time */
96 #endif
97 {"cdt", DTZ, -18000}, /* Central Daylight Time */
98 {"cest", DTZ, 7200}, /* Central European Dayl.Time */
99 {"cet", TZ, 3600}, /* Central European Time */
100 {"cetdst", DTZ, 7200}, /* Central European Dayl.Time */
101 {"chadt", DTZ, 49500}, /* Chatham Island Daylight Time (13:45) */
102 {"chast", TZ, 45900}, /* Chatham Island Time (12:45) */
103 #if 0
104 ckhst
105 #endif
106 {"ckt", TZ, 43200}, /* Cook Islands Time */
107 {"clst", DTZ, -10800}, /* Chile Summer Time */
108 {"clt", TZ, -14400}, /* Chile Time */
109 #if 0
110 cost
111 #endif
112 {"cot", TZ, -18000}, /* Columbia Time */
113 {"cst", TZ, -21600}, /* Central Standard Time */
114 {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
115 #if 0
116 cvst
117 #endif
118 {"cvt", TZ, 25200}, /* Christmas Island Time (Indian Ocean) */
119 {"cxt", TZ, 25200}, /* Christmas Island Time (Indian Ocean) */
120 {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
121 {"davt", TZ, 25200}, /* Davis Time (Antarctica) */
122 {"ddut", TZ, 36000}, /* Dumont-d'Urville Time (Antarctica) */
123 {"dec", MONTH, 12},
124 {"december", MONTH, 12},
125 {"dnt", TZ, 3600}, /* Dansk Normal Tid */
126 {"dow", UNITS, DTK_DOW}, /* day of week */
127 {"doy", UNITS, DTK_DOY}, /* day of year */
128 {"dst", DTZMOD, SECS_PER_HOUR},
129 #if 0
130 {"dusst", DTZ, 21600}, /* Dushanbe Summer Time */
131 #endif
132 {"easst", DTZ, -18000}, /* Easter Island Summer Time */
133 {"east", TZ, -21600}, /* Easter Island Time */
134 {"eat", TZ, 10800}, /* East Africa Time */
135 #if 0
136 {"east", DTZ, 14400}, /* Indian Antananarivo Savings Time */
137 {"eat", TZ, 10800}, /* Indian Antananarivo Time */
138 {"ect", TZ, -14400}, /* Eastern Caribbean Time */
139 {"ect", TZ, -18000}, /* Ecuador Time */
140 #endif
141 {"edt", DTZ, -14400}, /* Eastern Daylight Time */
142 {"eest", DTZ, 10800}, /* Eastern Europe Summer Time */
143 {"eet", TZ, 7200}, /* East. Europe, USSR Zone 1 */
144 {"eetdst", DTZ, 10800}, /* Eastern Europe Daylight Time */
145 {"egst", DTZ, 0}, /* East Greenland Summer Time */
146 {"egt", TZ, -3600}, /* East Greenland Time */
147 #if 0
148 ehdt
149 #endif
150 {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
151 {"est", TZ, -18000}, /* Eastern Standard Time */
152 {"feb", MONTH, 2},
153 {"february", MONTH, 2},
154 {"fjst", DTZ, -46800}, /* Fiji Summer Time (13 hour offset!) */
155 {"fjt", TZ, -43200}, /* Fiji Time */
156 {"fkst", DTZ, -10800}, /* Falkland Islands Summer Time */
157 {"fkt", TZ, -7200}, /* Falkland Islands Time */
158 #if 0
159 fnst
160 fnt
161 #endif
162 {"fri", DOW, 5},
163 {"friday", DOW, 5},
164 {"fst", TZ, 3600}, /* French Summer Time */
165 {"fwt", DTZ, 7200}, /* French Winter Time */
166 {"galt", TZ, -21600}, /* Galapagos Time */
167 {"gamt", TZ, -32400}, /* Gambier Time */
168 {"gest", DTZ, 18000}, /* Georgia Summer Time */
169 {"get", TZ, 14400}, /* Georgia Time */
170 {"gft", TZ, -10800}, /* French Guiana Time */
171 #if 0
172 ghst
173 #endif
174 {"gilt", TZ, 43200}, /* Gilbert Islands Time */
175 {"gmt", TZ, 0}, /* Greenwish Mean Time */
176 {"gst", TZ, 36000}, /* Guam Std Time, USSR Zone 9 */
177 {"gyt", TZ, -14400}, /* Guyana Time */
178 {"h", UNITS, DTK_HOUR}, /* "hour" */
179 #if 0
180 hadt
181 hast
182 #endif
183 {"hdt", DTZ, -32400}, /* Hawaii/Alaska Daylight Time */
184 #if 0
185 hkst
186 #endif
187 {"hkt", TZ, 28800}, /* Hong Kong Time */
188 #if 0
189 {"hmt", TZ, 10800}, /* Hellas ? ? */
190 hovst
191 hovt
192 #endif
193 {"hst", TZ, -36000}, /* Hawaii Std Time */
194 #if 0
195 hwt
196 #endif
197 {"ict", TZ, 25200}, /* Indochina Time */
198 {"idle", TZ, 43200}, /* Intl. Date Line, East */
199 {"idlw", TZ, -43200}, /* Intl. Date Line, West */
200 #if 0
201 idt /* Israeli, Iran, Indian Daylight Time */
202 #endif
203 {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
204 {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
205 {"iot", TZ, 18000}, /* Indian Chagos Time */
206 {"irkst", DTZ, 32400}, /* Irkutsk Summer Time */
207 {"irkt", TZ, 28800}, /* Irkutsk Time */
208 {"irt", TZ, 12600}, /* Iran Time */
209 {"isodow", UNITS, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */
210 #if 0
211 isst
212 #endif
213 {"ist", TZ, 7200}, /* Israel */
214 {"it", TZ, 12600}, /* Iran Time */
215 {"j", UNITS, DTK_JULIAN},
216 {"jan", MONTH, 1},
217 {"january", MONTH, 1},
218 {"javt", TZ, 25200}, /* Java Time (07:00? see JT) */
219 {"jayt", TZ, 32400}, /* Jayapura Time (Indonesia) */
220 {"jd", UNITS, DTK_JULIAN},
221 {"jst", TZ, 32400}, /* Japan Std Time,USSR Zone 8 */
222 {"jt", TZ, 27000}, /* Java Time (07:30? see JAVT) */
223 {"jul", MONTH, 7},
224 {"julian", UNITS, DTK_JULIAN},
225 {"july", MONTH, 7},
226 {"jun", MONTH, 6},
227 {"june", MONTH, 6},
228 {"kdt", DTZ, 36000}, /* Korea Daylight Time */
229 {"kgst", DTZ, 21600}, /* Kyrgyzstan Summer Time */
230 {"kgt", TZ, 18000}, /* Kyrgyzstan Time */
231 {"kost", TZ, 43200}, /* Kosrae Time */
232 {"krast", DTZ, 25200}, /* Krasnoyarsk Summer Time */
233 {"krat", TZ, 28800}, /* Krasnoyarsk Standard Time */
234 {"kst", TZ, 32400}, /* Korea Standard Time */
235 {"lhdt", DTZ, 39600}, /* Lord Howe Daylight Time, Australia */
236 {"lhst", TZ, 37800}, /* Lord Howe Standard Time, Australia */
237 {"ligt", TZ, 36000}, /* From Melbourne, Australia */
238 {"lint", TZ, 50400}, /* Line Islands Time (Kiribati; +14 hours!) */
239 {"lkt", TZ, 21600}, /* Lanka Time */
240 {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
241 {"magst", DTZ, 43200}, /* Magadan Summer Time */
242 {"magt", TZ, 39600}, /* Magadan Time */
243 {"mar", MONTH, 3},
244 {"march", MONTH, 3},
245 {"mart", TZ, -34200}, /* Marquesas Time */
246 {"mawt", TZ, 21600}, /* Mawson, Antarctica */
247 {"may", MONTH, 5},
248 {"mdt", DTZ, -21600}, /* Mountain Daylight Time */
249 {"mest", DTZ, 7200}, /* Middle Europe Summer Time */
250 {"met", TZ, 3600}, /* Middle Europe Time */
251 {"metdst", DTZ, 7200}, /* Middle Europe Daylight Time */
252 {"mewt", TZ, 3600}, /* Middle Europe Winter Time */
253 {"mez", TZ, 3600}, /* Middle Europe Zone */
254 {"mht", TZ, 43200}, /* Kwajalein */
255 {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
256 {"mmt", TZ, 23400}, /* Myannar Time */
257 {"mon", DOW, 1},
258 {"monday", DOW, 1},
259 #if 0
260 most
261 #endif
262 {"mpt", TZ, 36000}, /* North Mariana Islands Time */
263 {"msd", DTZ, 14400}, /* Moscow Summer Time */
264 {"msk", TZ, 10800}, /* Moscow Time */
265 {"mst", TZ, -25200}, /* Mountain Standard Time */
266 {"mt", TZ, 30600}, /* Moluccas Time */
267 {"mut", TZ, 14400}, /* Mauritius Island Time */
268 {"mvt", TZ, 18000}, /* Maldives Island Time */
269 {"myt", TZ, 28800}, /* Malaysia Time */
270 #if 0
271 ncst
272 #endif
273 {"nct", TZ, 39600}, /* New Caledonia Time */
274 {"ndt", DTZ, -9000}, /* Nfld. Daylight Time */
275 {"nft", TZ, -12600}, /* Newfoundland Standard Time */
276 {"nor", TZ, 3600}, /* Norway Standard Time */
277 {"nov", MONTH, 11},
278 {"november", MONTH, 11},
279 {"novst", DTZ, 25200}, /* Novosibirsk Summer Time */
280 {"novt", TZ, 21600}, /* Novosibirsk Standard Time */
281 {NOW, RESERV, DTK_NOW}, /* current transaction time */
282 {"npt", TZ, 20700}, /* Nepal Standard Time (GMT-5:45) */
283 {"nst", TZ, -12600}, /* Nfld. Standard Time */
284 {"nt", TZ, -39600}, /* Nome Time */
285 {"nut", TZ, -39600}, /* Niue Time */
286 {"nzdt", DTZ, 46800}, /* New Zealand Daylight Time */
287 {"nzst", TZ, 43200}, /* New Zealand Standard Time */
288 {"nzt", TZ, 43200}, /* New Zealand Time */
289 {"oct", MONTH, 10},
290 {"october", MONTH, 10},
291 {"omsst", DTZ, 25200}, /* Omsk Summer Time */
292 {"omst", TZ, 21600}, /* Omsk Time */
293 {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
294 {"pdt", DTZ, -25200}, /* Pacific Daylight Time */
295 #if 0
296 pest
297 #endif
298 {"pet", TZ, -18000}, /* Peru Time */
299 {"petst", DTZ, 46800}, /* Petropavlovsk-Kamchatski Summer Time */
300 {"pett", TZ, 43200}, /* Petropavlovsk-Kamchatski Time */
301 {"pgt", TZ, 36000}, /* Papua New Guinea Time */
302 {"phot", TZ, 46800}, /* Phoenix Islands (Kiribati) Time */
303 #if 0
304 phst
305 #endif
306 {"pht", TZ, 28800}, /* Philippine Time */
307 {"pkt", TZ, 18000}, /* Pakistan Time */
308 {"pm", AMPM, PM},
309 {"pmdt", DTZ, -7200}, /* Pierre & Miquelon Daylight Time */
310 #if 0
311 pmst
312 #endif
313 {"pont", TZ, 39600}, /* Ponape Time (Micronesia) */
314 {"pst", TZ, -28800}, /* Pacific Standard Time */
315 {"pwt", TZ, 32400}, /* Palau Time */
316 {"pyst", DTZ, -10800}, /* Paraguay Summer Time */
317 {"pyt", TZ, -14400}, /* Paraguay Time */
318 {"ret", DTZ, 14400}, /* Reunion Island Time */
319 {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
320 {"sadt", DTZ, 37800}, /* S. Australian Dayl. Time */
321 #if 0
322 samst
323 samt
324 #endif
325 {"sast", TZ, 34200}, /* South Australian Std Time */
326 {"sat", DOW, 6},
327 {"saturday", DOW, 6},
328 #if 0
329 sbt
330 #endif
331 {"sct", DTZ, 14400}, /* Mahe Island Time */
332 {"sep", MONTH, 9},
333 {"sept", MONTH, 9},
334 {"september", MONTH, 9},
335 {"set", TZ, -3600}, /* Seychelles Time ?? */
336 #if 0
337 sgt
338 #endif
339 {"sst", DTZ, 7200}, /* Swedish Summer Time */
340 {"sun", DOW, 0},
341 {"sunday", DOW, 0},
342 {"swt", TZ, 3600}, /* Swedish Winter Time */
343 #if 0
344 syot
345 #endif
346 {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
347 {"tft", TZ, 18000}, /* Kerguelen Time */
348 {"that", TZ, -36000}, /* Tahiti Time */
349 {"thu", DOW, 4},
350 {"thur", DOW, 4},
351 {"thurs", DOW, 4},
352 {"thursday", DOW, 4},
353 {"tjt", TZ, 18000}, /* Tajikistan Time */
354 {"tkt", TZ, -36000}, /* Tokelau Time */
355 {"tmt", TZ, 18000}, /* Turkmenistan Time */
356 {TODAY, RESERV, DTK_TODAY}, /* midnight */
357 {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
358 #if 0
359 tost
360 #endif
361 {"tot", TZ, 46800}, /* Tonga Time */
362 #if 0
363 tpt
364 #endif
365 {"truk", TZ, 36000}, /* Truk Time */
366 {"tue", DOW, 2},
367 {"tues", DOW, 2},
368 {"tuesday", DOW, 2},
369 {"tvt", TZ, 43200}, /* Tuvalu Time */
370 #if 0
371 uct
372 #endif
373 {"ulast", DTZ, 32400}, /* Ulan Bator Summer Time */
374 {"ulat", TZ, 28800}, /* Ulan Bator Time */
375 {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
376 {"ut", TZ, 0},
377 {"utc", TZ, 0},
378 {"uyst", DTZ, -7200}, /* Uruguay Summer Time */
379 {"uyt", TZ, -10800}, /* Uruguay Time */
380 {"uzst", DTZ, 21600}, /* Uzbekistan Summer Time */
381 {"uzt", TZ, 18000}, /* Uzbekistan Time */
382 {"vet", TZ, -14400}, /* Venezuela Time */
383 {"vlast", DTZ, 39600}, /* Vladivostok Summer Time */
384 {"vlat", TZ, 36000}, /* Vladivostok Time */
385 #if 0
386 vust
387 #endif
388 {"vut", TZ, 39600}, /* Vanuata Time */
389 {"wadt", DTZ, 28800}, /* West Australian DST */
390 {"wakt", TZ, 43200}, /* Wake Time */
391 #if 0
392 warst
393 #endif
394 {"wast", TZ, 25200}, /* West Australian Std Time */
395 {"wat", TZ, -3600}, /* West Africa Time */
396 {"wdt", DTZ, 32400}, /* West Australian DST */
397 {"wed", DOW, 3},
398 {"wednesday", DOW, 3},
399 {"weds", DOW, 3},
400 {"west", DTZ, 3600}, /* Western Europe Summer Time */
401 {"wet", TZ, 0}, /* Western Europe */
402 {"wetdst", DTZ, 3600}, /* Western Europe Daylight Savings Time */
403 {"wft", TZ, 43200}, /* Wallis and Futuna Time */
404 {"wgst", DTZ, -7200}, /* West Greenland Summer Time */
405 {"wgt", TZ, -10800}, /* West Greenland Time */
406 {"wst", TZ, 28800}, /* West Australian Standard Time */
407 {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
408 {"yakst", DTZ, 36000}, /* Yakutsk Summer Time */
409 {"yakt", TZ, 32400}, /* Yakutsk Time */
410 {"yapt", TZ, 36000}, /* Yap Time (Micronesia) */
411 {"ydt", DTZ, -28800}, /* Yukon Daylight Time */
412 {"yekst", DTZ, 21600}, /* Yekaterinburg Summer Time */
413 {"yekt", TZ, 18000}, /* Yekaterinburg Time */
414 {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
415 {"yst", TZ, -32400}, /* Yukon Standard Time */
416 {"z", TZ, 0}, /* time zone tag per ISO-8601 */
417 {"zp4", TZ, -14400}, /* UTC +4 hours. */
418 {"zp5", TZ, -18000}, /* UTC +5 hours. */
419 {"zp6", TZ, -21600}, /* UTC +6 hours. */
420 {ZULU, TZ, 0}, /* UTC */
421 };
422
423 static datetkn deltatktbl[] = {
424 /* text, token, lexval */
425 {"@", IGNORE_DTF, 0}, /* postgres relative prefix */
426 {DAGO, AGO, 0}, /* "ago" indicates negative time offset */
427 {"c", UNITS, DTK_CENTURY}, /* "century" relative */
428 {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
429 {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
430 {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
431 {"d", UNITS, DTK_DAY}, /* "day" relative */
432 {DDAY, UNITS, DTK_DAY}, /* "day" relative */
433 {"days", UNITS, DTK_DAY}, /* "days" relative */
434 {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
435 {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
436 {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
437 {"decs", UNITS, DTK_DECADE}, /* "decades" relative */
438 {"h", UNITS, DTK_HOUR}, /* "hour" relative */
439 {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
440 {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
441 {"hr", UNITS, DTK_HOUR}, /* "hour" relative */
442 {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
443 {INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */
444 {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
445 {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
446 {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
447 {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
448 {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
449 {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
450 {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
451 {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
452 {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
453 {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
454 {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
455 {"mon", UNITS, DTK_MONTH}, /* "months" relative */
456 {"mons", UNITS, DTK_MONTH}, /* "months" relative */
457 {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
458 {"months", UNITS, DTK_MONTH},
459 {"ms", UNITS, DTK_MILLISEC},
460 {"msec", UNITS, DTK_MILLISEC},
461 {DMILLISEC, UNITS, DTK_MILLISEC},
462 {"mseconds", UNITS, DTK_MILLISEC},
463 {"msecs", UNITS, DTK_MILLISEC},
464 {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
465 {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
466 {"s", UNITS, DTK_SECOND},
467 {"sec", UNITS, DTK_SECOND},
468 {DSECOND, UNITS, DTK_SECOND},
469 {"seconds", UNITS, DTK_SECOND},
470 {"secs", UNITS, DTK_SECOND},
471 {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
472 {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
473 {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
474 {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
475 {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
476 {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
477 {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
478 {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
479 {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
480 {"w", UNITS, DTK_WEEK}, /* "week" relative */
481 {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
482 {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
483 {"y", UNITS, DTK_YEAR}, /* "year" relative */
484 {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
485 {"years", UNITS, DTK_YEAR}, /* "years" relative */
486 {"yr", UNITS, DTK_YEAR}, /* "year" relative */
487 {"yrs", UNITS, DTK_YEAR}, /* "years" relative */
488 };
489
490 static const unsigned int szdatetktbl = lengthof(datetktbl);
491 static const unsigned int szdeltatktbl = lengthof(deltatktbl);
492
493 static datetkn *datecache[MAXDATEFIELDS] = {NULL};
494
495 static datetkn *deltacache[MAXDATEFIELDS] = {NULL};
496
497 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
498
499 char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL};
500
501 char *pgtypes_date_weekdays_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
502
503 char *pgtypes_date_months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL};
504
505 static datetkn *
datebsearch(char * key,datetkn * base,unsigned int nel)506 datebsearch(char *key, datetkn *base, unsigned int nel)
507 {
508 if (nel > 0)
509 {
510 datetkn *last = base + nel - 1,
511 *position;
512 int result;
513
514 while (last >= base)
515 {
516 position = base + ((last - base) >> 1);
517 /* precheck the first character for a bit of extra speed */
518 result = (int) key[0] - (int) position->token[0];
519 if (result == 0)
520 {
521 /* use strncmp so that we match truncated tokens */
522 result = strncmp(key, position->token, TOKMAXLEN);
523 if (result == 0)
524 return position;
525 }
526 if (result < 0)
527 last = position - 1;
528 else
529 base = position + 1;
530 }
531 }
532 return NULL;
533 }
534
535 /* DecodeUnits()
536 * Decode text string using lookup table.
537 * This routine supports time interval decoding.
538 */
539 int
DecodeUnits(int field,char * lowtoken,int * val)540 DecodeUnits(int field, char *lowtoken, int *val)
541 {
542 int type;
543 datetkn *tp;
544
545 /* use strncmp so that we match truncated tokens */
546 if (deltacache[field] != NULL &&
547 strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
548 tp = deltacache[field];
549 else
550 tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
551 deltacache[field] = tp;
552 if (tp == NULL)
553 {
554 type = UNKNOWN_FIELD;
555 *val = 0;
556 }
557 else
558 {
559 type = tp->type;
560 *val = tp->value;
561 }
562
563 return type;
564 } /* DecodeUnits() */
565
566 /*
567 * Calendar time to Julian date conversions.
568 * Julian date is commonly used in astronomical applications,
569 * since it is numerically accurate and computationally simple.
570 * The algorithms here will accurately convert between Julian day
571 * and calendar date for all non-negative Julian days
572 * (i.e. from Nov 24, -4713 on).
573 *
574 * These routines will be used by other date/time packages
575 * - thomas 97/02/25
576 *
577 * Rewritten to eliminate overflow problems. This now allows the
578 * routines to work correctly for all Julian day counts from
579 * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
580 * a 32-bit integer. Longer types should also work to the limits
581 * of their precision.
582 */
583
584 int
date2j(int y,int m,int d)585 date2j(int y, int m, int d)
586 {
587 int julian;
588 int century;
589
590 if (m > 2)
591 {
592 m += 1;
593 y += 4800;
594 }
595 else
596 {
597 m += 13;
598 y += 4799;
599 }
600
601 century = y / 100;
602 julian = y * 365 - 32167;
603 julian += y / 4 - century + century / 4;
604 julian += 7834 * m / 256 + d;
605
606 return julian;
607 } /* date2j() */
608
609 void
j2date(int jd,int * year,int * month,int * day)610 j2date(int jd, int *year, int *month, int *day)
611 {
612 unsigned int julian;
613 unsigned int quad;
614 unsigned int extra;
615 int y;
616
617 julian = jd;
618 julian += 32044;
619 quad = julian / 146097;
620 extra = (julian - quad * 146097) * 4 + 3;
621 julian += 60 + quad * 3 + extra / 146097;
622 quad = julian / 1461;
623 julian -= quad * 1461;
624 y = julian * 4 / 1461;
625 julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
626 y += quad * 4;
627 *year = y - 4800;
628 quad = julian * 2141 / 65536;
629 *day = julian - 7834 * quad / 256;
630 *month = (quad + 10) % 12 + 1;
631
632 return;
633 } /* j2date() */
634
635 /* DecodeSpecial()
636 * Decode text string using lookup table.
637 * Implement a cache lookup since it is likely that dates
638 * will be related in format.
639 */
640 static int
DecodeSpecial(int field,char * lowtoken,int * val)641 DecodeSpecial(int field, char *lowtoken, int *val)
642 {
643 int type;
644 datetkn *tp;
645
646 /* use strncmp so that we match truncated tokens */
647 if (datecache[field] != NULL &&
648 strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
649 tp = datecache[field];
650 else
651 {
652 tp = NULL;
653 if (!tp)
654 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
655 }
656 datecache[field] = tp;
657 if (tp == NULL)
658 {
659 type = UNKNOWN_FIELD;
660 *val = 0;
661 }
662 else
663 {
664 type = tp->type;
665 *val = tp->value;
666 }
667
668 return type;
669 } /* DecodeSpecial() */
670
671 /* EncodeDateOnly()
672 * Encode date as local time.
673 */
674 int
EncodeDateOnly(struct tm * tm,int style,char * str,bool EuroDates)675 EncodeDateOnly(struct tm * tm, int style, char *str, bool EuroDates)
676 {
677 if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
678 return -1;
679
680 switch (style)
681 {
682 case USE_ISO_DATES:
683 /* compatible with ISO date formats */
684 if (tm->tm_year > 0)
685 sprintf(str, "%04d-%02d-%02d",
686 tm->tm_year, tm->tm_mon, tm->tm_mday);
687 else
688 sprintf(str, "%04d-%02d-%02d %s",
689 -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
690 break;
691
692 case USE_SQL_DATES:
693 /* compatible with Oracle/Ingres date formats */
694 if (EuroDates)
695 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
696 else
697 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
698 if (tm->tm_year > 0)
699 sprintf(str + 5, "/%04d", tm->tm_year);
700 else
701 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
702 break;
703
704 case USE_GERMAN_DATES:
705 /* German-style date format */
706 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
707 if (tm->tm_year > 0)
708 sprintf(str + 5, ".%04d", tm->tm_year);
709 else
710 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
711 break;
712
713 case USE_POSTGRES_DATES:
714 default:
715 /* traditional date-only style for Postgres */
716 if (EuroDates)
717 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
718 else
719 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
720 if (tm->tm_year > 0)
721 sprintf(str + 5, "-%04d", tm->tm_year);
722 else
723 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
724 break;
725 }
726
727 return TRUE;
728 } /* EncodeDateOnly() */
729
730 void
TrimTrailingZeros(char * str)731 TrimTrailingZeros(char *str)
732 {
733 int len = strlen(str);
734
735 /* chop off trailing zeros... but leave at least 2 fractional digits */
736 while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
737 {
738 len--;
739 *(str + len) = '\0';
740 }
741 }
742
743 /* EncodeDateTime()
744 * Encode date and time interpreted as local time.
745 *
746 * tm and fsec are the value to encode, print_tz determines whether to include
747 * a time zone (the difference between timestamp and timestamptz types), tz is
748 * the numeric time zone offset, tzn is the textual time zone, which if
749 * specified will be used instead of tz by some styles, style is the date
750 * style, str is where to write the output.
751 *
752 * Supported date styles:
753 * Postgres - day mon hh:mm:ss yyyy tz
754 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
755 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
756 * German - dd.mm.yyyy hh:mm:ss tz
757 * Variants (affects order of month and day for Postgres and SQL styles):
758 * US - mm/dd/yyyy
759 * European - dd/mm/yyyy
760 */
761 int
EncodeDateTime(struct tm * tm,fsec_t fsec,bool print_tz,int tz,const char * tzn,int style,char * str,bool EuroDates)762 EncodeDateTime(struct tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
763 {
764 int day,
765 hour,
766 min;
767
768 /*
769 * Negative tm_isdst means we have no valid time zone translation.
770 */
771 if (tm->tm_isdst < 0)
772 print_tz = false;
773
774 switch (style)
775 {
776 case USE_ISO_DATES:
777 /* Compatible with ISO-8601 date formats */
778
779 sprintf(str, "%04d-%02d-%02d %02d:%02d",
780 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
781 tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
782
783 /*
784 * Print fractional seconds if any. The field widths here should
785 * be at least equal to MAX_TIMESTAMP_PRECISION.
786 *
787 * In float mode, don't print fractional seconds before 1 AD,
788 * since it's unlikely there's any precision left ...
789 */
790 #ifdef HAVE_INT64_TIMESTAMP
791 if (fsec != 0)
792 {
793 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
794 #else
795 if ((fsec != 0) && (tm->tm_year > 0))
796 {
797 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
798 #endif
799 TrimTrailingZeros(str);
800 }
801 else
802 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
803
804 if (tm->tm_year <= 0)
805 sprintf(str + strlen(str), " BC");
806
807 if (print_tz)
808 {
809 hour = -(tz / SECS_PER_HOUR);
810 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
811 if (min != 0)
812 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
813 else
814 sprintf(str + strlen(str), "%+03d", hour);
815 }
816 break;
817
818 case USE_SQL_DATES:
819 /* Compatible with Oracle/Ingres date formats */
820
821 if (EuroDates)
822 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
823 else
824 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
825
826 sprintf(str + 5, "/%04d %02d:%02d",
827 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
828 tm->tm_hour, tm->tm_min);
829
830 /*
831 * Print fractional seconds if any. The field widths here should
832 * be at least equal to MAX_TIMESTAMP_PRECISION.
833 *
834 * In float mode, don't print fractional seconds before 1 AD,
835 * since it's unlikely there's any precision left ...
836 */
837 #ifdef HAVE_INT64_TIMESTAMP
838 if (fsec != 0)
839 {
840 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
841 #else
842 if (fsec != 0 && tm->tm_year > 0)
843 {
844 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
845 #endif
846 TrimTrailingZeros(str);
847 }
848 else
849 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
850
851 if (tm->tm_year <= 0)
852 sprintf(str + strlen(str), " BC");
853
854 /*
855 * Note: the uses of %.*s in this function would be risky if the
856 * timezone names ever contain non-ASCII characters. However, all
857 * TZ abbreviations in the IANA database are plain ASCII.
858 */
859
860 if (print_tz)
861 {
862 if (tzn)
863 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
864 else
865 {
866 hour = -(tz / SECS_PER_HOUR);
867 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
868 if (min != 0)
869 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
870 else
871 sprintf(str + strlen(str), "%+03d", hour);
872 }
873 }
874 break;
875
876 case USE_GERMAN_DATES:
877 /* German variant on European style */
878
879 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
880
881 sprintf(str + 5, ".%04d %02d:%02d",
882 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
883 tm->tm_hour, tm->tm_min);
884
885 /*
886 * Print fractional seconds if any. The field widths here should
887 * be at least equal to MAX_TIMESTAMP_PRECISION.
888 *
889 * In float mode, don't print fractional seconds before 1 AD,
890 * since it's unlikely there's any precision left ...
891 */
892 #ifdef HAVE_INT64_TIMESTAMP
893 if (fsec != 0)
894 {
895 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
896 #else
897 if (fsec != 0 && tm->tm_year > 0)
898 {
899 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
900 #endif
901 TrimTrailingZeros(str);
902 }
903 else
904 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
905
906 if (tm->tm_year <= 0)
907 sprintf(str + strlen(str), " BC");
908
909 if (print_tz)
910 {
911 if (tzn)
912 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
913 else
914 {
915 hour = -(tz / SECS_PER_HOUR);
916 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
917 if (min != 0)
918 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
919 else
920 sprintf(str + strlen(str), "%+03d", hour);
921 }
922 }
923 break;
924
925 case USE_POSTGRES_DATES:
926 default:
927 /* Backward-compatible with traditional Postgres abstime dates */
928
929 day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
930 tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
931
932 memcpy(str, days[tm->tm_wday], 3);
933 strcpy(str + 3, " ");
934
935 if (EuroDates)
936 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
937 else
938 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
939
940 sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
941
942 /*
943 * Print fractional seconds if any. The field widths here should
944 * be at least equal to MAX_TIMESTAMP_PRECISION.
945 *
946 * In float mode, don't print fractional seconds before 1 AD,
947 * since it's unlikely there's any precision left ...
948 */
949 #ifdef HAVE_INT64_TIMESTAMP
950 if (fsec != 0)
951 {
952 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
953 #else
954 if (fsec != 0 && tm->tm_year > 0)
955 {
956 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
957 #endif
958 TrimTrailingZeros(str);
959 }
960 else
961 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
962
963 sprintf(str + strlen(str), " %04d",
964 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
965 if (tm->tm_year <= 0)
966 sprintf(str + strlen(str), " BC");
967
968 if (print_tz)
969 {
970 if (tzn)
971 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
972 else
973 {
974 /*
975 * We have a time zone, but no string version. Use the
976 * numeric form, but be sure to include a leading space to
977 * avoid formatting something which would be rejected by
978 * the date/time parser later. - thomas 2001-10-19
979 */
980 hour = -(tz / SECS_PER_HOUR);
981 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
982 if (min != 0)
983 sprintf(str + strlen(str), " %+03d:%02d", hour, min);
984 else
985 sprintf(str + strlen(str), " %+03d", hour);
986 }
987 }
988 break;
989 }
990
991 return TRUE;
992 } /* EncodeDateTime() */
993
994 int
995 GetEpochTime(struct tm * tm)
996 {
997 struct tm *t0;
998 time_t epoch = 0;
999
1000 t0 = gmtime(&epoch);
1001
1002 if (t0)
1003 {
1004 tm->tm_year = t0->tm_year + 1900;
1005 tm->tm_mon = t0->tm_mon + 1;
1006 tm->tm_mday = t0->tm_mday;
1007 tm->tm_hour = t0->tm_hour;
1008 tm->tm_min = t0->tm_min;
1009 tm->tm_sec = t0->tm_sec;
1010
1011 return 0;
1012 }
1013
1014 return -1;
1015 } /* GetEpochTime() */
1016
1017 static void
1018 abstime2tm(AbsoluteTime _time, int *tzp, struct tm * tm, char **tzn)
1019 {
1020 time_t time = (time_t) _time;
1021 struct tm *tx;
1022
1023 errno = 0;
1024 if (tzp != NULL)
1025 tx = localtime((time_t *) &time);
1026 else
1027 tx = gmtime((time_t *) &time);
1028
1029 if (!tx)
1030 {
1031 errno = PGTYPES_TS_BAD_TIMESTAMP;
1032 return;
1033 }
1034
1035 tm->tm_year = tx->tm_year + 1900;
1036 tm->tm_mon = tx->tm_mon + 1;
1037 tm->tm_mday = tx->tm_mday;
1038 tm->tm_hour = tx->tm_hour;
1039 tm->tm_min = tx->tm_min;
1040 tm->tm_sec = tx->tm_sec;
1041 tm->tm_isdst = tx->tm_isdst;
1042
1043 #if defined(HAVE_TM_ZONE)
1044 tm->tm_gmtoff = tx->tm_gmtoff;
1045 tm->tm_zone = tx->tm_zone;
1046
1047 if (tzp != NULL)
1048 {
1049 /*
1050 * We have a brute force time zone per SQL99? Then use it without
1051 * change since we have already rotated to the time zone.
1052 */
1053 *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
1054
1055 /*
1056 * FreeBSD man pages indicate that this should work - tgl 97/04/23
1057 */
1058 if (tzn != NULL)
1059 {
1060 /*
1061 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1062 * contains an error message, which doesn't fit in the buffer
1063 */
1064 StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
1065 if (strlen(tm->tm_zone) > MAXTZLEN)
1066 tm->tm_isdst = -1;
1067 }
1068 }
1069 else
1070 tm->tm_isdst = -1;
1071 #elif defined(HAVE_INT_TIMEZONE)
1072 if (tzp != NULL)
1073 {
1074 *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
1075
1076 if (tzn != NULL)
1077 {
1078 /*
1079 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1080 * contains an error message, which doesn't fit in the buffer
1081 */
1082 StrNCpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
1083 if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
1084 tm->tm_isdst = -1;
1085 }
1086 }
1087 else
1088 tm->tm_isdst = -1;
1089 #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1090 if (tzp != NULL)
1091 {
1092 /* default to UTC */
1093 *tzp = 0;
1094 if (tzn != NULL)
1095 *tzn = NULL;
1096 }
1097 else
1098 tm->tm_isdst = -1;
1099 #endif
1100 }
1101
1102 void
1103 GetCurrentDateTime(struct tm * tm)
1104 {
1105 int tz;
1106
1107 abstime2tm(time(NULL), &tz, tm, NULL);
1108 }
1109
1110 void
1111 dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
1112 {
1113 #ifdef HAVE_INT64_TIMESTAMP
1114 int64 time;
1115 #else
1116 double time;
1117 #endif
1118
1119 time = jd;
1120 #ifdef HAVE_INT64_TIMESTAMP
1121 *hour = time / USECS_PER_HOUR;
1122 time -= (*hour) * USECS_PER_HOUR;
1123 *min = time / USECS_PER_MINUTE;
1124 time -= (*min) * USECS_PER_MINUTE;
1125 *sec = time / USECS_PER_SEC;
1126 *fsec = time - (*sec * USECS_PER_SEC);
1127 #else
1128 *hour = time / SECS_PER_HOUR;
1129 time -= (*hour) * SECS_PER_HOUR;
1130 *min = time / SECS_PER_MINUTE;
1131 time -= (*min) * SECS_PER_MINUTE;
1132 *sec = time;
1133 *fsec = time - *sec;
1134 #endif
1135 } /* dt2time() */
1136
1137
1138
1139 /* DecodeNumberField()
1140 * Interpret numeric string as a concatenated date or time field.
1141 * Use the context of previously decoded fields to help with
1142 * the interpretation.
1143 */
1144 static int
1145 DecodeNumberField(int len, char *str, int fmask,
1146 int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
1147 {
1148 char *cp;
1149
1150 /*
1151 * Have a decimal point? Then this is a date or something with a seconds
1152 * field...
1153 */
1154 if ((cp = strchr(str, '.')) != NULL)
1155 {
1156 #ifdef HAVE_INT64_TIMESTAMP
1157 char fstr[7];
1158 int i;
1159
1160 cp++;
1161
1162 /*
1163 * OK, we have at most six digits to care about. Let's construct a
1164 * string with those digits, zero-padded on the right, and then do the
1165 * conversion to an integer.
1166 *
1167 * XXX This truncates the seventh digit, unlike rounding it as do the
1168 * backend and the !HAVE_INT64_TIMESTAMP case.
1169 */
1170 for (i = 0; i < 6; i++)
1171 fstr[i] = *cp != '\0' ? *cp++ : '0';
1172 fstr[i] = '\0';
1173 *fsec = strtol(fstr, NULL, 10);
1174 #else
1175 *fsec = strtod(cp, NULL);
1176 #endif
1177 *cp = '\0';
1178 len = strlen(str);
1179 }
1180 /* No decimal point and no complete date yet? */
1181 else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1182 {
1183 /* yyyymmdd? */
1184 if (len == 8)
1185 {
1186 *tmask = DTK_DATE_M;
1187
1188 tm->tm_mday = atoi(str + 6);
1189 *(str + 6) = '\0';
1190 tm->tm_mon = atoi(str + 4);
1191 *(str + 4) = '\0';
1192 tm->tm_year = atoi(str + 0);
1193
1194 return DTK_DATE;
1195 }
1196 /* yymmdd? */
1197 else if (len == 6)
1198 {
1199 *tmask = DTK_DATE_M;
1200 tm->tm_mday = atoi(str + 4);
1201 *(str + 4) = '\0';
1202 tm->tm_mon = atoi(str + 2);
1203 *(str + 2) = '\0';
1204 tm->tm_year = atoi(str + 0);
1205 *is2digits = TRUE;
1206
1207 return DTK_DATE;
1208 }
1209 /* yyddd? */
1210 else if (len == 5)
1211 {
1212 *tmask = DTK_DATE_M;
1213 tm->tm_mday = atoi(str + 2);
1214 *(str + 2) = '\0';
1215 tm->tm_mon = 1;
1216 tm->tm_year = atoi(str + 0);
1217 *is2digits = TRUE;
1218
1219 return DTK_DATE;
1220 }
1221 }
1222
1223 /* not all time fields are specified? */
1224 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
1225 {
1226 /* hhmmss */
1227 if (len == 6)
1228 {
1229 *tmask = DTK_TIME_M;
1230 tm->tm_sec = atoi(str + 4);
1231 *(str + 4) = '\0';
1232 tm->tm_min = atoi(str + 2);
1233 *(str + 2) = '\0';
1234 tm->tm_hour = atoi(str + 0);
1235
1236 return DTK_TIME;
1237 }
1238 /* hhmm? */
1239 else if (len == 4)
1240 {
1241 *tmask = DTK_TIME_M;
1242 tm->tm_sec = 0;
1243 tm->tm_min = atoi(str + 2);
1244 *(str + 2) = '\0';
1245 tm->tm_hour = atoi(str + 0);
1246
1247 return DTK_TIME;
1248 }
1249 }
1250
1251 return -1;
1252 } /* DecodeNumberField() */
1253
1254
1255 /* DecodeNumber()
1256 * Interpret plain numeric field as a date value in context.
1257 */
1258 static int
1259 DecodeNumber(int flen, char *str, int fmask,
1260 int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits, bool EuroDates)
1261 {
1262 int val;
1263 char *cp;
1264
1265 *tmask = 0;
1266
1267 val = strtol(str, &cp, 10);
1268 if (cp == str)
1269 return -1;
1270
1271 if (*cp == '.')
1272 {
1273 /*
1274 * More than two digits? Then could be a date or a run-together time:
1275 * 2001.360 20011225 040506.789
1276 */
1277 if (cp - str > 2)
1278 return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
1279 tmask, tm, fsec, is2digits);
1280
1281 *fsec = strtod(cp, &cp);
1282 if (*cp != '\0')
1283 return -1;
1284 }
1285 else if (*cp != '\0')
1286 return -1;
1287
1288 /* Special case day of year? */
1289 if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
1290 {
1291 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1292 tm->tm_yday = val;
1293 j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
1294 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1295 }
1296
1297 /***
1298 * Enough digits to be unequivocal year? Used to test for 4 digits or
1299 * more, but we now test first for a three-digit doy so anything
1300 * bigger than two digits had better be an explicit year.
1301 * - thomas 1999-01-09
1302 * Back to requiring a 4 digit year. We accept a two digit
1303 * year farther down. - thomas 2000-03-28
1304 ***/
1305 else if (flen >= 4)
1306 {
1307 *tmask = DTK_M(YEAR);
1308
1309 /* already have a year? then see if we can substitute... */
1310 if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1311 tm->tm_year >= 1 && tm->tm_year <= 31)
1312 {
1313 tm->tm_mday = tm->tm_year;
1314 *tmask = DTK_M(DAY);
1315 }
1316
1317 tm->tm_year = val;
1318 }
1319
1320 /* already have year? then could be month */
1321 else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1322 {
1323 *tmask = DTK_M(MONTH);
1324 tm->tm_mon = val;
1325 }
1326 /* no year and EuroDates enabled? then could be day */
1327 else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
1328 !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1329 val >= 1 && val <= 31)
1330 {
1331 *tmask = DTK_M(DAY);
1332 tm->tm_mday = val;
1333 }
1334 else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1335 {
1336 *tmask = DTK_M(MONTH);
1337 tm->tm_mon = val;
1338 }
1339 else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
1340 {
1341 *tmask = DTK_M(DAY);
1342 tm->tm_mday = val;
1343 }
1344
1345 /*
1346 * Check for 2 or 4 or more digits, but currently we reach here only if
1347 * two digits. - thomas 2000-03-28
1348 */
1349 else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
1350 {
1351 *tmask = DTK_M(YEAR);
1352 tm->tm_year = val;
1353
1354 /* adjust ONLY if exactly two digits... */
1355 *is2digits = (flen == 2);
1356 }
1357 else
1358 return -1;
1359
1360 return 0;
1361 } /* DecodeNumber() */
1362
1363 /* DecodeDate()
1364 * Decode date string which includes delimiters.
1365 * Insist on a complete set of fields.
1366 */
1367 static int
1368 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm, bool EuroDates)
1369 {
1370 fsec_t fsec;
1371
1372 int nf = 0;
1373 int i,
1374 len;
1375 int bc = FALSE;
1376 int is2digits = FALSE;
1377 int type,
1378 val,
1379 dmask = 0;
1380 char *field[MAXDATEFIELDS];
1381
1382 /* parse this string... */
1383 while (*str != '\0' && nf < MAXDATEFIELDS)
1384 {
1385 /* skip field separators */
1386 while (!isalnum((unsigned char) *str))
1387 str++;
1388
1389 field[nf] = str;
1390 if (isdigit((unsigned char) *str))
1391 {
1392 while (isdigit((unsigned char) *str))
1393 str++;
1394 }
1395 else if (isalpha((unsigned char) *str))
1396 {
1397 while (isalpha((unsigned char) *str))
1398 str++;
1399 }
1400
1401 /* Just get rid of any non-digit, non-alpha characters... */
1402 if (*str != '\0')
1403 *str++ = '\0';
1404 nf++;
1405 }
1406
1407 #if 0
1408 /* don't allow too many fields */
1409 if (nf > 3)
1410 return -1;
1411 #endif
1412
1413 *tmask = 0;
1414
1415 /* look first for text fields, since that will be unambiguous month */
1416 for (i = 0; i < nf; i++)
1417 {
1418 if (isalpha((unsigned char) *field[i]))
1419 {
1420 type = DecodeSpecial(i, field[i], &val);
1421 if (type == IGNORE_DTF)
1422 continue;
1423
1424 dmask = DTK_M(type);
1425 switch (type)
1426 {
1427 case MONTH:
1428 tm->tm_mon = val;
1429 break;
1430
1431 case ADBC:
1432 bc = (val == BC);
1433 break;
1434
1435 default:
1436 return -1;
1437 }
1438 if (fmask & dmask)
1439 return -1;
1440
1441 fmask |= dmask;
1442 *tmask |= dmask;
1443
1444 /* mark this field as being completed */
1445 field[i] = NULL;
1446 }
1447 }
1448
1449 /* now pick up remaining numeric fields */
1450 for (i = 0; i < nf; i++)
1451 {
1452 if (field[i] == NULL)
1453 continue;
1454
1455 if ((len = strlen(field[i])) <= 0)
1456 return -1;
1457
1458 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
1459 return -1;
1460
1461 if (fmask & dmask)
1462 return -1;
1463
1464 fmask |= dmask;
1465 *tmask |= dmask;
1466 }
1467
1468 if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
1469 return -1;
1470
1471 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1472 if (bc)
1473 {
1474 if (tm->tm_year > 0)
1475 tm->tm_year = -(tm->tm_year - 1);
1476 else
1477 return -1;
1478 }
1479 else if (is2digits)
1480 {
1481 if (tm->tm_year < 70)
1482 tm->tm_year += 2000;
1483 else if (tm->tm_year < 100)
1484 tm->tm_year += 1900;
1485 }
1486
1487 return 0;
1488 } /* DecodeDate() */
1489
1490
1491 /* DecodeTime()
1492 * Decode time string which includes delimiters.
1493 * Only check the lower limit on hours, since this same code
1494 * can be used to represent time spans.
1495 */
1496 int
1497 DecodeTime(char *str, int *tmask, struct tm * tm, fsec_t *fsec)
1498 {
1499 char *cp;
1500
1501 *tmask = DTK_TIME_M;
1502
1503 tm->tm_hour = strtol(str, &cp, 10);
1504 if (*cp != ':')
1505 return -1;
1506 str = cp + 1;
1507 tm->tm_min = strtol(str, &cp, 10);
1508 if (*cp == '\0')
1509 {
1510 tm->tm_sec = 0;
1511 *fsec = 0;
1512 }
1513 else if (*cp != ':')
1514 return -1;
1515 else
1516 {
1517 str = cp + 1;
1518 tm->tm_sec = strtol(str, &cp, 10);
1519 if (*cp == '\0')
1520 *fsec = 0;
1521 else if (*cp == '.')
1522 {
1523 #ifdef HAVE_INT64_TIMESTAMP
1524 char fstr[7];
1525 int i;
1526
1527 cp++;
1528
1529 /*
1530 * OK, we have at most six digits to care about. Let's construct a
1531 * string with those digits, zero-padded on the right, and then do
1532 * the conversion to an integer.
1533 *
1534 * XXX This truncates the seventh digit, unlike rounding it as do
1535 * the backend and the !HAVE_INT64_TIMESTAMP case.
1536 */
1537 for (i = 0; i < 6; i++)
1538 fstr[i] = *cp != '\0' ? *cp++ : '0';
1539 fstr[i] = '\0';
1540 *fsec = strtol(fstr, &cp, 10);
1541 #else
1542 str = cp;
1543 *fsec = strtod(str, &cp);
1544 #endif
1545 if (*cp != '\0')
1546 return -1;
1547 }
1548 else
1549 return -1;
1550 }
1551
1552 /* do a sanity check */
1553 #ifdef HAVE_INT64_TIMESTAMP
1554 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
1555 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
1556 return -1;
1557 #else
1558 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
1559 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= 1)
1560 return -1;
1561 #endif
1562
1563 return 0;
1564 } /* DecodeTime() */
1565
1566 /* DecodeTimezone()
1567 * Interpret string as a numeric timezone.
1568 *
1569 * Note: we allow timezone offsets up to 13:59. There are places that
1570 * use +1300 summer time.
1571 */
1572 static int
1573 DecodeTimezone(char *str, int *tzp)
1574 {
1575 int tz;
1576 int hr,
1577 min;
1578 char *cp;
1579 int len;
1580
1581 /* assume leading character is "+" or "-" */
1582 hr = strtol(str + 1, &cp, 10);
1583
1584 /* explicit delimiter? */
1585 if (*cp == ':')
1586 min = strtol(cp + 1, &cp, 10);
1587 /* otherwise, might have run things together... */
1588 else if (*cp == '\0' && (len = strlen(str)) > 3)
1589 {
1590 min = strtol(str + len - 2, &cp, 10);
1591 if (min < 0 || min >= 60)
1592 return -1;
1593
1594 *(str + len - 2) = '\0';
1595 hr = strtol(str + 1, &cp, 10);
1596 if (hr < 0 || hr > 13)
1597 return -1;
1598 }
1599 else
1600 min = 0;
1601
1602 tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
1603 if (*str == '-')
1604 tz = -tz;
1605
1606 *tzp = -tz;
1607 return *cp != '\0';
1608 } /* DecodeTimezone() */
1609
1610
1611 /* DecodePosixTimezone()
1612 * Interpret string as a POSIX-compatible timezone:
1613 * PST-hh:mm
1614 * PST+h
1615 * - thomas 2000-03-15
1616 */
1617 static int
1618 DecodePosixTimezone(char *str, int *tzp)
1619 {
1620 int val,
1621 tz;
1622 int type;
1623 char *cp;
1624 char delim;
1625
1626 cp = str;
1627 while (*cp != '\0' && isalpha((unsigned char) *cp))
1628 cp++;
1629
1630 if (DecodeTimezone(cp, &tz) != 0)
1631 return -1;
1632
1633 delim = *cp;
1634 *cp = '\0';
1635 type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
1636 *cp = delim;
1637
1638 switch (type)
1639 {
1640 case DTZ:
1641 case TZ:
1642 *tzp = -(val + tz);
1643 break;
1644
1645 default:
1646 return -1;
1647 }
1648
1649 return 0;
1650 } /* DecodePosixTimezone() */
1651
1652 /* ParseDateTime()
1653 * Break string into tokens based on a date/time context.
1654 * Several field types are assigned:
1655 * DTK_NUMBER - digits and (possibly) a decimal point
1656 * DTK_DATE - digits and two delimiters, or digits and text
1657 * DTK_TIME - digits, colon delimiters, and possibly a decimal point
1658 * DTK_STRING - text (no digits)
1659 * DTK_SPECIAL - leading "+" or "-" followed by text
1660 * DTK_TZ - leading "+" or "-" followed by digits
1661 * Note that some field types can hold unexpected items:
1662 * DTK_NUMBER can hold date fields (yy.ddd)
1663 * DTK_STRING can hold months (January) and time zones (PST)
1664 * DTK_DATE can hold Posix time zones (GMT-8)
1665 *
1666 * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
1667 * bytes of space. On output, field[] entries will point into it.
1668 * The field[] and ftype[] arrays must have at least MAXDATEFIELDS entries.
1669 */
1670 int
1671 ParseDateTime(char *timestr, char *lowstr,
1672 char **field, int *ftype, int *numfields, char **endstr)
1673 {
1674 int nf = 0;
1675 char *lp = lowstr;
1676
1677 *endstr = timestr;
1678 /* outer loop through fields */
1679 while (*(*endstr) != '\0')
1680 {
1681 /* Record start of current field */
1682 if (nf >= MAXDATEFIELDS)
1683 return -1;
1684 field[nf] = lp;
1685
1686 /* leading digit? then date or time */
1687 if (isdigit((unsigned char) *(*endstr)))
1688 {
1689 *lp++ = *(*endstr)++;
1690 while (isdigit((unsigned char) *(*endstr)))
1691 *lp++ = *(*endstr)++;
1692
1693 /* time field? */
1694 if (*(*endstr) == ':')
1695 {
1696 ftype[nf] = DTK_TIME;
1697 *lp++ = *(*endstr)++;
1698 while (isdigit((unsigned char) *(*endstr)) ||
1699 (*(*endstr) == ':') || (*(*endstr) == '.'))
1700 *lp++ = *(*endstr)++;
1701 }
1702 /* date field? allow embedded text month */
1703 else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1704 {
1705 /* save delimiting character to use later */
1706 char *dp = (*endstr);
1707
1708 *lp++ = *(*endstr)++;
1709 /* second field is all digits? then no embedded text month */
1710 if (isdigit((unsigned char) *(*endstr)))
1711 {
1712 ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
1713 while (isdigit((unsigned char) *(*endstr)))
1714 *lp++ = *(*endstr)++;
1715
1716 /*
1717 * insist that the delimiters match to get a three-field
1718 * date.
1719 */
1720 if (*(*endstr) == *dp)
1721 {
1722 ftype[nf] = DTK_DATE;
1723 *lp++ = *(*endstr)++;
1724 while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1725 *lp++ = *(*endstr)++;
1726 }
1727 }
1728 else
1729 {
1730 ftype[nf] = DTK_DATE;
1731 while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1732 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1733 }
1734 }
1735
1736 /*
1737 * otherwise, number only and will determine year, month, day, or
1738 * concatenated fields later...
1739 */
1740 else
1741 ftype[nf] = DTK_NUMBER;
1742 }
1743 /* Leading decimal point? Then fractional seconds... */
1744 else if (*(*endstr) == '.')
1745 {
1746 *lp++ = *(*endstr)++;
1747 while (isdigit((unsigned char) *(*endstr)))
1748 *lp++ = *(*endstr)++;
1749
1750 ftype[nf] = DTK_NUMBER;
1751 }
1752
1753 /*
1754 * text? then date string, month, day of week, special, or timezone
1755 */
1756 else if (isalpha((unsigned char) *(*endstr)))
1757 {
1758 ftype[nf] = DTK_STRING;
1759 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1760 while (isalpha((unsigned char) *(*endstr)))
1761 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1762
1763 /*
1764 * Full date string with leading text month? Could also be a POSIX
1765 * time zone...
1766 */
1767 if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1768 {
1769 char *dp = (*endstr);
1770
1771 ftype[nf] = DTK_DATE;
1772 *lp++ = *(*endstr)++;
1773 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
1774 *lp++ = *(*endstr)++;
1775 }
1776 }
1777 /* skip leading spaces */
1778 else if (isspace((unsigned char) *(*endstr)))
1779 {
1780 (*endstr)++;
1781 continue;
1782 }
1783 /* sign? then special or numeric timezone */
1784 else if (*(*endstr) == '+' || *(*endstr) == '-')
1785 {
1786 *lp++ = *(*endstr)++;
1787 /* soak up leading whitespace */
1788 while (isspace((unsigned char) *(*endstr)))
1789 (*endstr)++;
1790 /* numeric timezone? */
1791 if (isdigit((unsigned char) *(*endstr)))
1792 {
1793 ftype[nf] = DTK_TZ;
1794 *lp++ = *(*endstr)++;
1795 while (isdigit((unsigned char) *(*endstr)) ||
1796 (*(*endstr) == ':') || (*(*endstr) == '.'))
1797 *lp++ = *(*endstr)++;
1798 }
1799 /* special? */
1800 else if (isalpha((unsigned char) *(*endstr)))
1801 {
1802 ftype[nf] = DTK_SPECIAL;
1803 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1804 while (isalpha((unsigned char) *(*endstr)))
1805 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1806 }
1807 /* otherwise something wrong... */
1808 else
1809 return -1;
1810 }
1811 /* ignore punctuation but use as delimiter */
1812 else if (ispunct((unsigned char) *(*endstr)))
1813 {
1814 (*endstr)++;
1815 continue;
1816
1817 }
1818 /* otherwise, something is not right... */
1819 else
1820 return -1;
1821
1822 /* force in a delimiter after each field */
1823 *lp++ = '\0';
1824 nf++;
1825 }
1826
1827 *numfields = nf;
1828
1829 return 0;
1830 } /* ParseDateTime() */
1831
1832
1833 /* DecodeDateTime()
1834 * Interpret previously parsed fields for general date and time.
1835 * Return 0 if full date, 1 if only time, and -1 if problems.
1836 * External format(s):
1837 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
1838 * "Fri Feb-7-1997 15:23:27"
1839 * "Feb-7-1997 15:23:27"
1840 * "2-7-1997 15:23:27"
1841 * "1997-2-7 15:23:27"
1842 * "1997.038 15:23:27" (day of year 1-366)
1843 * Also supports input in compact time:
1844 * "970207 152327"
1845 * "97038 152327"
1846 * "20011225T040506.789-07"
1847 *
1848 * Use the system-provided functions to get the current time zone
1849 * if not specified in the input string.
1850 * If the date is outside the time_t system-supported time range,
1851 * then assume UTC time zone. - thomas 1997-05-27
1852 */
1853 int
1854 DecodeDateTime(char **field, int *ftype, int nf,
1855 int *dtype, struct tm * tm, fsec_t *fsec, bool EuroDates)
1856 {
1857 int fmask = 0,
1858 tmask,
1859 type;
1860 int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */
1861 int i;
1862 int val;
1863 int mer = HR24;
1864 int haveTextMonth = FALSE;
1865 int is2digits = FALSE;
1866 int bc = FALSE;
1867 int t = 0;
1868 int *tzp = &t;
1869
1870 /***
1871 * We'll insist on at least all of the date fields, but initialize the
1872 * remaining fields in case they are not set later...
1873 ***/
1874 *dtype = DTK_DATE;
1875 tm->tm_hour = 0;
1876 tm->tm_min = 0;
1877 tm->tm_sec = 0;
1878 *fsec = 0;
1879 /* don't know daylight savings time status apriori */
1880 tm->tm_isdst = -1;
1881 if (tzp != NULL)
1882 *tzp = 0;
1883
1884 for (i = 0; i < nf; i++)
1885 {
1886 switch (ftype[i])
1887 {
1888 case DTK_DATE:
1889 /***
1890 * Integral julian day with attached time zone?
1891 * All other forms with JD will be separated into
1892 * distinct fields, so we handle just this case here.
1893 ***/
1894 if (ptype == DTK_JULIAN)
1895 {
1896 char *cp;
1897 int val;
1898
1899 if (tzp == NULL)
1900 return -1;
1901
1902 val = strtol(field[i], &cp, 10);
1903 if (*cp != '-')
1904 return -1;
1905
1906 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1907 /* Get the time zone from the end of the string */
1908 if (DecodeTimezone(cp, tzp) != 0)
1909 return -1;
1910
1911 tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1912 ptype = 0;
1913 break;
1914 }
1915 /***
1916 * Already have a date? Then this might be a POSIX time
1917 * zone with an embedded dash (e.g. "PST-3" == "EST") or
1918 * a run-together time with trailing time zone (e.g. hhmmss-zz).
1919 * - thomas 2001-12-25
1920 ***/
1921 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
1922 || (ptype != 0))
1923 {
1924 /* No time zone accepted? Then quit... */
1925 if (tzp == NULL)
1926 return -1;
1927
1928 if (isdigit((unsigned char) *field[i]) || ptype != 0)
1929 {
1930 char *cp;
1931
1932 if (ptype != 0)
1933 {
1934 /* Sanity check; should not fail this test */
1935 if (ptype != DTK_TIME)
1936 return -1;
1937 ptype = 0;
1938 }
1939
1940 /*
1941 * Starts with a digit but we already have a time
1942 * field? Then we are in trouble with a date and time
1943 * already...
1944 */
1945 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1946 return -1;
1947
1948 if ((cp = strchr(field[i], '-')) == NULL)
1949 return -1;
1950
1951 /* Get the time zone from the end of the string */
1952 if (DecodeTimezone(cp, tzp) != 0)
1953 return -1;
1954 *cp = '\0';
1955
1956 /*
1957 * Then read the rest of the field as a concatenated
1958 * time
1959 */
1960 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
1961 &tmask, tm, fsec, &is2digits)) < 0)
1962 return -1;
1963
1964 /*
1965 * modify tmask after returning from
1966 * DecodeNumberField()
1967 */
1968 tmask |= DTK_M(TZ);
1969 }
1970 else
1971 {
1972 if (DecodePosixTimezone(field[i], tzp) != 0)
1973 return -1;
1974
1975 ftype[i] = DTK_TZ;
1976 tmask = DTK_M(TZ);
1977 }
1978 }
1979 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
1980 return -1;
1981 break;
1982
1983 case DTK_TIME:
1984 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
1985 return -1;
1986
1987 /*
1988 * Check upper limit on hours; other limits checked in
1989 * DecodeTime()
1990 */
1991 /* test for > 24:00:00 */
1992 if (tm->tm_hour > 24 ||
1993 (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
1994 return -1;
1995 break;
1996
1997 case DTK_TZ:
1998 {
1999 int tz;
2000
2001 if (tzp == NULL)
2002 return -1;
2003
2004 if (DecodeTimezone(field[i], &tz) != 0)
2005 return -1;
2006
2007 /*
2008 * Already have a time zone? Then maybe this is the second
2009 * field of a POSIX time: EST+3 (equivalent to PST)
2010 */
2011 if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
2012 ftype[i - 1] == DTK_TZ &&
2013 isalpha((unsigned char) *field[i - 1]))
2014 {
2015 *tzp -= tz;
2016 tmask = 0;
2017 }
2018 else
2019 {
2020 *tzp = tz;
2021 tmask = DTK_M(TZ);
2022 }
2023 }
2024 break;
2025
2026 case DTK_NUMBER:
2027
2028 /*
2029 * Was this an "ISO date" with embedded field labels? An
2030 * example is "y2001m02d04" - thomas 2001-02-04
2031 */
2032 if (ptype != 0)
2033 {
2034 char *cp;
2035 int val;
2036
2037 val = strtol(field[i], &cp, 10);
2038
2039 /*
2040 * only a few kinds are allowed to have an embedded
2041 * decimal
2042 */
2043 if (*cp == '.')
2044 switch (ptype)
2045 {
2046 case DTK_JULIAN:
2047 case DTK_TIME:
2048 case DTK_SECOND:
2049 break;
2050 default:
2051 return 1;
2052 break;
2053 }
2054 else if (*cp != '\0')
2055 return -1;
2056
2057 switch (ptype)
2058 {
2059 case DTK_YEAR:
2060 tm->tm_year = val;
2061 tmask = DTK_M(YEAR);
2062 break;
2063
2064 case DTK_MONTH:
2065
2066 /*
2067 * already have a month and hour? then assume
2068 * minutes
2069 */
2070 if ((fmask & DTK_M(MONTH)) != 0 &&
2071 (fmask & DTK_M(HOUR)) != 0)
2072 {
2073 tm->tm_min = val;
2074 tmask = DTK_M(MINUTE);
2075 }
2076 else
2077 {
2078 tm->tm_mon = val;
2079 tmask = DTK_M(MONTH);
2080 }
2081 break;
2082
2083 case DTK_DAY:
2084 tm->tm_mday = val;
2085 tmask = DTK_M(DAY);
2086 break;
2087
2088 case DTK_HOUR:
2089 tm->tm_hour = val;
2090 tmask = DTK_M(HOUR);
2091 break;
2092
2093 case DTK_MINUTE:
2094 tm->tm_min = val;
2095 tmask = DTK_M(MINUTE);
2096 break;
2097
2098 case DTK_SECOND:
2099 tm->tm_sec = val;
2100 tmask = DTK_M(SECOND);
2101 if (*cp == '.')
2102 {
2103 double frac;
2104
2105 frac = strtod(cp, &cp);
2106 if (*cp != '\0')
2107 return -1;
2108 #ifdef HAVE_INT64_TIMESTAMP
2109 *fsec = frac * 1000000;
2110 #else
2111 *fsec = frac;
2112 #endif
2113 }
2114 break;
2115
2116 case DTK_TZ:
2117 tmask = DTK_M(TZ);
2118 if (DecodeTimezone(field[i], tzp) != 0)
2119 return -1;
2120 break;
2121
2122 case DTK_JULIAN:
2123 /***
2124 * previous field was a label for "julian date"?
2125 ***/
2126 tmask = DTK_DATE_M;
2127 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2128 /* fractional Julian Day? */
2129 if (*cp == '.')
2130 {
2131 double time;
2132
2133 time = strtod(cp, &cp);
2134 if (*cp != '\0')
2135 return -1;
2136
2137 tmask |= DTK_TIME_M;
2138 #ifdef HAVE_INT64_TIMESTAMP
2139 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
2140 #else
2141 dt2time((time * SECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
2142 #endif
2143 }
2144 break;
2145
2146 case DTK_TIME:
2147 /* previous field was "t" for ISO time */
2148 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
2149 &tmask, tm, fsec, &is2digits)) < 0)
2150 return -1;
2151
2152 if (tmask != DTK_TIME_M)
2153 return -1;
2154 break;
2155
2156 default:
2157 return -1;
2158 break;
2159 }
2160
2161 ptype = 0;
2162 *dtype = DTK_DATE;
2163 }
2164 else
2165 {
2166 char *cp;
2167 int flen;
2168
2169 flen = strlen(field[i]);
2170 cp = strchr(field[i], '.');
2171
2172 /* Embedded decimal and no date yet? */
2173 if (cp != NULL && !(fmask & DTK_DATE_M))
2174 {
2175 if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
2176 return -1;
2177 }
2178 /* embedded decimal and several digits before? */
2179 else if (cp != NULL && flen - strlen(cp) > 2)
2180 {
2181 /*
2182 * Interpret as a concatenated date or time Set the
2183 * type field to allow decoding other fields later.
2184 * Example: 20011223 or 040506
2185 */
2186 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2187 &tmask, tm, fsec, &is2digits)) < 0)
2188 return -1;
2189 }
2190 else if (flen > 4)
2191 {
2192 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2193 &tmask, tm, fsec, &is2digits)) < 0)
2194 return -1;
2195 }
2196 /* otherwise it is a single date/time field... */
2197 else if (DecodeNumber(flen, field[i], fmask,
2198 &tmask, tm, fsec, &is2digits, EuroDates) != 0)
2199 return -1;
2200 }
2201 break;
2202
2203 case DTK_STRING:
2204 case DTK_SPECIAL:
2205 type = DecodeSpecial(i, field[i], &val);
2206 if (type == IGNORE_DTF)
2207 continue;
2208
2209 tmask = DTK_M(type);
2210 switch (type)
2211 {
2212 case RESERV:
2213 switch (val)
2214 {
2215 case DTK_NOW:
2216 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
2217 *dtype = DTK_DATE;
2218 GetCurrentDateTime(tm);
2219 break;
2220
2221 case DTK_YESTERDAY:
2222 tmask = DTK_DATE_M;
2223 *dtype = DTK_DATE;
2224 GetCurrentDateTime(tm);
2225 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
2226 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2227 tm->tm_hour = 0;
2228 tm->tm_min = 0;
2229 tm->tm_sec = 0;
2230 break;
2231
2232 case DTK_TODAY:
2233 tmask = DTK_DATE_M;
2234 *dtype = DTK_DATE;
2235 GetCurrentDateTime(tm);
2236 tm->tm_hour = 0;
2237 tm->tm_min = 0;
2238 tm->tm_sec = 0;
2239 break;
2240
2241 case DTK_TOMORROW:
2242 tmask = DTK_DATE_M;
2243 *dtype = DTK_DATE;
2244 GetCurrentDateTime(tm);
2245 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
2246 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2247 tm->tm_hour = 0;
2248 tm->tm_min = 0;
2249 tm->tm_sec = 0;
2250 break;
2251
2252 case DTK_ZULU:
2253 tmask = (DTK_TIME_M | DTK_M(TZ));
2254 *dtype = DTK_DATE;
2255 tm->tm_hour = 0;
2256 tm->tm_min = 0;
2257 tm->tm_sec = 0;
2258 if (tzp != NULL)
2259 *tzp = 0;
2260 break;
2261
2262 default:
2263 *dtype = val;
2264 }
2265
2266 break;
2267
2268 case MONTH:
2269
2270 /*
2271 * already have a (numeric) month? then see if we can
2272 * substitute...
2273 */
2274 if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
2275 !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
2276 {
2277 tm->tm_mday = tm->tm_mon;
2278 tmask = DTK_M(DAY);
2279 }
2280 haveTextMonth = TRUE;
2281 tm->tm_mon = val;
2282 break;
2283
2284 case DTZMOD:
2285
2286 /*
2287 * daylight savings time modifier (solves "MET DST"
2288 * syntax)
2289 */
2290 tmask |= DTK_M(DTZ);
2291 tm->tm_isdst = 1;
2292 if (tzp == NULL)
2293 return -1;
2294 *tzp -= val;
2295 break;
2296
2297 case DTZ:
2298
2299 /*
2300 * set mask for TZ here _or_ check for DTZ later when
2301 * getting default timezone
2302 */
2303 tmask |= DTK_M(TZ);
2304 tm->tm_isdst = 1;
2305 if (tzp == NULL)
2306 return -1;
2307 *tzp = -val;
2308 ftype[i] = DTK_TZ;
2309 break;
2310
2311 case TZ:
2312 tm->tm_isdst = 0;
2313 if (tzp == NULL)
2314 return -1;
2315 *tzp = -val;
2316 ftype[i] = DTK_TZ;
2317 break;
2318
2319 case IGNORE_DTF:
2320 break;
2321
2322 case AMPM:
2323 mer = val;
2324 break;
2325
2326 case ADBC:
2327 bc = (val == BC);
2328 break;
2329
2330 case DOW:
2331 tm->tm_wday = val;
2332 break;
2333
2334 case UNITS:
2335 tmask = 0;
2336 ptype = val;
2337 break;
2338
2339 case ISOTIME:
2340
2341 /*
2342 * This is a filler field "t" indicating that the next
2343 * field is time. Try to verify that this is sensible.
2344 */
2345 tmask = 0;
2346
2347 /* No preceding date? Then quit... */
2348 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2349 return -1;
2350
2351 /***
2352 * We will need one of the following fields:
2353 * DTK_NUMBER should be hhmmss.fff
2354 * DTK_TIME should be hh:mm:ss.fff
2355 * DTK_DATE should be hhmmss-zz
2356 ***/
2357 if (i >= nf - 1 ||
2358 (ftype[i + 1] != DTK_NUMBER &&
2359 ftype[i + 1] != DTK_TIME &&
2360 ftype[i + 1] != DTK_DATE))
2361 return -1;
2362
2363 ptype = val;
2364 break;
2365
2366 default:
2367 return -1;
2368 }
2369 break;
2370
2371 default:
2372 return -1;
2373 }
2374
2375 if (tmask & fmask)
2376 return -1;
2377 fmask |= tmask;
2378 }
2379
2380 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2381 if (bc)
2382 {
2383 if (tm->tm_year > 0)
2384 tm->tm_year = -(tm->tm_year - 1);
2385 else
2386 return -1;
2387 }
2388 else if (is2digits)
2389 {
2390 if (tm->tm_year < 70)
2391 tm->tm_year += 2000;
2392 else if (tm->tm_year < 100)
2393 tm->tm_year += 1900;
2394 }
2395
2396 if (mer != HR24 && tm->tm_hour > 12)
2397 return -1;
2398 if (mer == AM && tm->tm_hour == 12)
2399 tm->tm_hour = 0;
2400 else if (mer == PM && tm->tm_hour != 12)
2401 tm->tm_hour += 12;
2402
2403 /* do additional checking for full date specs... */
2404 if (*dtype == DTK_DATE)
2405 {
2406 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2407 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
2408
2409 /*
2410 * check for valid day of month, now that we know for sure the month
2411 * and year...
2412 */
2413 if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2414 return -1;
2415
2416 /*
2417 * backend tried to find local timezone here but we don't use the
2418 * result afterwards anyway so we only check for this error: daylight
2419 * savings time modifier but no standard timezone?
2420 */
2421 if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
2422 return -1;
2423 }
2424
2425 return 0;
2426 } /* DecodeDateTime() */
2427
2428 /* Function works as follows:
2429 *
2430 *
2431 * */
2432
2433 static char *
2434 find_end_token(char *str, char *fmt)
2435 {
2436 /*
2437 * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
2438 *
2439 * we extract the 28, we read the percent sign and the type "d" then this
2440 * functions gets called as find_end_token("28the day12the hour", "the
2441 * day%hthehour")
2442 *
2443 * fmt points to "the day%hthehour", next_percent points to %hthehour and
2444 * we have to find a match for everything between these positions ("the
2445 * day"). We look for "the day" in str and know that the pattern we are
2446 * about to scan ends where this string starts (right after the "28")
2447 *
2448 * At the end, *fmt is '\0' and *str isn't. end_position then is
2449 * unchanged.
2450 */
2451 char *end_position = NULL;
2452 char *next_percent,
2453 *subst_location = NULL;
2454 int scan_offset = 0;
2455 char last_char;
2456
2457 /* are we at the end? */
2458 if (!*fmt)
2459 {
2460 end_position = fmt;
2461 return end_position;
2462 }
2463
2464 /* not at the end */
2465 while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
2466 {
2467 /*
2468 * there is no delimiter, skip to the next delimiter if we're reading
2469 * a number and then something that is not a number "9:15pm", we might
2470 * be able to recover with the strtol end pointer. Go for the next
2471 * percent sign
2472 */
2473 scan_offset += 2;
2474 }
2475 next_percent = strchr(fmt + scan_offset, '%');
2476 if (next_percent)
2477 {
2478 /*
2479 * we don't want to allocate extra memory, so we temporarily set the
2480 * '%' sign to '\0' and call strstr However since we allow whitespace
2481 * to float around everything, we have to shorten the pattern until we
2482 * reach a non-whitespace character
2483 */
2484
2485 subst_location = next_percent;
2486 while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
2487 subst_location--;
2488 last_char = *subst_location;
2489 *subst_location = '\0';
2490
2491 /*
2492 * the haystack is the str and the needle is the original fmt but it
2493 * ends at the position where the next percent sign would be
2494 */
2495
2496 /*
2497 * There is one special case. Imagine: str = " 2", fmt = "%d %...",
2498 * since we want to allow blanks as "dynamic" padding we have to
2499 * accept this. Now, we are called with a fmt of " %..." and look for
2500 * " " in str. We find it at the first position and never read the
2501 * 2...
2502 */
2503 while (*str == ' ')
2504 str++;
2505 end_position = strstr(str, fmt + scan_offset);
2506 *subst_location = last_char;
2507 }
2508 else
2509 {
2510 /*
2511 * there is no other percent sign. So everything up to the end has to
2512 * match.
2513 */
2514 end_position = str + strlen(str);
2515 }
2516 if (!end_position)
2517 {
2518 /*
2519 * maybe we have the following case:
2520 *
2521 * str = "4:15am" fmt = "%M:%S %p"
2522 *
2523 * at this place we could have
2524 *
2525 * str = "15am" fmt = " %p"
2526 *
2527 * and have set fmt to " " because overwrote the % sign with a NULL
2528 *
2529 * In this case where we would have to match a space but can't find
2530 * it, set end_position to the end of the string
2531 */
2532 if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
2533 end_position = str + strlen(str);
2534 }
2535 return end_position;
2536 }
2537
2538 static int
2539 pgtypes_defmt_scan(union un_fmt_comb * scan_val, int scan_type, char **pstr, char *pfmt)
2540 {
2541 /*
2542 * scan everything between pstr and pstr_end. This is not including the
2543 * last character so we might set it to '\0' for the parsing
2544 */
2545
2546 char last_char;
2547 int err = 0;
2548 char *pstr_end;
2549 char *strtol_end = NULL;
2550
2551 while (**pstr == ' ')
2552 pstr++;
2553 pstr_end = find_end_token(*pstr, pfmt);
2554 if (!pstr_end)
2555 {
2556 /* there was an error, no match */
2557 return 1;
2558 }
2559 last_char = *pstr_end;
2560 *pstr_end = '\0';
2561
2562 switch (scan_type)
2563 {
2564 case PGTYPES_TYPE_UINT:
2565
2566 /*
2567 * numbers may be blank-padded, this is the only deviation from
2568 * the fmt-string we accept
2569 */
2570 while (**pstr == ' ')
2571 (*pstr)++;
2572 errno = 0;
2573 scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
2574 if (errno)
2575 err = 1;
2576 break;
2577 case PGTYPES_TYPE_UINT_LONG:
2578 while (**pstr == ' ')
2579 (*pstr)++;
2580 errno = 0;
2581 scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
2582 if (errno)
2583 err = 1;
2584 break;
2585 case PGTYPES_TYPE_STRING_MALLOCED:
2586 scan_val->str_val = pgtypes_strdup(*pstr);
2587 if (scan_val->str_val == NULL)
2588 err = 1;
2589 break;
2590 }
2591 if (strtol_end && *strtol_end)
2592 *pstr = strtol_end;
2593 else
2594 *pstr = pstr_end;
2595 *pstr_end = last_char;
2596 return err;
2597 }
2598
2599 /* XXX range checking */
2600 int
2601 PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
2602 int *year, int *month, int *day,
2603 int *hour, int *minute, int *second,
2604 int *tz)
2605 {
2606 union un_fmt_comb scan_val;
2607 int scan_type;
2608
2609 char *pstr,
2610 *pfmt,
2611 *tmp;
2612 int err = 1;
2613 unsigned int j;
2614 struct tm tm;
2615
2616 pfmt = fmt;
2617 pstr = *str;
2618
2619 while (*pfmt)
2620 {
2621 err = 0;
2622 while (*pfmt == ' ')
2623 pfmt++;
2624 while (*pstr == ' ')
2625 pstr++;
2626 if (*pfmt != '%')
2627 {
2628 if (*pfmt == *pstr)
2629 {
2630 pfmt++;
2631 pstr++;
2632 }
2633 else
2634 {
2635 /* Error: no match */
2636 err = 1;
2637 return err;
2638 }
2639 continue;
2640 }
2641 /* here *pfmt equals '%' */
2642 pfmt++;
2643 switch (*pfmt)
2644 {
2645 case 'a':
2646 pfmt++;
2647
2648 /*
2649 * we parse the day and see if it is a week day but we do not
2650 * check if the week day really matches the date
2651 */
2652 err = 1;
2653 j = 0;
2654 while (pgtypes_date_weekdays_short[j])
2655 {
2656 if (strncmp(pgtypes_date_weekdays_short[j], pstr,
2657 strlen(pgtypes_date_weekdays_short[j])) == 0)
2658 {
2659 /* found it */
2660 err = 0;
2661 pstr += strlen(pgtypes_date_weekdays_short[j]);
2662 break;
2663 }
2664 j++;
2665 }
2666 break;
2667 case 'A':
2668 /* see note above */
2669 pfmt++;
2670 err = 1;
2671 j = 0;
2672 while (days[j])
2673 {
2674 if (strncmp(days[j], pstr, strlen(days[j])) == 0)
2675 {
2676 /* found it */
2677 err = 0;
2678 pstr += strlen(days[j]);
2679 break;
2680 }
2681 j++;
2682 }
2683 break;
2684 case 'b':
2685 case 'h':
2686 pfmt++;
2687 err = 1;
2688 j = 0;
2689 while (months[j])
2690 {
2691 if (strncmp(months[j], pstr, strlen(months[j])) == 0)
2692 {
2693 /* found it */
2694 err = 0;
2695 pstr += strlen(months[j]);
2696 *month = j + 1;
2697 break;
2698 }
2699 j++;
2700 }
2701 break;
2702 case 'B':
2703 /* see note above */
2704 pfmt++;
2705 err = 1;
2706 j = 0;
2707 while (pgtypes_date_months[j])
2708 {
2709 if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
2710 {
2711 /* found it */
2712 err = 0;
2713 pstr += strlen(pgtypes_date_months[j]);
2714 *month = j + 1;
2715 break;
2716 }
2717 j++;
2718 }
2719 break;
2720 case 'c':
2721 /* XXX */
2722 break;
2723 case 'C':
2724 pfmt++;
2725 scan_type = PGTYPES_TYPE_UINT;
2726 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2727 *year = scan_val.uint_val * 100;
2728 break;
2729 case 'd':
2730 case 'e':
2731 pfmt++;
2732 scan_type = PGTYPES_TYPE_UINT;
2733 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2734 *day = scan_val.uint_val;
2735 break;
2736 case 'D':
2737
2738 /*
2739 * we have to concatenate the strings in order to be able to
2740 * find the end of the substitution
2741 */
2742 pfmt++;
2743 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
2744 strcpy(tmp, "%m/%d/%y");
2745 strcat(tmp, pfmt);
2746 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2747 free(tmp);
2748 return err;
2749 case 'm':
2750 pfmt++;
2751 scan_type = PGTYPES_TYPE_UINT;
2752 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2753 *month = scan_val.uint_val;
2754 break;
2755 case 'y':
2756 case 'g': /* XXX difference to y (ISO) */
2757 pfmt++;
2758 scan_type = PGTYPES_TYPE_UINT;
2759 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2760 if (*year < 0)
2761 {
2762 /* not yet set */
2763 *year = scan_val.uint_val;
2764 }
2765 else
2766 *year += scan_val.uint_val;
2767 if (*year < 100)
2768 *year += 1900;
2769 break;
2770 case 'G':
2771 /* XXX difference to %V (ISO) */
2772 pfmt++;
2773 scan_type = PGTYPES_TYPE_UINT;
2774 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2775 *year = scan_val.uint_val;
2776 break;
2777 case 'H':
2778 case 'I':
2779 case 'k':
2780 case 'l':
2781 pfmt++;
2782 scan_type = PGTYPES_TYPE_UINT;
2783 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2784 *hour += scan_val.uint_val;
2785 break;
2786 case 'j':
2787 pfmt++;
2788 scan_type = PGTYPES_TYPE_UINT;
2789 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2790
2791 /*
2792 * XXX what should we do with that? We could say that it's
2793 * sufficient if we have the year and the day within the year
2794 * to get at least a specific day.
2795 */
2796 break;
2797 case 'M':
2798 pfmt++;
2799 scan_type = PGTYPES_TYPE_UINT;
2800 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2801 *minute = scan_val.uint_val;
2802 break;
2803 case 'n':
2804 pfmt++;
2805 if (*pstr == '\n')
2806 pstr++;
2807 else
2808 err = 1;
2809 break;
2810 case 'p':
2811 err = 1;
2812 pfmt++;
2813 if (strncmp(pstr, "am", 2) == 0)
2814 {
2815 *hour += 0;
2816 err = 0;
2817 pstr += 2;
2818 }
2819 if (strncmp(pstr, "a.m.", 4) == 0)
2820 {
2821 *hour += 0;
2822 err = 0;
2823 pstr += 4;
2824 }
2825 if (strncmp(pstr, "pm", 2) == 0)
2826 {
2827 *hour += 12;
2828 err = 0;
2829 pstr += 2;
2830 }
2831 if (strncmp(pstr, "p.m.", 4) == 0)
2832 {
2833 *hour += 12;
2834 err = 0;
2835 pstr += 4;
2836 }
2837 break;
2838 case 'P':
2839 err = 1;
2840 pfmt++;
2841 if (strncmp(pstr, "AM", 2) == 0)
2842 {
2843 *hour += 0;
2844 err = 0;
2845 pstr += 2;
2846 }
2847 if (strncmp(pstr, "A.M.", 4) == 0)
2848 {
2849 *hour += 0;
2850 err = 0;
2851 pstr += 4;
2852 }
2853 if (strncmp(pstr, "PM", 2) == 0)
2854 {
2855 *hour += 12;
2856 err = 0;
2857 pstr += 2;
2858 }
2859 if (strncmp(pstr, "P.M.", 4) == 0)
2860 {
2861 *hour += 12;
2862 err = 0;
2863 pstr += 4;
2864 }
2865 break;
2866 case 'r':
2867 pfmt++;
2868 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
2869 strcpy(tmp, "%I:%M:%S %p");
2870 strcat(tmp, pfmt);
2871 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2872 free(tmp);
2873 return err;
2874 case 'R':
2875 pfmt++;
2876 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
2877 strcpy(tmp, "%H:%M");
2878 strcat(tmp, pfmt);
2879 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2880 free(tmp);
2881 return err;
2882 case 's':
2883 pfmt++;
2884 scan_type = PGTYPES_TYPE_UINT_LONG;
2885 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2886 /* number of seconds in scan_val.luint_val */
2887 {
2888 struct tm *tms;
2889 time_t et = (time_t) scan_val.luint_val;
2890
2891 tms = gmtime(&et);
2892
2893 if (tms)
2894 {
2895 *year = tms->tm_year + 1900;
2896 *month = tms->tm_mon + 1;
2897 *day = tms->tm_mday;
2898 *hour = tms->tm_hour;
2899 *minute = tms->tm_min;
2900 *second = tms->tm_sec;
2901 }
2902 else
2903 err = 1;
2904 }
2905 break;
2906 case 'S':
2907 pfmt++;
2908 scan_type = PGTYPES_TYPE_UINT;
2909 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2910 *second = scan_val.uint_val;
2911 break;
2912 case 't':
2913 pfmt++;
2914 if (*pstr == '\t')
2915 pstr++;
2916 else
2917 err = 1;
2918 break;
2919 case 'T':
2920 pfmt++;
2921 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
2922 strcpy(tmp, "%H:%M:%S");
2923 strcat(tmp, pfmt);
2924 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2925 free(tmp);
2926 return err;
2927 case 'u':
2928 pfmt++;
2929 scan_type = PGTYPES_TYPE_UINT;
2930 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2931 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
2932 err = 1;
2933 break;
2934 case 'U':
2935 pfmt++;
2936 scan_type = PGTYPES_TYPE_UINT;
2937 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2938 if (scan_val.uint_val > 53)
2939 err = 1;
2940 break;
2941 case 'V':
2942 pfmt++;
2943 scan_type = PGTYPES_TYPE_UINT;
2944 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2945 if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
2946 err = 1;
2947 break;
2948 case 'w':
2949 pfmt++;
2950 scan_type = PGTYPES_TYPE_UINT;
2951 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2952 if (scan_val.uint_val > 6)
2953 err = 1;
2954 break;
2955 case 'W':
2956 pfmt++;
2957 scan_type = PGTYPES_TYPE_UINT;
2958 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2959 if (scan_val.uint_val > 53)
2960 err = 1;
2961 break;
2962 case 'x':
2963 case 'X':
2964 /* XXX */
2965 break;
2966 case 'Y':
2967 pfmt++;
2968 scan_type = PGTYPES_TYPE_UINT;
2969 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2970 *year = scan_val.uint_val;
2971 break;
2972 case 'z':
2973 pfmt++;
2974 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2975 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2976 if (!err)
2977 {
2978 err = DecodeTimezone(scan_val.str_val, tz);
2979 free(scan_val.str_val);
2980 }
2981 break;
2982 case 'Z':
2983 pfmt++;
2984 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2985 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2986 if (!err)
2987 {
2988 /*
2989 * XXX use DecodeSpecial instead? Do we need strcasecmp
2990 * here?
2991 */
2992 err = 1;
2993 for (j = 0; j < szdatetktbl; j++)
2994 {
2995 if ((datetktbl[j].type == TZ || datetktbl[j].type == DTZ) &&
2996 pg_strcasecmp(datetktbl[j].token,
2997 scan_val.str_val) == 0)
2998 {
2999 *tz = -datetktbl[j].value;
3000 err = 0;
3001 break;
3002 }
3003 }
3004 free(scan_val.str_val);
3005 }
3006 break;
3007 case '+':
3008 /* XXX */
3009 break;
3010 case '%':
3011 pfmt++;
3012 if (*pstr == '%')
3013 pstr++;
3014 else
3015 err = 1;
3016 break;
3017 default:
3018 err = 1;
3019 }
3020 }
3021 if (!err)
3022 {
3023 if (*second < 0)
3024 *second = 0;
3025 if (*minute < 0)
3026 *minute = 0;
3027 if (*hour < 0)
3028 *hour = 0;
3029 if (*day < 0)
3030 {
3031 err = 1;
3032 *day = 1;
3033 }
3034 if (*month < 0)
3035 {
3036 err = 1;
3037 *month = 1;
3038 }
3039 if (*year < 0)
3040 {
3041 err = 1;
3042 *year = 1970;
3043 }
3044
3045 if (*second > 59)
3046 {
3047 err = 1;
3048 *second = 0;
3049 }
3050 if (*minute > 59)
3051 {
3052 err = 1;
3053 *minute = 0;
3054 }
3055 if (*hour > 24 || /* test for > 24:00:00 */
3056 (*hour == 24 && (*minute > 0 || *second > 0)))
3057 {
3058 err = 1;
3059 *hour = 0;
3060 }
3061 if (*month > MONTHS_PER_YEAR)
3062 {
3063 err = 1;
3064 *month = 1;
3065 }
3066 if (*day > day_tab[isleap(*year)][*month - 1])
3067 {
3068 *day = day_tab[isleap(*year)][*month - 1];
3069 err = 1;
3070 }
3071
3072 tm.tm_sec = *second;
3073 tm.tm_min = *minute;
3074 tm.tm_hour = *hour;
3075 tm.tm_mday = *day;
3076 tm.tm_mon = *month;
3077 tm.tm_year = *year;
3078
3079 tm2timestamp(&tm, 0, tz, d);
3080 }
3081 return err;
3082 }
3083
3084 /* XXX: 1900 is compiled in as the base for years */
3085