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 void
EncodeDateOnly(struct tm * tm,int style,char * str,bool EuroDates)675 EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates)
676 {
677 Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
678
679 switch (style)
680 {
681 case USE_ISO_DATES:
682 /* compatible with ISO date formats */
683 if (tm->tm_year > 0)
684 sprintf(str, "%04d-%02d-%02d",
685 tm->tm_year, tm->tm_mon, tm->tm_mday);
686 else
687 sprintf(str, "%04d-%02d-%02d %s",
688 -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
689 break;
690
691 case USE_SQL_DATES:
692 /* compatible with Oracle/Ingres date formats */
693 if (EuroDates)
694 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
695 else
696 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
697 if (tm->tm_year > 0)
698 sprintf(str + 5, "/%04d", tm->tm_year);
699 else
700 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
701 break;
702
703 case USE_GERMAN_DATES:
704 /* German-style date format */
705 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
706 if (tm->tm_year > 0)
707 sprintf(str + 5, ".%04d", tm->tm_year);
708 else
709 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
710 break;
711
712 case USE_POSTGRES_DATES:
713 default:
714 /* traditional date-only style for Postgres */
715 if (EuroDates)
716 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
717 else
718 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
719 if (tm->tm_year > 0)
720 sprintf(str + 5, "-%04d", tm->tm_year);
721 else
722 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
723 break;
724 }
725 }
726
727 void
TrimTrailingZeros(char * str)728 TrimTrailingZeros(char *str)
729 {
730 int len = strlen(str);
731
732 /* chop off trailing zeros... but leave at least 2 fractional digits */
733 while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
734 {
735 len--;
736 *(str + len) = '\0';
737 }
738 }
739
740 /* EncodeDateTime()
741 * Encode date and time interpreted as local time.
742 *
743 * tm and fsec are the value to encode, print_tz determines whether to include
744 * a time zone (the difference between timestamp and timestamptz types), tz is
745 * the numeric time zone offset, tzn is the textual time zone, which if
746 * specified will be used instead of tz by some styles, style is the date
747 * style, str is where to write the output.
748 *
749 * Supported date styles:
750 * Postgres - day mon hh:mm:ss yyyy tz
751 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
752 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
753 * German - dd.mm.yyyy hh:mm:ss tz
754 * Variants (affects order of month and day for Postgres and SQL styles):
755 * US - mm/dd/yyyy
756 * European - dd/mm/yyyy
757 */
758 void
EncodeDateTime(struct tm * tm,fsec_t fsec,bool print_tz,int tz,const char * tzn,int style,char * str,bool EuroDates)759 EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
760 {
761 int day,
762 hour,
763 min;
764
765 /*
766 * Negative tm_isdst means we have no valid time zone translation.
767 */
768 if (tm->tm_isdst < 0)
769 print_tz = false;
770
771 switch (style)
772 {
773 case USE_ISO_DATES:
774 /* Compatible with ISO-8601 date formats */
775
776 sprintf(str, "%04d-%02d-%02d %02d:%02d",
777 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
778 tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
779
780 /*
781 * Print fractional seconds if any. The field widths here should
782 * be at least equal to MAX_TIMESTAMP_PRECISION.
783 */
784 if (fsec != 0)
785 {
786 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
787 TrimTrailingZeros(str);
788 }
789 else
790 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
791
792 if (tm->tm_year <= 0)
793 sprintf(str + strlen(str), " BC");
794
795 if (print_tz)
796 {
797 hour = -(tz / SECS_PER_HOUR);
798 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
799 if (min != 0)
800 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
801 else
802 sprintf(str + strlen(str), "%+03d", hour);
803 }
804 break;
805
806 case USE_SQL_DATES:
807 /* Compatible with Oracle/Ingres date formats */
808
809 if (EuroDates)
810 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
811 else
812 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
813
814 sprintf(str + 5, "/%04d %02d:%02d",
815 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
816 tm->tm_hour, tm->tm_min);
817
818 /*
819 * Print fractional seconds if any. The field widths here should
820 * be at least equal to MAX_TIMESTAMP_PRECISION.
821 */
822 if (fsec != 0)
823 {
824 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
825 TrimTrailingZeros(str);
826 }
827 else
828 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
829
830 if (tm->tm_year <= 0)
831 sprintf(str + strlen(str), " BC");
832
833 /*
834 * Note: the uses of %.*s in this function would be risky if the
835 * timezone names ever contain non-ASCII characters. However, all
836 * TZ abbreviations in the IANA database are plain ASCII.
837 */
838
839 if (print_tz)
840 {
841 if (tzn)
842 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
843 else
844 {
845 hour = -(tz / SECS_PER_HOUR);
846 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
847 if (min != 0)
848 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
849 else
850 sprintf(str + strlen(str), "%+03d", hour);
851 }
852 }
853 break;
854
855 case USE_GERMAN_DATES:
856 /* German variant on European style */
857
858 sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
859
860 sprintf(str + 5, ".%04d %02d:%02d",
861 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
862 tm->tm_hour, tm->tm_min);
863
864 /*
865 * Print fractional seconds if any. The field widths here should
866 * be at least equal to MAX_TIMESTAMP_PRECISION.
867 */
868 if (fsec != 0)
869 {
870 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
871 TrimTrailingZeros(str);
872 }
873 else
874 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
875
876 if (tm->tm_year <= 0)
877 sprintf(str + strlen(str), " BC");
878
879 if (print_tz)
880 {
881 if (tzn)
882 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
883 else
884 {
885 hour = -(tz / SECS_PER_HOUR);
886 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
887 if (min != 0)
888 sprintf(str + strlen(str), "%+03d:%02d", hour, min);
889 else
890 sprintf(str + strlen(str), "%+03d", hour);
891 }
892 }
893 break;
894
895 case USE_POSTGRES_DATES:
896 default:
897 /* Backward-compatible with traditional Postgres abstime dates */
898
899 day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
900 tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
901
902 memcpy(str, days[tm->tm_wday], 3);
903 strcpy(str + 3, " ");
904
905 if (EuroDates)
906 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
907 else
908 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
909
910 sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
911
912 /*
913 * Print fractional seconds if any. The field widths here should
914 * be at least equal to MAX_TIMESTAMP_PRECISION.
915 */
916 if (fsec != 0)
917 {
918 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
919 TrimTrailingZeros(str);
920 }
921 else
922 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
923
924 sprintf(str + strlen(str), " %04d",
925 (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
926 if (tm->tm_year <= 0)
927 sprintf(str + strlen(str), " BC");
928
929 if (print_tz)
930 {
931 if (tzn)
932 sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
933 else
934 {
935 /*
936 * We have a time zone, but no string version. Use the
937 * numeric form, but be sure to include a leading space to
938 * avoid formatting something which would be rejected by
939 * the date/time parser later. - thomas 2001-10-19
940 */
941 hour = -(tz / SECS_PER_HOUR);
942 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
943 if (min != 0)
944 sprintf(str + strlen(str), " %+03d:%02d", hour, min);
945 else
946 sprintf(str + strlen(str), " %+03d", hour);
947 }
948 }
949 break;
950 }
951 }
952
953 int
GetEpochTime(struct tm * tm)954 GetEpochTime(struct tm *tm)
955 {
956 struct tm *t0;
957 time_t epoch = 0;
958
959 t0 = gmtime(&epoch);
960
961 if (t0)
962 {
963 tm->tm_year = t0->tm_year + 1900;
964 tm->tm_mon = t0->tm_mon + 1;
965 tm->tm_mday = t0->tm_mday;
966 tm->tm_hour = t0->tm_hour;
967 tm->tm_min = t0->tm_min;
968 tm->tm_sec = t0->tm_sec;
969
970 return 0;
971 }
972
973 return -1;
974 } /* GetEpochTime() */
975
976 static void
abstime2tm(AbsoluteTime _time,int * tzp,struct tm * tm,char ** tzn)977 abstime2tm(AbsoluteTime _time, int *tzp, struct tm *tm, char **tzn)
978 {
979 time_t time = (time_t) _time;
980 struct tm *tx;
981
982 errno = 0;
983 if (tzp != NULL)
984 tx = localtime((time_t *) &time);
985 else
986 tx = gmtime((time_t *) &time);
987
988 if (!tx)
989 {
990 errno = PGTYPES_TS_BAD_TIMESTAMP;
991 return;
992 }
993
994 tm->tm_year = tx->tm_year + 1900;
995 tm->tm_mon = tx->tm_mon + 1;
996 tm->tm_mday = tx->tm_mday;
997 tm->tm_hour = tx->tm_hour;
998 tm->tm_min = tx->tm_min;
999 tm->tm_sec = tx->tm_sec;
1000 tm->tm_isdst = tx->tm_isdst;
1001
1002 #if defined(HAVE_TM_ZONE)
1003 tm->tm_gmtoff = tx->tm_gmtoff;
1004 tm->tm_zone = tx->tm_zone;
1005
1006 if (tzp != NULL)
1007 {
1008 /*
1009 * We have a brute force time zone per SQL99? Then use it without
1010 * change since we have already rotated to the time zone.
1011 */
1012 *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
1013
1014 /*
1015 * FreeBSD man pages indicate that this should work - tgl 97/04/23
1016 */
1017 if (tzn != NULL)
1018 {
1019 /*
1020 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1021 * contains an error message, which doesn't fit in the buffer
1022 */
1023 StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
1024 if (strlen(tm->tm_zone) > MAXTZLEN)
1025 tm->tm_isdst = -1;
1026 }
1027 }
1028 else
1029 tm->tm_isdst = -1;
1030 #elif defined(HAVE_INT_TIMEZONE)
1031 if (tzp != NULL)
1032 {
1033 *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
1034
1035 if (tzn != NULL)
1036 {
1037 /*
1038 * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
1039 * contains an error message, which doesn't fit in the buffer
1040 */
1041 StrNCpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
1042 if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
1043 tm->tm_isdst = -1;
1044 }
1045 }
1046 else
1047 tm->tm_isdst = -1;
1048 #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1049 if (tzp != NULL)
1050 {
1051 /* default to UTC */
1052 *tzp = 0;
1053 if (tzn != NULL)
1054 *tzn = NULL;
1055 }
1056 else
1057 tm->tm_isdst = -1;
1058 #endif
1059 }
1060
1061 void
GetCurrentDateTime(struct tm * tm)1062 GetCurrentDateTime(struct tm *tm)
1063 {
1064 int tz;
1065
1066 abstime2tm(time(NULL), &tz, tm, NULL);
1067 }
1068
1069 void
dt2time(double jd,int * hour,int * min,int * sec,fsec_t * fsec)1070 dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
1071 {
1072 int64 time;
1073
1074 time = jd;
1075 *hour = time / USECS_PER_HOUR;
1076 time -= (*hour) * USECS_PER_HOUR;
1077 *min = time / USECS_PER_MINUTE;
1078 time -= (*min) * USECS_PER_MINUTE;
1079 *sec = time / USECS_PER_SEC;
1080 *fsec = time - (*sec * USECS_PER_SEC);
1081 } /* dt2time() */
1082
1083
1084
1085 /* DecodeNumberField()
1086 * Interpret numeric string as a concatenated date or time field.
1087 * Use the context of previously decoded fields to help with
1088 * the interpretation.
1089 */
1090 static int
DecodeNumberField(int len,char * str,int fmask,int * tmask,struct tm * tm,fsec_t * fsec,bool * is2digits)1091 DecodeNumberField(int len, char *str, int fmask,
1092 int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits)
1093 {
1094 char *cp;
1095
1096 /*
1097 * Have a decimal point? Then this is a date or something with a seconds
1098 * field...
1099 */
1100 if ((cp = strchr(str, '.')) != NULL)
1101 {
1102 char fstr[7];
1103 int i;
1104
1105 cp++;
1106
1107 /*
1108 * OK, we have at most six digits to care about. Let's construct a
1109 * string with those digits, zero-padded on the right, and then do the
1110 * conversion to an integer.
1111 *
1112 * XXX This truncates the seventh digit, unlike rounding it as the
1113 * backend does.
1114 */
1115 for (i = 0; i < 6; i++)
1116 fstr[i] = *cp != '\0' ? *cp++ : '0';
1117 fstr[i] = '\0';
1118 *fsec = strtol(fstr, NULL, 10);
1119 *cp = '\0';
1120 len = strlen(str);
1121 }
1122 /* No decimal point and no complete date yet? */
1123 else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1124 {
1125 /* yyyymmdd? */
1126 if (len == 8)
1127 {
1128 *tmask = DTK_DATE_M;
1129
1130 tm->tm_mday = atoi(str + 6);
1131 *(str + 6) = '\0';
1132 tm->tm_mon = atoi(str + 4);
1133 *(str + 4) = '\0';
1134 tm->tm_year = atoi(str + 0);
1135
1136 return DTK_DATE;
1137 }
1138 /* yymmdd? */
1139 else if (len == 6)
1140 {
1141 *tmask = DTK_DATE_M;
1142 tm->tm_mday = atoi(str + 4);
1143 *(str + 4) = '\0';
1144 tm->tm_mon = atoi(str + 2);
1145 *(str + 2) = '\0';
1146 tm->tm_year = atoi(str + 0);
1147 *is2digits = true;
1148
1149 return DTK_DATE;
1150 }
1151 /* yyddd? */
1152 else if (len == 5)
1153 {
1154 *tmask = DTK_DATE_M;
1155 tm->tm_mday = atoi(str + 2);
1156 *(str + 2) = '\0';
1157 tm->tm_mon = 1;
1158 tm->tm_year = atoi(str + 0);
1159 *is2digits = true;
1160
1161 return DTK_DATE;
1162 }
1163 }
1164
1165 /* not all time fields are specified? */
1166 if ((fmask & DTK_TIME_M) != DTK_TIME_M)
1167 {
1168 /* hhmmss */
1169 if (len == 6)
1170 {
1171 *tmask = DTK_TIME_M;
1172 tm->tm_sec = atoi(str + 4);
1173 *(str + 4) = '\0';
1174 tm->tm_min = atoi(str + 2);
1175 *(str + 2) = '\0';
1176 tm->tm_hour = atoi(str + 0);
1177
1178 return DTK_TIME;
1179 }
1180 /* hhmm? */
1181 else if (len == 4)
1182 {
1183 *tmask = DTK_TIME_M;
1184 tm->tm_sec = 0;
1185 tm->tm_min = atoi(str + 2);
1186 *(str + 2) = '\0';
1187 tm->tm_hour = atoi(str + 0);
1188
1189 return DTK_TIME;
1190 }
1191 }
1192
1193 return -1;
1194 } /* DecodeNumberField() */
1195
1196
1197 /* DecodeNumber()
1198 * Interpret plain numeric field as a date value in context.
1199 */
1200 static int
DecodeNumber(int flen,char * str,int fmask,int * tmask,struct tm * tm,fsec_t * fsec,bool * is2digits,bool EuroDates)1201 DecodeNumber(int flen, char *str, int fmask,
1202 int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits, bool EuroDates)
1203 {
1204 int val;
1205 char *cp;
1206
1207 *tmask = 0;
1208
1209 val = strtol(str, &cp, 10);
1210 if (cp == str)
1211 return -1;
1212
1213 if (*cp == '.')
1214 {
1215 /*
1216 * More than two digits? Then could be a date or a run-together time:
1217 * 2001.360 20011225 040506.789
1218 */
1219 if (cp - str > 2)
1220 return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
1221 tmask, tm, fsec, is2digits);
1222
1223 *fsec = strtod(cp, &cp);
1224 if (*cp != '\0')
1225 return -1;
1226 }
1227 else if (*cp != '\0')
1228 return -1;
1229
1230 /* Special case day of year? */
1231 if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
1232 {
1233 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1234 tm->tm_yday = val;
1235 j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
1236 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1237 }
1238
1239 /***
1240 * Enough digits to be unequivocal year? Used to test for 4 digits or
1241 * more, but we now test first for a three-digit doy so anything
1242 * bigger than two digits had better be an explicit year.
1243 * - thomas 1999-01-09
1244 * Back to requiring a 4 digit year. We accept a two digit
1245 * year farther down. - thomas 2000-03-28
1246 ***/
1247 else if (flen >= 4)
1248 {
1249 *tmask = DTK_M(YEAR);
1250
1251 /* already have a year? then see if we can substitute... */
1252 if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1253 tm->tm_year >= 1 && tm->tm_year <= 31)
1254 {
1255 tm->tm_mday = tm->tm_year;
1256 *tmask = DTK_M(DAY);
1257 }
1258
1259 tm->tm_year = val;
1260 }
1261
1262 /* already have year? then could be month */
1263 else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1264 {
1265 *tmask = DTK_M(MONTH);
1266 tm->tm_mon = val;
1267 }
1268 /* no year and EuroDates enabled? then could be day */
1269 else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
1270 !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
1271 val >= 1 && val <= 31)
1272 {
1273 *tmask = DTK_M(DAY);
1274 tm->tm_mday = val;
1275 }
1276 else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
1277 {
1278 *tmask = DTK_M(MONTH);
1279 tm->tm_mon = val;
1280 }
1281 else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
1282 {
1283 *tmask = DTK_M(DAY);
1284 tm->tm_mday = val;
1285 }
1286
1287 /*
1288 * Check for 2 or 4 or more digits, but currently we reach here only if
1289 * two digits. - thomas 2000-03-28
1290 */
1291 else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
1292 {
1293 *tmask = DTK_M(YEAR);
1294 tm->tm_year = val;
1295
1296 /* adjust ONLY if exactly two digits... */
1297 *is2digits = (flen == 2);
1298 }
1299 else
1300 return -1;
1301
1302 return 0;
1303 } /* DecodeNumber() */
1304
1305 /* DecodeDate()
1306 * Decode date string which includes delimiters.
1307 * Insist on a complete set of fields.
1308 */
1309 static int
DecodeDate(char * str,int fmask,int * tmask,struct tm * tm,bool EuroDates)1310 DecodeDate(char *str, int fmask, int *tmask, struct tm *tm, bool EuroDates)
1311 {
1312 fsec_t fsec;
1313
1314 int nf = 0;
1315 int i,
1316 len;
1317 bool bc = false;
1318 bool is2digits = false;
1319 int type,
1320 val,
1321 dmask = 0;
1322 char *field[MAXDATEFIELDS];
1323
1324 /* parse this string... */
1325 while (*str != '\0' && nf < MAXDATEFIELDS)
1326 {
1327 /* skip field separators */
1328 while (!isalnum((unsigned char) *str))
1329 str++;
1330
1331 field[nf] = str;
1332 if (isdigit((unsigned char) *str))
1333 {
1334 while (isdigit((unsigned char) *str))
1335 str++;
1336 }
1337 else if (isalpha((unsigned char) *str))
1338 {
1339 while (isalpha((unsigned char) *str))
1340 str++;
1341 }
1342
1343 /* Just get rid of any non-digit, non-alpha characters... */
1344 if (*str != '\0')
1345 *str++ = '\0';
1346 nf++;
1347 }
1348
1349 #if 0
1350 /* don't allow too many fields */
1351 if (nf > 3)
1352 return -1;
1353 #endif
1354
1355 *tmask = 0;
1356
1357 /* look first for text fields, since that will be unambiguous month */
1358 for (i = 0; i < nf; i++)
1359 {
1360 if (isalpha((unsigned char) *field[i]))
1361 {
1362 type = DecodeSpecial(i, field[i], &val);
1363 if (type == IGNORE_DTF)
1364 continue;
1365
1366 dmask = DTK_M(type);
1367 switch (type)
1368 {
1369 case MONTH:
1370 tm->tm_mon = val;
1371 break;
1372
1373 case ADBC:
1374 bc = (val == BC);
1375 break;
1376
1377 default:
1378 return -1;
1379 }
1380 if (fmask & dmask)
1381 return -1;
1382
1383 fmask |= dmask;
1384 *tmask |= dmask;
1385
1386 /* mark this field as being completed */
1387 field[i] = NULL;
1388 }
1389 }
1390
1391 /* now pick up remaining numeric fields */
1392 for (i = 0; i < nf; i++)
1393 {
1394 if (field[i] == NULL)
1395 continue;
1396
1397 if ((len = strlen(field[i])) <= 0)
1398 return -1;
1399
1400 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
1401 return -1;
1402
1403 if (fmask & dmask)
1404 return -1;
1405
1406 fmask |= dmask;
1407 *tmask |= dmask;
1408 }
1409
1410 if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
1411 return -1;
1412
1413 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1414 if (bc)
1415 {
1416 if (tm->tm_year > 0)
1417 tm->tm_year = -(tm->tm_year - 1);
1418 else
1419 return -1;
1420 }
1421 else if (is2digits)
1422 {
1423 if (tm->tm_year < 70)
1424 tm->tm_year += 2000;
1425 else if (tm->tm_year < 100)
1426 tm->tm_year += 1900;
1427 }
1428
1429 return 0;
1430 } /* DecodeDate() */
1431
1432
1433 /* DecodeTime()
1434 * Decode time string which includes delimiters.
1435 * Only check the lower limit on hours, since this same code
1436 * can be used to represent time spans.
1437 */
1438 int
DecodeTime(char * str,int * tmask,struct tm * tm,fsec_t * fsec)1439 DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec)
1440 {
1441 char *cp;
1442
1443 *tmask = DTK_TIME_M;
1444
1445 tm->tm_hour = strtol(str, &cp, 10);
1446 if (*cp != ':')
1447 return -1;
1448 str = cp + 1;
1449 tm->tm_min = strtol(str, &cp, 10);
1450 if (*cp == '\0')
1451 {
1452 tm->tm_sec = 0;
1453 *fsec = 0;
1454 }
1455 else if (*cp != ':')
1456 return -1;
1457 else
1458 {
1459 str = cp + 1;
1460 tm->tm_sec = strtol(str, &cp, 10);
1461 if (*cp == '\0')
1462 *fsec = 0;
1463 else if (*cp == '.')
1464 {
1465 char fstr[7];
1466 int i;
1467
1468 cp++;
1469
1470 /*
1471 * OK, we have at most six digits to care about. Let's construct a
1472 * string with those digits, zero-padded on the right, and then do
1473 * the conversion to an integer.
1474 *
1475 * XXX This truncates the seventh digit, unlike rounding it as the
1476 * backend does.
1477 */
1478 for (i = 0; i < 6; i++)
1479 fstr[i] = *cp != '\0' ? *cp++ : '0';
1480 fstr[i] = '\0';
1481 *fsec = strtol(fstr, &cp, 10);
1482 if (*cp != '\0')
1483 return -1;
1484 }
1485 else
1486 return -1;
1487 }
1488
1489 /* do a sanity check */
1490 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
1491 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
1492 return -1;
1493
1494 return 0;
1495 } /* DecodeTime() */
1496
1497 /* DecodeTimezone()
1498 * Interpret string as a numeric timezone.
1499 *
1500 * Note: we allow timezone offsets up to 13:59. There are places that
1501 * use +1300 summer time.
1502 */
1503 static int
DecodeTimezone(char * str,int * tzp)1504 DecodeTimezone(char *str, int *tzp)
1505 {
1506 int tz;
1507 int hr,
1508 min;
1509 char *cp;
1510 int len;
1511
1512 /* assume leading character is "+" or "-" */
1513 hr = strtol(str + 1, &cp, 10);
1514
1515 /* explicit delimiter? */
1516 if (*cp == ':')
1517 min = strtol(cp + 1, &cp, 10);
1518 /* otherwise, might have run things together... */
1519 else if (*cp == '\0' && (len = strlen(str)) > 3)
1520 {
1521 min = strtol(str + len - 2, &cp, 10);
1522 if (min < 0 || min >= 60)
1523 return -1;
1524
1525 *(str + len - 2) = '\0';
1526 hr = strtol(str + 1, &cp, 10);
1527 if (hr < 0 || hr > 13)
1528 return -1;
1529 }
1530 else
1531 min = 0;
1532
1533 tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
1534 if (*str == '-')
1535 tz = -tz;
1536
1537 *tzp = -tz;
1538 return *cp != '\0';
1539 } /* DecodeTimezone() */
1540
1541
1542 /* DecodePosixTimezone()
1543 * Interpret string as a POSIX-compatible timezone:
1544 * PST-hh:mm
1545 * PST+h
1546 * - thomas 2000-03-15
1547 */
1548 static int
DecodePosixTimezone(char * str,int * tzp)1549 DecodePosixTimezone(char *str, int *tzp)
1550 {
1551 int val,
1552 tz;
1553 int type;
1554 char *cp;
1555 char delim;
1556
1557 cp = str;
1558 while (*cp != '\0' && isalpha((unsigned char) *cp))
1559 cp++;
1560
1561 if (DecodeTimezone(cp, &tz) != 0)
1562 return -1;
1563
1564 delim = *cp;
1565 *cp = '\0';
1566 type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
1567 *cp = delim;
1568
1569 switch (type)
1570 {
1571 case DTZ:
1572 case TZ:
1573 *tzp = -(val + tz);
1574 break;
1575
1576 default:
1577 return -1;
1578 }
1579
1580 return 0;
1581 } /* DecodePosixTimezone() */
1582
1583 /* ParseDateTime()
1584 * Break string into tokens based on a date/time context.
1585 * Several field types are assigned:
1586 * DTK_NUMBER - digits and (possibly) a decimal point
1587 * DTK_DATE - digits and two delimiters, or digits and text
1588 * DTK_TIME - digits, colon delimiters, and possibly a decimal point
1589 * DTK_STRING - text (no digits)
1590 * DTK_SPECIAL - leading "+" or "-" followed by text
1591 * DTK_TZ - leading "+" or "-" followed by digits
1592 * Note that some field types can hold unexpected items:
1593 * DTK_NUMBER can hold date fields (yy.ddd)
1594 * DTK_STRING can hold months (January) and time zones (PST)
1595 * DTK_DATE can hold Posix time zones (GMT-8)
1596 *
1597 * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
1598 * bytes of space. On output, field[] entries will point into it.
1599 * The field[] and ftype[] arrays must have at least MAXDATEFIELDS entries.
1600 */
1601 int
ParseDateTime(char * timestr,char * lowstr,char ** field,int * ftype,int * numfields,char ** endstr)1602 ParseDateTime(char *timestr, char *lowstr,
1603 char **field, int *ftype, int *numfields, char **endstr)
1604 {
1605 int nf = 0;
1606 char *lp = lowstr;
1607
1608 *endstr = timestr;
1609 /* outer loop through fields */
1610 while (*(*endstr) != '\0')
1611 {
1612 /* Record start of current field */
1613 if (nf >= MAXDATEFIELDS)
1614 return -1;
1615 field[nf] = lp;
1616
1617 /* leading digit? then date or time */
1618 if (isdigit((unsigned char) *(*endstr)))
1619 {
1620 *lp++ = *(*endstr)++;
1621 while (isdigit((unsigned char) *(*endstr)))
1622 *lp++ = *(*endstr)++;
1623
1624 /* time field? */
1625 if (*(*endstr) == ':')
1626 {
1627 ftype[nf] = DTK_TIME;
1628 *lp++ = *(*endstr)++;
1629 while (isdigit((unsigned char) *(*endstr)) ||
1630 (*(*endstr) == ':') || (*(*endstr) == '.'))
1631 *lp++ = *(*endstr)++;
1632 }
1633 /* date field? allow embedded text month */
1634 else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1635 {
1636 /* save delimiting character to use later */
1637 char *dp = (*endstr);
1638
1639 *lp++ = *(*endstr)++;
1640 /* second field is all digits? then no embedded text month */
1641 if (isdigit((unsigned char) *(*endstr)))
1642 {
1643 ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
1644 while (isdigit((unsigned char) *(*endstr)))
1645 *lp++ = *(*endstr)++;
1646
1647 /*
1648 * insist that the delimiters match to get a three-field
1649 * date.
1650 */
1651 if (*(*endstr) == *dp)
1652 {
1653 ftype[nf] = DTK_DATE;
1654 *lp++ = *(*endstr)++;
1655 while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1656 *lp++ = *(*endstr)++;
1657 }
1658 }
1659 else
1660 {
1661 ftype[nf] = DTK_DATE;
1662 while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
1663 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1664 }
1665 }
1666
1667 /*
1668 * otherwise, number only and will determine year, month, day, or
1669 * concatenated fields later...
1670 */
1671 else
1672 ftype[nf] = DTK_NUMBER;
1673 }
1674 /* Leading decimal point? Then fractional seconds... */
1675 else if (*(*endstr) == '.')
1676 {
1677 *lp++ = *(*endstr)++;
1678 while (isdigit((unsigned char) *(*endstr)))
1679 *lp++ = *(*endstr)++;
1680
1681 ftype[nf] = DTK_NUMBER;
1682 }
1683
1684 /*
1685 * text? then date string, month, day of week, special, or timezone
1686 */
1687 else if (isalpha((unsigned char) *(*endstr)))
1688 {
1689 ftype[nf] = DTK_STRING;
1690 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1691 while (isalpha((unsigned char) *(*endstr)))
1692 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1693
1694 /*
1695 * Full date string with leading text month? Could also be a POSIX
1696 * time zone...
1697 */
1698 if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
1699 {
1700 char *dp = (*endstr);
1701
1702 ftype[nf] = DTK_DATE;
1703 *lp++ = *(*endstr)++;
1704 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
1705 *lp++ = *(*endstr)++;
1706 }
1707 }
1708 /* skip leading spaces */
1709 else if (isspace((unsigned char) *(*endstr)))
1710 {
1711 (*endstr)++;
1712 continue;
1713 }
1714 /* sign? then special or numeric timezone */
1715 else if (*(*endstr) == '+' || *(*endstr) == '-')
1716 {
1717 *lp++ = *(*endstr)++;
1718 /* soak up leading whitespace */
1719 while (isspace((unsigned char) *(*endstr)))
1720 (*endstr)++;
1721 /* numeric timezone? */
1722 if (isdigit((unsigned char) *(*endstr)))
1723 {
1724 ftype[nf] = DTK_TZ;
1725 *lp++ = *(*endstr)++;
1726 while (isdigit((unsigned char) *(*endstr)) ||
1727 (*(*endstr) == ':') || (*(*endstr) == '.'))
1728 *lp++ = *(*endstr)++;
1729 }
1730 /* special? */
1731 else if (isalpha((unsigned char) *(*endstr)))
1732 {
1733 ftype[nf] = DTK_SPECIAL;
1734 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1735 while (isalpha((unsigned char) *(*endstr)))
1736 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
1737 }
1738 /* otherwise something wrong... */
1739 else
1740 return -1;
1741 }
1742 /* ignore punctuation but use as delimiter */
1743 else if (ispunct((unsigned char) *(*endstr)))
1744 {
1745 (*endstr)++;
1746 continue;
1747
1748 }
1749 /* otherwise, something is not right... */
1750 else
1751 return -1;
1752
1753 /* force in a delimiter after each field */
1754 *lp++ = '\0';
1755 nf++;
1756 }
1757
1758 *numfields = nf;
1759
1760 return 0;
1761 } /* ParseDateTime() */
1762
1763
1764 /* DecodeDateTime()
1765 * Interpret previously parsed fields for general date and time.
1766 * Return 0 if full date, 1 if only time, and -1 if problems.
1767 * External format(s):
1768 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
1769 * "Fri Feb-7-1997 15:23:27"
1770 * "Feb-7-1997 15:23:27"
1771 * "2-7-1997 15:23:27"
1772 * "1997-2-7 15:23:27"
1773 * "1997.038 15:23:27" (day of year 1-366)
1774 * Also supports input in compact time:
1775 * "970207 152327"
1776 * "97038 152327"
1777 * "20011225T040506.789-07"
1778 *
1779 * Use the system-provided functions to get the current time zone
1780 * if not specified in the input string.
1781 * If the date is outside the time_t system-supported time range,
1782 * then assume UTC time zone. - thomas 1997-05-27
1783 */
1784 int
DecodeDateTime(char ** field,int * ftype,int nf,int * dtype,struct tm * tm,fsec_t * fsec,bool EuroDates)1785 DecodeDateTime(char **field, int *ftype, int nf,
1786 int *dtype, struct tm *tm, fsec_t *fsec, bool EuroDates)
1787 {
1788 int fmask = 0,
1789 tmask,
1790 type;
1791 int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */
1792 int i;
1793 int val;
1794 int mer = HR24;
1795 bool haveTextMonth = false;
1796 bool is2digits = false;
1797 bool bc = false;
1798 int t = 0;
1799 int *tzp = &t;
1800
1801 /***
1802 * We'll insist on at least all of the date fields, but initialize the
1803 * remaining fields in case they are not set later...
1804 ***/
1805 *dtype = DTK_DATE;
1806 tm->tm_hour = 0;
1807 tm->tm_min = 0;
1808 tm->tm_sec = 0;
1809 *fsec = 0;
1810 /* don't know daylight savings time status apriori */
1811 tm->tm_isdst = -1;
1812 if (tzp != NULL)
1813 *tzp = 0;
1814
1815 for (i = 0; i < nf; i++)
1816 {
1817 switch (ftype[i])
1818 {
1819 case DTK_DATE:
1820 /***
1821 * Integral julian day with attached time zone?
1822 * All other forms with JD will be separated into
1823 * distinct fields, so we handle just this case here.
1824 ***/
1825 if (ptype == DTK_JULIAN)
1826 {
1827 char *cp;
1828 int val;
1829
1830 if (tzp == NULL)
1831 return -1;
1832
1833 val = strtol(field[i], &cp, 10);
1834 if (*cp != '-')
1835 return -1;
1836
1837 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1838 /* Get the time zone from the end of the string */
1839 if (DecodeTimezone(cp, tzp) != 0)
1840 return -1;
1841
1842 tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1843 ptype = 0;
1844 break;
1845 }
1846 /***
1847 * Already have a date? Then this might be a POSIX time
1848 * zone with an embedded dash (e.g. "PST-3" == "EST") or
1849 * a run-together time with trailing time zone (e.g. hhmmss-zz).
1850 * - thomas 2001-12-25
1851 ***/
1852 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
1853 || (ptype != 0))
1854 {
1855 /* No time zone accepted? Then quit... */
1856 if (tzp == NULL)
1857 return -1;
1858
1859 if (isdigit((unsigned char) *field[i]) || ptype != 0)
1860 {
1861 char *cp;
1862
1863 if (ptype != 0)
1864 {
1865 /* Sanity check; should not fail this test */
1866 if (ptype != DTK_TIME)
1867 return -1;
1868 ptype = 0;
1869 }
1870
1871 /*
1872 * Starts with a digit but we already have a time
1873 * field? Then we are in trouble with a date and time
1874 * already...
1875 */
1876 if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1877 return -1;
1878
1879 if ((cp = strchr(field[i], '-')) == NULL)
1880 return -1;
1881
1882 /* Get the time zone from the end of the string */
1883 if (DecodeTimezone(cp, tzp) != 0)
1884 return -1;
1885 *cp = '\0';
1886
1887 /*
1888 * Then read the rest of the field as a concatenated
1889 * time
1890 */
1891 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
1892 &tmask, tm, fsec, &is2digits)) < 0)
1893 return -1;
1894
1895 /*
1896 * modify tmask after returning from
1897 * DecodeNumberField()
1898 */
1899 tmask |= DTK_M(TZ);
1900 }
1901 else
1902 {
1903 if (DecodePosixTimezone(field[i], tzp) != 0)
1904 return -1;
1905
1906 ftype[i] = DTK_TZ;
1907 tmask = DTK_M(TZ);
1908 }
1909 }
1910 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
1911 return -1;
1912 break;
1913
1914 case DTK_TIME:
1915 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
1916 return -1;
1917
1918 /*
1919 * Check upper limit on hours; other limits checked in
1920 * DecodeTime()
1921 */
1922 /* test for > 24:00:00 */
1923 if (tm->tm_hour > 24 ||
1924 (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
1925 return -1;
1926 break;
1927
1928 case DTK_TZ:
1929 {
1930 int tz;
1931
1932 if (tzp == NULL)
1933 return -1;
1934
1935 if (DecodeTimezone(field[i], &tz) != 0)
1936 return -1;
1937
1938 /*
1939 * Already have a time zone? Then maybe this is the second
1940 * field of a POSIX time: EST+3 (equivalent to PST)
1941 */
1942 if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
1943 ftype[i - 1] == DTK_TZ &&
1944 isalpha((unsigned char) *field[i - 1]))
1945 {
1946 *tzp -= tz;
1947 tmask = 0;
1948 }
1949 else
1950 {
1951 *tzp = tz;
1952 tmask = DTK_M(TZ);
1953 }
1954 }
1955 break;
1956
1957 case DTK_NUMBER:
1958
1959 /*
1960 * Was this an "ISO date" with embedded field labels? An
1961 * example is "y2001m02d04" - thomas 2001-02-04
1962 */
1963 if (ptype != 0)
1964 {
1965 char *cp;
1966 int val;
1967
1968 val = strtol(field[i], &cp, 10);
1969
1970 /*
1971 * only a few kinds are allowed to have an embedded
1972 * decimal
1973 */
1974 if (*cp == '.')
1975 switch (ptype)
1976 {
1977 case DTK_JULIAN:
1978 case DTK_TIME:
1979 case DTK_SECOND:
1980 break;
1981 default:
1982 return 1;
1983 break;
1984 }
1985 else if (*cp != '\0')
1986 return -1;
1987
1988 switch (ptype)
1989 {
1990 case DTK_YEAR:
1991 tm->tm_year = val;
1992 tmask = DTK_M(YEAR);
1993 break;
1994
1995 case DTK_MONTH:
1996
1997 /*
1998 * already have a month and hour? then assume
1999 * minutes
2000 */
2001 if ((fmask & DTK_M(MONTH)) != 0 &&
2002 (fmask & DTK_M(HOUR)) != 0)
2003 {
2004 tm->tm_min = val;
2005 tmask = DTK_M(MINUTE);
2006 }
2007 else
2008 {
2009 tm->tm_mon = val;
2010 tmask = DTK_M(MONTH);
2011 }
2012 break;
2013
2014 case DTK_DAY:
2015 tm->tm_mday = val;
2016 tmask = DTK_M(DAY);
2017 break;
2018
2019 case DTK_HOUR:
2020 tm->tm_hour = val;
2021 tmask = DTK_M(HOUR);
2022 break;
2023
2024 case DTK_MINUTE:
2025 tm->tm_min = val;
2026 tmask = DTK_M(MINUTE);
2027 break;
2028
2029 case DTK_SECOND:
2030 tm->tm_sec = val;
2031 tmask = DTK_M(SECOND);
2032 if (*cp == '.')
2033 {
2034 double frac;
2035
2036 frac = strtod(cp, &cp);
2037 if (*cp != '\0')
2038 return -1;
2039 *fsec = frac * 1000000;
2040 }
2041 break;
2042
2043 case DTK_TZ:
2044 tmask = DTK_M(TZ);
2045 if (DecodeTimezone(field[i], tzp) != 0)
2046 return -1;
2047 break;
2048
2049 case DTK_JULIAN:
2050 /***
2051 * previous field was a label for "julian date"?
2052 ***/
2053 tmask = DTK_DATE_M;
2054 j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2055 /* fractional Julian Day? */
2056 if (*cp == '.')
2057 {
2058 double time;
2059
2060 time = strtod(cp, &cp);
2061 if (*cp != '\0')
2062 return -1;
2063
2064 tmask |= DTK_TIME_M;
2065 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
2066 }
2067 break;
2068
2069 case DTK_TIME:
2070 /* previous field was "t" for ISO time */
2071 if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
2072 &tmask, tm, fsec, &is2digits)) < 0)
2073 return -1;
2074
2075 if (tmask != DTK_TIME_M)
2076 return -1;
2077 break;
2078
2079 default:
2080 return -1;
2081 break;
2082 }
2083
2084 ptype = 0;
2085 *dtype = DTK_DATE;
2086 }
2087 else
2088 {
2089 char *cp;
2090 int flen;
2091
2092 flen = strlen(field[i]);
2093 cp = strchr(field[i], '.');
2094
2095 /* Embedded decimal and no date yet? */
2096 if (cp != NULL && !(fmask & DTK_DATE_M))
2097 {
2098 if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
2099 return -1;
2100 }
2101 /* embedded decimal and several digits before? */
2102 else if (cp != NULL && flen - strlen(cp) > 2)
2103 {
2104 /*
2105 * Interpret as a concatenated date or time Set the
2106 * type field to allow decoding other fields later.
2107 * Example: 20011223 or 040506
2108 */
2109 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2110 &tmask, tm, fsec, &is2digits)) < 0)
2111 return -1;
2112 }
2113 else if (flen > 4)
2114 {
2115 if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
2116 &tmask, tm, fsec, &is2digits)) < 0)
2117 return -1;
2118 }
2119 /* otherwise it is a single date/time field... */
2120 else if (DecodeNumber(flen, field[i], fmask,
2121 &tmask, tm, fsec, &is2digits, EuroDates) != 0)
2122 return -1;
2123 }
2124 break;
2125
2126 case DTK_STRING:
2127 case DTK_SPECIAL:
2128 type = DecodeSpecial(i, field[i], &val);
2129 if (type == IGNORE_DTF)
2130 continue;
2131
2132 tmask = DTK_M(type);
2133 switch (type)
2134 {
2135 case RESERV:
2136 switch (val)
2137 {
2138 case DTK_NOW:
2139 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
2140 *dtype = DTK_DATE;
2141 GetCurrentDateTime(tm);
2142 break;
2143
2144 case DTK_YESTERDAY:
2145 tmask = DTK_DATE_M;
2146 *dtype = DTK_DATE;
2147 GetCurrentDateTime(tm);
2148 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
2149 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2150 tm->tm_hour = 0;
2151 tm->tm_min = 0;
2152 tm->tm_sec = 0;
2153 break;
2154
2155 case DTK_TODAY:
2156 tmask = DTK_DATE_M;
2157 *dtype = DTK_DATE;
2158 GetCurrentDateTime(tm);
2159 tm->tm_hour = 0;
2160 tm->tm_min = 0;
2161 tm->tm_sec = 0;
2162 break;
2163
2164 case DTK_TOMORROW:
2165 tmask = DTK_DATE_M;
2166 *dtype = DTK_DATE;
2167 GetCurrentDateTime(tm);
2168 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
2169 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2170 tm->tm_hour = 0;
2171 tm->tm_min = 0;
2172 tm->tm_sec = 0;
2173 break;
2174
2175 case DTK_ZULU:
2176 tmask = (DTK_TIME_M | DTK_M(TZ));
2177 *dtype = DTK_DATE;
2178 tm->tm_hour = 0;
2179 tm->tm_min = 0;
2180 tm->tm_sec = 0;
2181 if (tzp != NULL)
2182 *tzp = 0;
2183 break;
2184
2185 default:
2186 *dtype = val;
2187 }
2188
2189 break;
2190
2191 case MONTH:
2192
2193 /*
2194 * already have a (numeric) month? then see if we can
2195 * substitute...
2196 */
2197 if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
2198 !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
2199 {
2200 tm->tm_mday = tm->tm_mon;
2201 tmask = DTK_M(DAY);
2202 }
2203 haveTextMonth = true;
2204 tm->tm_mon = val;
2205 break;
2206
2207 case DTZMOD:
2208
2209 /*
2210 * daylight savings time modifier (solves "MET DST"
2211 * syntax)
2212 */
2213 tmask |= DTK_M(DTZ);
2214 tm->tm_isdst = 1;
2215 if (tzp == NULL)
2216 return -1;
2217 *tzp -= val;
2218 break;
2219
2220 case DTZ:
2221
2222 /*
2223 * set mask for TZ here _or_ check for DTZ later when
2224 * getting default timezone
2225 */
2226 tmask |= DTK_M(TZ);
2227 tm->tm_isdst = 1;
2228 if (tzp == NULL)
2229 return -1;
2230 *tzp = -val;
2231 ftype[i] = DTK_TZ;
2232 break;
2233
2234 case TZ:
2235 tm->tm_isdst = 0;
2236 if (tzp == NULL)
2237 return -1;
2238 *tzp = -val;
2239 ftype[i] = DTK_TZ;
2240 break;
2241
2242 case IGNORE_DTF:
2243 break;
2244
2245 case AMPM:
2246 mer = val;
2247 break;
2248
2249 case ADBC:
2250 bc = (val == BC);
2251 break;
2252
2253 case DOW:
2254 tm->tm_wday = val;
2255 break;
2256
2257 case UNITS:
2258 tmask = 0;
2259 ptype = val;
2260 break;
2261
2262 case ISOTIME:
2263
2264 /*
2265 * This is a filler field "t" indicating that the next
2266 * field is time. Try to verify that this is sensible.
2267 */
2268 tmask = 0;
2269
2270 /* No preceding date? Then quit... */
2271 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2272 return -1;
2273
2274 /***
2275 * We will need one of the following fields:
2276 * DTK_NUMBER should be hhmmss.fff
2277 * DTK_TIME should be hh:mm:ss.fff
2278 * DTK_DATE should be hhmmss-zz
2279 ***/
2280 if (i >= nf - 1 ||
2281 (ftype[i + 1] != DTK_NUMBER &&
2282 ftype[i + 1] != DTK_TIME &&
2283 ftype[i + 1] != DTK_DATE))
2284 return -1;
2285
2286 ptype = val;
2287 break;
2288
2289 default:
2290 return -1;
2291 }
2292 break;
2293
2294 default:
2295 return -1;
2296 }
2297
2298 if (tmask & fmask)
2299 return -1;
2300 fmask |= tmask;
2301 }
2302
2303 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2304 if (bc)
2305 {
2306 if (tm->tm_year > 0)
2307 tm->tm_year = -(tm->tm_year - 1);
2308 else
2309 return -1;
2310 }
2311 else if (is2digits)
2312 {
2313 if (tm->tm_year < 70)
2314 tm->tm_year += 2000;
2315 else if (tm->tm_year < 100)
2316 tm->tm_year += 1900;
2317 }
2318
2319 if (mer != HR24 && tm->tm_hour > 12)
2320 return -1;
2321 if (mer == AM && tm->tm_hour == 12)
2322 tm->tm_hour = 0;
2323 else if (mer == PM && tm->tm_hour != 12)
2324 tm->tm_hour += 12;
2325
2326 /* do additional checking for full date specs... */
2327 if (*dtype == DTK_DATE)
2328 {
2329 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2330 return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
2331
2332 /*
2333 * check for valid day of month, now that we know for sure the month
2334 * and year...
2335 */
2336 if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2337 return -1;
2338
2339 /*
2340 * backend tried to find local timezone here but we don't use the
2341 * result afterwards anyway so we only check for this error: daylight
2342 * savings time modifier but no standard timezone?
2343 */
2344 if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
2345 return -1;
2346 }
2347
2348 return 0;
2349 } /* DecodeDateTime() */
2350
2351 /* Function works as follows:
2352 *
2353 *
2354 * */
2355
2356 static char *
find_end_token(char * str,char * fmt)2357 find_end_token(char *str, char *fmt)
2358 {
2359 /*
2360 * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
2361 *
2362 * we extract the 28, we read the percent sign and the type "d" then this
2363 * functions gets called as find_end_token("28the day12the hour", "the
2364 * day%hthehour")
2365 *
2366 * fmt points to "the day%hthehour", next_percent points to %hthehour and
2367 * we have to find a match for everything between these positions ("the
2368 * day"). We look for "the day" in str and know that the pattern we are
2369 * about to scan ends where this string starts (right after the "28")
2370 *
2371 * At the end, *fmt is '\0' and *str isn't. end_position then is
2372 * unchanged.
2373 */
2374 char *end_position = NULL;
2375 char *next_percent,
2376 *subst_location = NULL;
2377 int scan_offset = 0;
2378 char last_char;
2379
2380 /* are we at the end? */
2381 if (!*fmt)
2382 {
2383 end_position = fmt;
2384 return end_position;
2385 }
2386
2387 /* not at the end */
2388 while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
2389 {
2390 /*
2391 * there is no delimiter, skip to the next delimiter if we're reading
2392 * a number and then something that is not a number "9:15pm", we might
2393 * be able to recover with the strtol end pointer. Go for the next
2394 * percent sign
2395 */
2396 scan_offset += 2;
2397 }
2398 next_percent = strchr(fmt + scan_offset, '%');
2399 if (next_percent)
2400 {
2401 /*
2402 * we don't want to allocate extra memory, so we temporarily set the
2403 * '%' sign to '\0' and call strstr However since we allow whitespace
2404 * to float around everything, we have to shorten the pattern until we
2405 * reach a non-whitespace character
2406 */
2407
2408 subst_location = next_percent;
2409 while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
2410 subst_location--;
2411 last_char = *subst_location;
2412 *subst_location = '\0';
2413
2414 /*
2415 * the haystack is the str and the needle is the original fmt but it
2416 * ends at the position where the next percent sign would be
2417 */
2418
2419 /*
2420 * There is one special case. Imagine: str = " 2", fmt = "%d %...",
2421 * since we want to allow blanks as "dynamic" padding we have to
2422 * accept this. Now, we are called with a fmt of " %..." and look for
2423 * " " in str. We find it at the first position and never read the
2424 * 2...
2425 */
2426 while (*str == ' ')
2427 str++;
2428 end_position = strstr(str, fmt + scan_offset);
2429 *subst_location = last_char;
2430 }
2431 else
2432 {
2433 /*
2434 * there is no other percent sign. So everything up to the end has to
2435 * match.
2436 */
2437 end_position = str + strlen(str);
2438 }
2439 if (!end_position)
2440 {
2441 /*
2442 * maybe we have the following case:
2443 *
2444 * str = "4:15am" fmt = "%M:%S %p"
2445 *
2446 * at this place we could have
2447 *
2448 * str = "15am" fmt = " %p"
2449 *
2450 * and have set fmt to " " because overwrote the % sign with a NULL
2451 *
2452 * In this case where we would have to match a space but can't find
2453 * it, set end_position to the end of the string
2454 */
2455 if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
2456 end_position = str + strlen(str);
2457 }
2458 return end_position;
2459 }
2460
2461 static int
pgtypes_defmt_scan(union un_fmt_comb * scan_val,int scan_type,char ** pstr,char * pfmt)2462 pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char *pfmt)
2463 {
2464 /*
2465 * scan everything between pstr and pstr_end. This is not including the
2466 * last character so we might set it to '\0' for the parsing
2467 */
2468
2469 char last_char;
2470 int err = 0;
2471 char *pstr_end;
2472 char *strtol_end = NULL;
2473
2474 while (**pstr == ' ')
2475 pstr++;
2476 pstr_end = find_end_token(*pstr, pfmt);
2477 if (!pstr_end)
2478 {
2479 /* there was an error, no match */
2480 return 1;
2481 }
2482 last_char = *pstr_end;
2483 *pstr_end = '\0';
2484
2485 switch (scan_type)
2486 {
2487 case PGTYPES_TYPE_UINT:
2488
2489 /*
2490 * numbers may be blank-padded, this is the only deviation from
2491 * the fmt-string we accept
2492 */
2493 while (**pstr == ' ')
2494 (*pstr)++;
2495 errno = 0;
2496 scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
2497 if (errno)
2498 err = 1;
2499 break;
2500 case PGTYPES_TYPE_UINT_LONG:
2501 while (**pstr == ' ')
2502 (*pstr)++;
2503 errno = 0;
2504 scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
2505 if (errno)
2506 err = 1;
2507 break;
2508 case PGTYPES_TYPE_STRING_MALLOCED:
2509 scan_val->str_val = pgtypes_strdup(*pstr);
2510 if (scan_val->str_val == NULL)
2511 err = 1;
2512 break;
2513 }
2514 if (strtol_end && *strtol_end)
2515 *pstr = strtol_end;
2516 else
2517 *pstr = pstr_end;
2518 *pstr_end = last_char;
2519 return err;
2520 }
2521
2522 /* XXX range checking */
2523 int
PGTYPEStimestamp_defmt_scan(char ** str,char * fmt,timestamp * d,int * year,int * month,int * day,int * hour,int * minute,int * second,int * tz)2524 PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
2525 int *year, int *month, int *day,
2526 int *hour, int *minute, int *second,
2527 int *tz)
2528 {
2529 union un_fmt_comb scan_val;
2530 int scan_type;
2531
2532 char *pstr,
2533 *pfmt,
2534 *tmp;
2535 int err = 1;
2536 unsigned int j;
2537 struct tm tm;
2538
2539 pfmt = fmt;
2540 pstr = *str;
2541
2542 while (*pfmt)
2543 {
2544 err = 0;
2545 while (*pfmt == ' ')
2546 pfmt++;
2547 while (*pstr == ' ')
2548 pstr++;
2549 if (*pfmt != '%')
2550 {
2551 if (*pfmt == *pstr)
2552 {
2553 pfmt++;
2554 pstr++;
2555 }
2556 else
2557 {
2558 /* Error: no match */
2559 err = 1;
2560 return err;
2561 }
2562 continue;
2563 }
2564 /* here *pfmt equals '%' */
2565 pfmt++;
2566 switch (*pfmt)
2567 {
2568 case 'a':
2569 pfmt++;
2570
2571 /*
2572 * we parse the day and see if it is a week day but we do not
2573 * check if the week day really matches the date
2574 */
2575 err = 1;
2576 j = 0;
2577 while (pgtypes_date_weekdays_short[j])
2578 {
2579 if (strncmp(pgtypes_date_weekdays_short[j], pstr,
2580 strlen(pgtypes_date_weekdays_short[j])) == 0)
2581 {
2582 /* found it */
2583 err = 0;
2584 pstr += strlen(pgtypes_date_weekdays_short[j]);
2585 break;
2586 }
2587 j++;
2588 }
2589 break;
2590 case 'A':
2591 /* see note above */
2592 pfmt++;
2593 err = 1;
2594 j = 0;
2595 while (days[j])
2596 {
2597 if (strncmp(days[j], pstr, strlen(days[j])) == 0)
2598 {
2599 /* found it */
2600 err = 0;
2601 pstr += strlen(days[j]);
2602 break;
2603 }
2604 j++;
2605 }
2606 break;
2607 case 'b':
2608 case 'h':
2609 pfmt++;
2610 err = 1;
2611 j = 0;
2612 while (months[j])
2613 {
2614 if (strncmp(months[j], pstr, strlen(months[j])) == 0)
2615 {
2616 /* found it */
2617 err = 0;
2618 pstr += strlen(months[j]);
2619 *month = j + 1;
2620 break;
2621 }
2622 j++;
2623 }
2624 break;
2625 case 'B':
2626 /* see note above */
2627 pfmt++;
2628 err = 1;
2629 j = 0;
2630 while (pgtypes_date_months[j])
2631 {
2632 if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
2633 {
2634 /* found it */
2635 err = 0;
2636 pstr += strlen(pgtypes_date_months[j]);
2637 *month = j + 1;
2638 break;
2639 }
2640 j++;
2641 }
2642 break;
2643 case 'c':
2644 /* XXX */
2645 break;
2646 case 'C':
2647 pfmt++;
2648 scan_type = PGTYPES_TYPE_UINT;
2649 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2650 *year = scan_val.uint_val * 100;
2651 break;
2652 case 'd':
2653 case 'e':
2654 pfmt++;
2655 scan_type = PGTYPES_TYPE_UINT;
2656 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2657 *day = scan_val.uint_val;
2658 break;
2659 case 'D':
2660
2661 /*
2662 * we have to concatenate the strings in order to be able to
2663 * find the end of the substitution
2664 */
2665 pfmt++;
2666 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
2667 strcpy(tmp, "%m/%d/%y");
2668 strcat(tmp, pfmt);
2669 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2670 free(tmp);
2671 return err;
2672 case 'm':
2673 pfmt++;
2674 scan_type = PGTYPES_TYPE_UINT;
2675 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2676 *month = scan_val.uint_val;
2677 break;
2678 case 'y':
2679 case 'g': /* XXX difference to y (ISO) */
2680 pfmt++;
2681 scan_type = PGTYPES_TYPE_UINT;
2682 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2683 if (*year < 0)
2684 {
2685 /* not yet set */
2686 *year = scan_val.uint_val;
2687 }
2688 else
2689 *year += scan_val.uint_val;
2690 if (*year < 100)
2691 *year += 1900;
2692 break;
2693 case 'G':
2694 /* XXX difference to %V (ISO) */
2695 pfmt++;
2696 scan_type = PGTYPES_TYPE_UINT;
2697 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2698 *year = scan_val.uint_val;
2699 break;
2700 case 'H':
2701 case 'I':
2702 case 'k':
2703 case 'l':
2704 pfmt++;
2705 scan_type = PGTYPES_TYPE_UINT;
2706 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2707 *hour += scan_val.uint_val;
2708 break;
2709 case 'j':
2710 pfmt++;
2711 scan_type = PGTYPES_TYPE_UINT;
2712 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2713
2714 /*
2715 * XXX what should we do with that? We could say that it's
2716 * sufficient if we have the year and the day within the year
2717 * to get at least a specific day.
2718 */
2719 break;
2720 case 'M':
2721 pfmt++;
2722 scan_type = PGTYPES_TYPE_UINT;
2723 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2724 *minute = scan_val.uint_val;
2725 break;
2726 case 'n':
2727 pfmt++;
2728 if (*pstr == '\n')
2729 pstr++;
2730 else
2731 err = 1;
2732 break;
2733 case 'p':
2734 err = 1;
2735 pfmt++;
2736 if (strncmp(pstr, "am", 2) == 0)
2737 {
2738 *hour += 0;
2739 err = 0;
2740 pstr += 2;
2741 }
2742 if (strncmp(pstr, "a.m.", 4) == 0)
2743 {
2744 *hour += 0;
2745 err = 0;
2746 pstr += 4;
2747 }
2748 if (strncmp(pstr, "pm", 2) == 0)
2749 {
2750 *hour += 12;
2751 err = 0;
2752 pstr += 2;
2753 }
2754 if (strncmp(pstr, "p.m.", 4) == 0)
2755 {
2756 *hour += 12;
2757 err = 0;
2758 pstr += 4;
2759 }
2760 break;
2761 case 'P':
2762 err = 1;
2763 pfmt++;
2764 if (strncmp(pstr, "AM", 2) == 0)
2765 {
2766 *hour += 0;
2767 err = 0;
2768 pstr += 2;
2769 }
2770 if (strncmp(pstr, "A.M.", 4) == 0)
2771 {
2772 *hour += 0;
2773 err = 0;
2774 pstr += 4;
2775 }
2776 if (strncmp(pstr, "PM", 2) == 0)
2777 {
2778 *hour += 12;
2779 err = 0;
2780 pstr += 2;
2781 }
2782 if (strncmp(pstr, "P.M.", 4) == 0)
2783 {
2784 *hour += 12;
2785 err = 0;
2786 pstr += 4;
2787 }
2788 break;
2789 case 'r':
2790 pfmt++;
2791 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
2792 strcpy(tmp, "%I:%M:%S %p");
2793 strcat(tmp, pfmt);
2794 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2795 free(tmp);
2796 return err;
2797 case 'R':
2798 pfmt++;
2799 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
2800 strcpy(tmp, "%H:%M");
2801 strcat(tmp, pfmt);
2802 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2803 free(tmp);
2804 return err;
2805 case 's':
2806 pfmt++;
2807 scan_type = PGTYPES_TYPE_UINT_LONG;
2808 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2809 /* number of seconds in scan_val.luint_val */
2810 {
2811 struct tm *tms;
2812 time_t et = (time_t) scan_val.luint_val;
2813
2814 tms = gmtime(&et);
2815
2816 if (tms)
2817 {
2818 *year = tms->tm_year + 1900;
2819 *month = tms->tm_mon + 1;
2820 *day = tms->tm_mday;
2821 *hour = tms->tm_hour;
2822 *minute = tms->tm_min;
2823 *second = tms->tm_sec;
2824 }
2825 else
2826 err = 1;
2827 }
2828 break;
2829 case 'S':
2830 pfmt++;
2831 scan_type = PGTYPES_TYPE_UINT;
2832 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2833 *second = scan_val.uint_val;
2834 break;
2835 case 't':
2836 pfmt++;
2837 if (*pstr == '\t')
2838 pstr++;
2839 else
2840 err = 1;
2841 break;
2842 case 'T':
2843 pfmt++;
2844 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
2845 strcpy(tmp, "%H:%M:%S");
2846 strcat(tmp, pfmt);
2847 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
2848 free(tmp);
2849 return err;
2850 case 'u':
2851 pfmt++;
2852 scan_type = PGTYPES_TYPE_UINT;
2853 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2854 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
2855 err = 1;
2856 break;
2857 case 'U':
2858 pfmt++;
2859 scan_type = PGTYPES_TYPE_UINT;
2860 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2861 if (scan_val.uint_val > 53)
2862 err = 1;
2863 break;
2864 case 'V':
2865 pfmt++;
2866 scan_type = PGTYPES_TYPE_UINT;
2867 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2868 if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
2869 err = 1;
2870 break;
2871 case 'w':
2872 pfmt++;
2873 scan_type = PGTYPES_TYPE_UINT;
2874 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2875 if (scan_val.uint_val > 6)
2876 err = 1;
2877 break;
2878 case 'W':
2879 pfmt++;
2880 scan_type = PGTYPES_TYPE_UINT;
2881 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2882 if (scan_val.uint_val > 53)
2883 err = 1;
2884 break;
2885 case 'x':
2886 case 'X':
2887 /* XXX */
2888 break;
2889 case 'Y':
2890 pfmt++;
2891 scan_type = PGTYPES_TYPE_UINT;
2892 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2893 *year = scan_val.uint_val;
2894 break;
2895 case 'z':
2896 pfmt++;
2897 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2898 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2899 if (!err)
2900 {
2901 err = DecodeTimezone(scan_val.str_val, tz);
2902 free(scan_val.str_val);
2903 }
2904 break;
2905 case 'Z':
2906 pfmt++;
2907 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
2908 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
2909 if (!err)
2910 {
2911 /*
2912 * XXX use DecodeSpecial instead? Do we need strcasecmp
2913 * here?
2914 */
2915 err = 1;
2916 for (j = 0; j < szdatetktbl; j++)
2917 {
2918 if ((datetktbl[j].type == TZ || datetktbl[j].type == DTZ) &&
2919 pg_strcasecmp(datetktbl[j].token,
2920 scan_val.str_val) == 0)
2921 {
2922 *tz = -datetktbl[j].value;
2923 err = 0;
2924 break;
2925 }
2926 }
2927 free(scan_val.str_val);
2928 }
2929 break;
2930 case '+':
2931 /* XXX */
2932 break;
2933 case '%':
2934 pfmt++;
2935 if (*pstr == '%')
2936 pstr++;
2937 else
2938 err = 1;
2939 break;
2940 default:
2941 err = 1;
2942 }
2943 }
2944 if (!err)
2945 {
2946 if (*second < 0)
2947 *second = 0;
2948 if (*minute < 0)
2949 *minute = 0;
2950 if (*hour < 0)
2951 *hour = 0;
2952 if (*day < 0)
2953 {
2954 err = 1;
2955 *day = 1;
2956 }
2957 if (*month < 0)
2958 {
2959 err = 1;
2960 *month = 1;
2961 }
2962 if (*year < 0)
2963 {
2964 err = 1;
2965 *year = 1970;
2966 }
2967
2968 if (*second > 59)
2969 {
2970 err = 1;
2971 *second = 0;
2972 }
2973 if (*minute > 59)
2974 {
2975 err = 1;
2976 *minute = 0;
2977 }
2978 if (*hour > 24 || /* test for > 24:00:00 */
2979 (*hour == 24 && (*minute > 0 || *second > 0)))
2980 {
2981 err = 1;
2982 *hour = 0;
2983 }
2984 if (*month > MONTHS_PER_YEAR)
2985 {
2986 err = 1;
2987 *month = 1;
2988 }
2989 if (*day > day_tab[isleap(*year)][*month - 1])
2990 {
2991 *day = day_tab[isleap(*year)][*month - 1];
2992 err = 1;
2993 }
2994
2995 tm.tm_sec = *second;
2996 tm.tm_min = *minute;
2997 tm.tm_hour = *hour;
2998 tm.tm_mday = *day;
2999 tm.tm_mon = *month;
3000 tm.tm_year = *year;
3001
3002 tm2timestamp(&tm, 0, tz, d);
3003 }
3004 return err;
3005 }
3006
3007 /* XXX: 1900 is compiled in as the base for years */
3008