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