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