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