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