Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * datetime.c
4 : * Support functions for date/time types.
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/adt/datetime.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <ctype.h>
18 : #include <limits.h>
19 : #include <math.h>
20 :
21 : #include "access/htup_details.h"
22 : #include "access/xact.h"
23 : #include "catalog/pg_type.h"
24 : #include "common/int.h"
25 : #include "common/string.h"
26 : #include "funcapi.h"
27 : #include "miscadmin.h"
28 : #include "nodes/nodeFuncs.h"
29 : #include "parser/scansup.h"
30 : #include "utils/builtins.h"
31 : #include "utils/date.h"
32 : #include "utils/datetime.h"
33 : #include "utils/guc.h"
34 : #include "utils/memutils.h"
35 : #include "utils/tzparser.h"
36 :
37 : static int DecodeNumber(int flen, char *str, bool haveTextMonth,
38 : int fmask, int *tmask,
39 : struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
40 : static int DecodeNumberField(int len, char *str,
41 : int fmask, int *tmask,
42 : struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
43 : static int DecodeTimeCommon(char *str, int fmask, int range,
44 : int *tmask, struct pg_itm *itm);
45 : static int DecodeTime(char *str, int fmask, int range,
46 : int *tmask, struct pg_tm *tm, fsec_t *fsec);
47 : static int DecodeTimeForInterval(char *str, int fmask, int range,
48 : int *tmask, struct pg_itm_in *itm_in);
49 : static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
50 : static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
51 : struct pg_tm *tm);
52 : static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
53 : int precision, bool fillzeros);
54 : static bool int64_multiply_add(int64 val, int64 multiplier, int64 *sum);
55 : static bool AdjustFractMicroseconds(double frac, int64 scale,
56 : struct pg_itm_in *itm_in);
57 : static bool AdjustFractDays(double frac, int scale,
58 : struct pg_itm_in *itm_in);
59 : static bool AdjustFractYears(double frac, int scale,
60 : struct pg_itm_in *itm_in);
61 : static bool AdjustMicroseconds(int64 val, double fval, int64 scale,
62 : struct pg_itm_in *itm_in);
63 : static bool AdjustDays(int64 val, int scale,
64 : struct pg_itm_in *itm_in);
65 : static bool AdjustMonths(int64 val, struct pg_itm_in *itm_in);
66 : static bool AdjustYears(int64 val, int scale,
67 : struct pg_itm_in *itm_in);
68 : static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
69 : pg_time_t *tp);
70 : static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t,
71 : const char *abbr, pg_tz *tzp,
72 : int *offset, int *isdst);
73 : static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
74 : DateTimeErrorExtra *extra);
75 :
76 :
77 : const int day_tab[2][13] =
78 : {
79 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
80 : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
81 : };
82 :
83 : const char *const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
84 : "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
85 :
86 : const char *const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
87 : "Thursday", "Friday", "Saturday", NULL};
88 :
89 :
90 : /*****************************************************************************
91 : * PRIVATE ROUTINES *
92 : *****************************************************************************/
93 :
94 : /*
95 : * datetktbl holds date/time keywords.
96 : *
97 : * Note that this table must be strictly alphabetically ordered to allow an
98 : * O(ln(N)) search algorithm to be used.
99 : *
100 : * The token field must be NUL-terminated; we truncate entries to TOKMAXLEN
101 : * characters to fit.
102 : *
103 : * The static table contains no TZ, DTZ, or DYNTZ entries; rather those
104 : * are loaded from configuration files and stored in zoneabbrevtbl, whose
105 : * abbrevs[] field has the same format as the static datetktbl.
106 : */
107 : static const datetkn datetktbl[] = {
108 : /* token, type, value */
109 : {"+infinity", RESERV, DTK_LATE}, /* same as "infinity" */
110 : {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
111 : {DA_D, ADBC, AD}, /* "ad" for years > 0 */
112 : {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
113 : {"am", AMPM, AM},
114 : {"apr", MONTH, 4},
115 : {"april", MONTH, 4},
116 : {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
117 : {"aug", MONTH, 8},
118 : {"august", MONTH, 8},
119 : {DB_C, ADBC, BC}, /* "bc" for years <= 0 */
120 : {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
121 : {"dec", MONTH, 12},
122 : {"december", MONTH, 12},
123 : {"dow", UNITS, DTK_DOW}, /* day of week */
124 : {"doy", UNITS, DTK_DOY}, /* day of year */
125 : {"dst", DTZMOD, SECS_PER_HOUR},
126 : {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
127 : {"feb", MONTH, 2},
128 : {"february", MONTH, 2},
129 : {"fri", DOW, 5},
130 : {"friday", DOW, 5},
131 : {"h", UNITS, DTK_HOUR}, /* "hour" */
132 : {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
133 : {"isodow", UNITS, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */
134 : {"isoyear", UNITS, DTK_ISOYEAR}, /* year in terms of the ISO week date */
135 : {"j", UNITS, DTK_JULIAN},
136 : {"jan", MONTH, 1},
137 : {"january", MONTH, 1},
138 : {"jd", UNITS, DTK_JULIAN},
139 : {"jul", MONTH, 7},
140 : {"julian", UNITS, DTK_JULIAN},
141 : {"july", MONTH, 7},
142 : {"jun", MONTH, 6},
143 : {"june", MONTH, 6},
144 : {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
145 : {"mar", MONTH, 3},
146 : {"march", MONTH, 3},
147 : {"may", MONTH, 5},
148 : {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
149 : {"mon", DOW, 1},
150 : {"monday", DOW, 1},
151 : {"nov", MONTH, 11},
152 : {"november", MONTH, 11},
153 : {NOW, RESERV, DTK_NOW}, /* current transaction time */
154 : {"oct", MONTH, 10},
155 : {"october", MONTH, 10},
156 : {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
157 : {"pm", AMPM, PM},
158 : {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
159 : {"sat", DOW, 6},
160 : {"saturday", DOW, 6},
161 : {"sep", MONTH, 9},
162 : {"sept", MONTH, 9},
163 : {"september", MONTH, 9},
164 : {"sun", DOW, 0},
165 : {"sunday", DOW, 0},
166 : {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
167 : {"thu", DOW, 4},
168 : {"thur", DOW, 4},
169 : {"thurs", DOW, 4},
170 : {"thursday", DOW, 4},
171 : {TODAY, RESERV, DTK_TODAY}, /* midnight */
172 : {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
173 : {"tue", DOW, 2},
174 : {"tues", DOW, 2},
175 : {"tuesday", DOW, 2},
176 : {"wed", DOW, 3},
177 : {"wednesday", DOW, 3},
178 : {"weds", DOW, 3},
179 : {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
180 : {YESTERDAY, RESERV, DTK_YESTERDAY} /* yesterday midnight */
181 : };
182 :
183 : static const int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
184 :
185 : /*
186 : * deltatktbl: same format as datetktbl, but holds keywords used to represent
187 : * time units (eg, for intervals, and for EXTRACT).
188 : */
189 : static const datetkn deltatktbl[] = {
190 : /* token, type, value */
191 : {"@", IGNORE_DTF, 0}, /* postgres relative prefix */
192 : {DAGO, AGO, 0}, /* "ago" indicates negative time offset */
193 : {"c", UNITS, DTK_CENTURY}, /* "century" relative */
194 : {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
195 : {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
196 : {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
197 : {"d", UNITS, DTK_DAY}, /* "day" relative */
198 : {DDAY, UNITS, DTK_DAY}, /* "day" relative */
199 : {"days", UNITS, DTK_DAY}, /* "days" relative */
200 : {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
201 : {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
202 : {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
203 : {"decs", UNITS, DTK_DECADE}, /* "decades" relative */
204 : {"h", UNITS, DTK_HOUR}, /* "hour" relative */
205 : {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
206 : {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
207 : {"hr", UNITS, DTK_HOUR}, /* "hour" relative */
208 : {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
209 : {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
210 : {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
211 : {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
212 : {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
213 : {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
214 : {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
215 : {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
216 : {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
217 : {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
218 : {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
219 : {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
220 : {"mon", UNITS, DTK_MONTH}, /* "months" relative */
221 : {"mons", UNITS, DTK_MONTH}, /* "months" relative */
222 : {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
223 : {"months", UNITS, DTK_MONTH},
224 : {"ms", UNITS, DTK_MILLISEC},
225 : {"msec", UNITS, DTK_MILLISEC},
226 : {DMILLISEC, UNITS, DTK_MILLISEC},
227 : {"mseconds", UNITS, DTK_MILLISEC},
228 : {"msecs", UNITS, DTK_MILLISEC},
229 : {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
230 : {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
231 : {"s", UNITS, DTK_SECOND},
232 : {"sec", UNITS, DTK_SECOND},
233 : {DSECOND, UNITS, DTK_SECOND},
234 : {"seconds", UNITS, DTK_SECOND},
235 : {"secs", UNITS, DTK_SECOND},
236 : {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
237 : {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
238 : {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
239 : {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
240 : {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
241 : {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
242 : {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
243 : {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
244 : {"w", UNITS, DTK_WEEK}, /* "week" relative */
245 : {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
246 : {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
247 : {"y", UNITS, DTK_YEAR}, /* "year" relative */
248 : {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
249 : {"years", UNITS, DTK_YEAR}, /* "years" relative */
250 : {"yr", UNITS, DTK_YEAR}, /* "year" relative */
251 : {"yrs", UNITS, DTK_YEAR} /* "years" relative */
252 : };
253 :
254 : static const int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
255 :
256 : static TimeZoneAbbrevTable *zoneabbrevtbl = NULL;
257 :
258 : /* Caches of recent lookup results in the above tables */
259 :
260 : static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
261 :
262 : static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
263 :
264 : static const datetkn *abbrevcache[MAXDATEFIELDS] = {NULL};
265 :
266 :
267 : /*
268 : * Calendar time to Julian date conversions.
269 : * Julian date is commonly used in astronomical applications,
270 : * since it is numerically accurate and computationally simple.
271 : * The algorithms here will accurately convert between Julian day
272 : * and calendar date for all non-negative Julian days
273 : * (i.e. from Nov 24, -4713 on).
274 : *
275 : * Rewritten to eliminate overflow problems. This now allows the
276 : * routines to work correctly for all Julian day counts from
277 : * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
278 : * a 32-bit integer. Longer types should also work to the limits
279 : * of their precision.
280 : *
281 : * Actually, date2j() will work sanely, in the sense of producing
282 : * valid negative Julian dates, significantly before Nov 24, -4713.
283 : * We rely on it to do so back to Nov 1, -4713; see IS_VALID_JULIAN()
284 : * and associated commentary in timestamp.h.
285 : */
286 :
287 : int
288 333772 : date2j(int year, int month, int day)
289 : {
290 : int julian;
291 : int century;
292 :
293 333772 : if (month > 2)
294 : {
295 181664 : month += 1;
296 181664 : year += 4800;
297 : }
298 : else
299 : {
300 152108 : month += 13;
301 152108 : year += 4799;
302 : }
303 :
304 333772 : century = year / 100;
305 333772 : julian = year * 365 - 32167;
306 333772 : julian += year / 4 - century + century / 4;
307 333772 : julian += 7834 * month / 256 + day;
308 :
309 333772 : return julian;
310 : } /* date2j() */
311 :
312 : void
313 271280 : j2date(int jd, int *year, int *month, int *day)
314 : {
315 : unsigned int julian;
316 : unsigned int quad;
317 : unsigned int extra;
318 : int y;
319 :
320 271280 : julian = jd;
321 271280 : julian += 32044;
322 271280 : quad = julian / 146097;
323 271280 : extra = (julian - quad * 146097) * 4 + 3;
324 271280 : julian += 60 + quad * 3 + extra / 146097;
325 271280 : quad = julian / 1461;
326 271280 : julian -= quad * 1461;
327 271280 : y = julian * 4 / 1461;
328 542560 : julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
329 271280 : + 123;
330 271280 : y += quad * 4;
331 271280 : *year = y - 4800;
332 271280 : quad = julian * 2141 / 65536;
333 271280 : *day = julian - 7834 * quad / 256;
334 271280 : *month = (quad + 10) % MONTHS_PER_YEAR + 1;
335 271280 : } /* j2date() */
336 :
337 :
338 : /*
339 : * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
340 : *
341 : * Note: various places use the locution j2day(date - 1) to produce a
342 : * result according to the convention 0..6 = Mon..Sun. This is a bit of
343 : * a crock, but will work as long as the computation here is just a modulo.
344 : */
345 : int
346 51120 : j2day(int date)
347 : {
348 51120 : date += 1;
349 51120 : date %= 7;
350 : /* Cope if division truncates towards zero, as it probably does */
351 51120 : if (date < 0)
352 0 : date += 7;
353 :
354 51120 : return date;
355 : } /* j2day() */
356 :
357 :
358 : /*
359 : * GetCurrentDateTime()
360 : *
361 : * Get the transaction start time ("now()") broken down as a struct pg_tm,
362 : * converted according to the session timezone setting.
363 : *
364 : * This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
365 : * case where caller doesn't need either fractional seconds or tz offset.
366 : */
367 : void
368 2492 : GetCurrentDateTime(struct pg_tm *tm)
369 : {
370 : fsec_t fsec;
371 :
372 2492 : GetCurrentTimeUsec(tm, &fsec, NULL);
373 2492 : }
374 :
375 : /*
376 : * GetCurrentTimeUsec()
377 : *
378 : * Get the transaction start time ("now()") broken down as a struct pg_tm,
379 : * including fractional seconds and timezone offset. The time is converted
380 : * according to the session timezone setting.
381 : *
382 : * Callers may pass tzp = NULL if they don't need the offset, but this does
383 : * not affect the conversion behavior (unlike timestamp2tm()).
384 : *
385 : * Internally, we cache the result, since this could be called many times
386 : * in a transaction, within which now() doesn't change.
387 : */
388 : void
389 2666 : GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
390 : {
391 2666 : TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
392 :
393 : /*
394 : * The cache key must include both current time and current timezone. By
395 : * representing the timezone by just a pointer, we're assuming that
396 : * distinct timezone settings could never have the same pointer value.
397 : * This is true by virtue of the hashtable used inside pg_tzset();
398 : * however, it might need another look if we ever allow entries in that
399 : * hash to be recycled.
400 : */
401 : static TimestampTz cache_ts = 0;
402 : static pg_tz *cache_timezone = NULL;
403 : static struct pg_tm cache_tm;
404 : static fsec_t cache_fsec;
405 : static int cache_tz;
406 :
407 2666 : if (cur_ts != cache_ts || session_timezone != cache_timezone)
408 : {
409 : /*
410 : * Make sure cache is marked invalid in case of error after partial
411 : * update within timestamp2tm.
412 : */
413 766 : cache_timezone = NULL;
414 :
415 : /*
416 : * Perform the computation, storing results into cache. We do not
417 : * really expect any error here, since current time surely ought to be
418 : * within range, but check just for sanity's sake.
419 : */
420 766 : if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
421 : NULL, session_timezone) != 0)
422 0 : ereport(ERROR,
423 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
424 : errmsg("timestamp out of range")));
425 :
426 : /* OK, so mark the cache valid. */
427 766 : cache_ts = cur_ts;
428 766 : cache_timezone = session_timezone;
429 : }
430 :
431 2666 : *tm = cache_tm;
432 2666 : *fsec = cache_fsec;
433 2666 : if (tzp != NULL)
434 162 : *tzp = cache_tz;
435 2666 : }
436 :
437 :
438 : /*
439 : * Append seconds and fractional seconds (if any) at *cp.
440 : *
441 : * precision is the max number of fraction digits, fillzeros says to
442 : * pad to two integral-seconds digits.
443 : *
444 : * Returns a pointer to the new end of string. No NUL terminator is put
445 : * there; callers are responsible for NUL terminating str themselves.
446 : *
447 : * Note that any sign is stripped from the input sec and fsec values.
448 : */
449 : static char *
450 135284 : AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
451 : {
452 : Assert(precision >= 0);
453 :
454 135284 : if (fillzeros)
455 131984 : cp = pg_ultostr_zeropad(cp, abs(sec), 2);
456 : else
457 3300 : cp = pg_ultostr(cp, abs(sec));
458 :
459 : /* fsec_t is just an int32 */
460 135284 : if (fsec != 0)
461 : {
462 21072 : int32 value = abs(fsec);
463 21072 : char *end = &cp[precision + 1];
464 21072 : bool gotnonzero = false;
465 :
466 21072 : *cp++ = '.';
467 :
468 : /*
469 : * Append the fractional seconds part. Note that we don't want any
470 : * trailing zeros here, so since we're building the number in reverse
471 : * we'll skip appending zeros until we've output a non-zero digit.
472 : */
473 147504 : while (precision--)
474 : {
475 126432 : int32 oldval = value;
476 : int32 remainder;
477 :
478 126432 : value /= 10;
479 126432 : remainder = oldval - value * 10;
480 :
481 : /* check if we got a non-zero */
482 126432 : if (remainder)
483 109106 : gotnonzero = true;
484 :
485 126432 : if (gotnonzero)
486 110324 : cp[precision] = '0' + remainder;
487 : else
488 16108 : end = &cp[precision];
489 : }
490 :
491 : /*
492 : * If we still have a non-zero value then precision must have not been
493 : * enough to print the number. We punt the problem to pg_ultostr(),
494 : * which will generate a correct answer in the minimum valid width.
495 : */
496 21072 : if (value)
497 0 : return pg_ultostr(cp, abs(fsec));
498 :
499 21072 : return end;
500 : }
501 : else
502 114212 : return cp;
503 : }
504 :
505 :
506 : /*
507 : * Variant of above that's specialized to timestamp case.
508 : *
509 : * Returns a pointer to the new end of string. No NUL terminator is put
510 : * there; callers are responsible for NUL terminating str themselves.
511 : */
512 : static char *
513 116020 : AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
514 : {
515 116020 : return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
516 : }
517 :
518 :
519 : /*
520 : * Add val * multiplier to *sum.
521 : * Returns true if successful, false on overflow.
522 : */
523 : static bool
524 8702 : int64_multiply_add(int64 val, int64 multiplier, int64 *sum)
525 : {
526 : int64 product;
527 :
528 17344 : if (pg_mul_s64_overflow(val, multiplier, &product) ||
529 8642 : pg_add_s64_overflow(*sum, product, sum))
530 156 : return false;
531 8546 : return true;
532 : }
533 :
534 : /*
535 : * Multiply frac by scale (to produce microseconds) and add to itm_in->tm_usec.
536 : * Returns true if successful, false if itm_in overflows.
537 : */
538 : static bool
539 9796 : AdjustFractMicroseconds(double frac, int64 scale,
540 : struct pg_itm_in *itm_in)
541 : {
542 : int64 usec;
543 :
544 : /* Fast path for common case */
545 9796 : if (frac == 0)
546 9334 : return true;
547 :
548 : /*
549 : * We assume the input frac has abs value less than 1, so overflow of frac
550 : * or usec is not an issue for interesting values of scale.
551 : */
552 462 : frac *= scale;
553 462 : usec = (int64) frac;
554 :
555 : /* Round off any fractional microsecond */
556 462 : frac -= usec;
557 462 : if (frac > 0.5)
558 24 : usec++;
559 438 : else if (frac < -0.5)
560 30 : usec--;
561 :
562 462 : return !pg_add_s64_overflow(itm_in->tm_usec, usec, &itm_in->tm_usec);
563 : }
564 :
565 : /*
566 : * Multiply frac by scale (to produce days). Add the integral part of the
567 : * result to itm_in->tm_mday, the fractional part to itm_in->tm_usec.
568 : * Returns true if successful, false if itm_in overflows.
569 : */
570 : static bool
571 1104 : AdjustFractDays(double frac, int scale,
572 : struct pg_itm_in *itm_in)
573 : {
574 : int extra_days;
575 :
576 : /* Fast path for common case */
577 1104 : if (frac == 0)
578 918 : return true;
579 :
580 : /*
581 : * We assume the input frac has abs value less than 1, so overflow of frac
582 : * or extra_days is not an issue.
583 : */
584 186 : frac *= scale;
585 186 : extra_days = (int) frac;
586 :
587 : /* ... but this could overflow, if tm_mday is already nonzero */
588 186 : if (pg_add_s32_overflow(itm_in->tm_mday, extra_days, &itm_in->tm_mday))
589 48 : return false;
590 :
591 : /* Handle any fractional day */
592 138 : frac -= extra_days;
593 138 : return AdjustFractMicroseconds(frac, USECS_PER_DAY, itm_in);
594 : }
595 :
596 : /*
597 : * Multiply frac by scale (to produce years), then further scale up to months.
598 : * Add the integral part of the result to itm_in->tm_mon, discarding any
599 : * fractional part.
600 : * Returns true if successful, false if itm_in overflows.
601 : */
602 : static bool
603 1220 : AdjustFractYears(double frac, int scale,
604 : struct pg_itm_in *itm_in)
605 : {
606 : /*
607 : * As above, we assume abs(frac) < 1, so this can't overflow for any
608 : * interesting value of scale.
609 : */
610 1220 : int extra_months = (int) rint(frac * scale * MONTHS_PER_YEAR);
611 :
612 1220 : return !pg_add_s32_overflow(itm_in->tm_mon, extra_months, &itm_in->tm_mon);
613 : }
614 :
615 : /*
616 : * Add (val + fval) * scale to itm_in->tm_usec.
617 : * Returns true if successful, false if itm_in overflows.
618 : */
619 : static bool
620 2768 : AdjustMicroseconds(int64 val, double fval, int64 scale,
621 : struct pg_itm_in *itm_in)
622 : {
623 : /* Handle the integer part */
624 2768 : if (!int64_multiply_add(val, scale, &itm_in->tm_usec))
625 150 : return false;
626 : /* Handle the float part */
627 2618 : return AdjustFractMicroseconds(fval, scale, itm_in);
628 : }
629 :
630 : /*
631 : * Multiply val by scale (to produce days) and add to itm_in->tm_mday.
632 : * Returns true if successful, false if itm_in overflows.
633 : */
634 : static bool
635 7298 : AdjustDays(int64 val, int scale, struct pg_itm_in *itm_in)
636 : {
637 : int days;
638 :
639 7298 : if (val < INT_MIN || val > INT_MAX)
640 36 : return false;
641 14512 : return !pg_mul_s32_overflow((int32) val, scale, &days) &&
642 7250 : !pg_add_s32_overflow(itm_in->tm_mday, days, &itm_in->tm_mday);
643 : }
644 :
645 : /*
646 : * Add val to itm_in->tm_mon (no need for scale here, as val is always
647 : * in months already).
648 : * Returns true if successful, false if itm_in overflows.
649 : */
650 : static bool
651 1074 : AdjustMonths(int64 val, struct pg_itm_in *itm_in)
652 : {
653 1074 : if (val < INT_MIN || val > INT_MAX)
654 12 : return false;
655 1062 : return !pg_add_s32_overflow(itm_in->tm_mon, (int32) val, &itm_in->tm_mon);
656 : }
657 :
658 : /*
659 : * Multiply val by scale (to produce years) and add to itm_in->tm_year.
660 : * Returns true if successful, false if itm_in overflows.
661 : */
662 : static bool
663 1358 : AdjustYears(int64 val, int scale,
664 : struct pg_itm_in *itm_in)
665 : {
666 : int years;
667 :
668 1358 : if (val < INT_MIN || val > INT_MAX)
669 24 : return false;
670 2632 : return !pg_mul_s32_overflow((int32) val, scale, &years) &&
671 1298 : !pg_add_s32_overflow(itm_in->tm_year, years, &itm_in->tm_year);
672 : }
673 :
674 :
675 : /*
676 : * Parse the fractional part of a number (decimal point and optional digits,
677 : * followed by end of string). Returns the fractional value into *frac.
678 : *
679 : * Returns 0 if successful, DTERR code if bogus input detected.
680 : */
681 : static int
682 23112 : ParseFraction(char *cp, double *frac)
683 : {
684 : /* Caller should always pass the start of the fraction part */
685 : Assert(*cp == '.');
686 :
687 : /*
688 : * We want to allow just "." with no digits, but some versions of strtod
689 : * will report EINVAL for that, so special-case it.
690 : */
691 23112 : if (cp[1] == '\0')
692 : {
693 0 : *frac = 0;
694 : }
695 : else
696 : {
697 23112 : errno = 0;
698 23112 : *frac = strtod(cp, &cp);
699 : /* check for parse failure */
700 23112 : if (*cp != '\0' || errno != 0)
701 12 : return DTERR_BAD_FORMAT;
702 : }
703 23100 : return 0;
704 : }
705 :
706 : /*
707 : * Fetch a fractional-second value with suitable error checking.
708 : * Same as ParseFraction except we convert the result to integer microseconds.
709 : */
710 : static int
711 22614 : ParseFractionalSecond(char *cp, fsec_t *fsec)
712 : {
713 : double frac;
714 : int dterr;
715 :
716 22614 : dterr = ParseFraction(cp, &frac);
717 22614 : if (dterr)
718 12 : return dterr;
719 22602 : *fsec = rint(frac * 1000000);
720 22602 : return 0;
721 : }
722 :
723 :
724 : /* ParseDateTime()
725 : * Break string into tokens based on a date/time context.
726 : * Returns 0 if successful, DTERR code if bogus input detected.
727 : *
728 : * timestr - the input string
729 : * workbuf - workspace for field string storage. This must be
730 : * larger than the largest legal input for this datetime type --
731 : * some additional space will be needed to NUL terminate fields.
732 : * buflen - the size of workbuf
733 : * field[] - pointers to field strings are returned in this array
734 : * ftype[] - field type indicators are returned in this array
735 : * maxfields - dimensions of the above two arrays
736 : * *numfields - set to the actual number of fields detected
737 : *
738 : * The fields extracted from the input are stored as separate,
739 : * null-terminated strings in the workspace at workbuf. Any text is
740 : * converted to lower case.
741 : *
742 : * Several field types are assigned:
743 : * DTK_NUMBER - digits and (possibly) a decimal point
744 : * DTK_DATE - digits and two delimiters, or digits and text
745 : * DTK_TIME - digits, colon delimiters, and possibly a decimal point
746 : * DTK_STRING - text (no digits or punctuation)
747 : * DTK_SPECIAL - leading "+" or "-" followed by text
748 : * DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
749 : *
750 : * Note that some field types can hold unexpected items:
751 : * DTK_NUMBER can hold date fields (yy.ddd)
752 : * DTK_STRING can hold months (January) and time zones (PST)
753 : * DTK_DATE can hold time zone names (America/New_York, GMT-8)
754 : */
755 : int
756 84206 : ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
757 : char **field, int *ftype, int maxfields, int *numfields)
758 : {
759 84206 : int nf = 0;
760 84206 : const char *cp = timestr;
761 84206 : char *bufp = workbuf;
762 84206 : const char *bufend = workbuf + buflen;
763 :
764 : /*
765 : * Set the character pointed-to by "bufptr" to "newchar", and increment
766 : * "bufptr". "end" gives the end of the buffer -- we return an error if
767 : * there is no space left to append a character to the buffer. Note that
768 : * "bufptr" is evaluated twice.
769 : */
770 : #define APPEND_CHAR(bufptr, end, newchar) \
771 : do \
772 : { \
773 : if (((bufptr) + 1) >= (end)) \
774 : return DTERR_BAD_FORMAT; \
775 : *(bufptr)++ = newchar; \
776 : } while (0)
777 :
778 : /* outer loop through fields */
779 381606 : while (*cp != '\0')
780 : {
781 : /* Ignore spaces between fields */
782 297400 : if (isspace((unsigned char) *cp))
783 : {
784 86744 : cp++;
785 86744 : continue;
786 : }
787 :
788 : /* Record start of current field */
789 210656 : if (nf >= maxfields)
790 0 : return DTERR_BAD_FORMAT;
791 210656 : field[nf] = bufp;
792 :
793 : /* leading digit? then date or time */
794 210656 : if (isdigit((unsigned char) *cp))
795 : {
796 143494 : APPEND_CHAR(bufp, bufend, *cp++);
797 423958 : while (isdigit((unsigned char) *cp))
798 280464 : APPEND_CHAR(bufp, bufend, *cp++);
799 :
800 : /* time field? */
801 143494 : if (*cp == ':')
802 : {
803 63546 : ftype[nf] = DTK_TIME;
804 63546 : APPEND_CHAR(bufp, bufend, *cp++);
805 533296 : while (isdigit((unsigned char) *cp) ||
806 148152 : (*cp == ':') || (*cp == '.'))
807 469750 : APPEND_CHAR(bufp, bufend, *cp++);
808 : }
809 : /* date field? allow embedded text month */
810 79948 : else if (*cp == '-' || *cp == '/' || *cp == '.')
811 64468 : {
812 : /* save delimiting character to use later */
813 64468 : char delim = *cp;
814 :
815 64468 : APPEND_CHAR(bufp, bufend, *cp++);
816 : /* second field is all digits? then no embedded text month */
817 64468 : if (isdigit((unsigned char) *cp))
818 : {
819 64372 : ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
820 193152 : while (isdigit((unsigned char) *cp))
821 128780 : APPEND_CHAR(bufp, bufend, *cp++);
822 :
823 : /*
824 : * insist that the delimiters match to get a three-field
825 : * date.
826 : */
827 64372 : if (*cp == delim)
828 : {
829 63790 : ftype[nf] = DTK_DATE;
830 63790 : APPEND_CHAR(bufp, bufend, *cp++);
831 194290 : while (isdigit((unsigned char) *cp) || *cp == delim)
832 130500 : APPEND_CHAR(bufp, bufend, *cp++);
833 : }
834 : }
835 : else
836 : {
837 96 : ftype[nf] = DTK_DATE;
838 756 : while (isalnum((unsigned char) *cp) || *cp == delim)
839 660 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
840 : }
841 : }
842 :
843 : /*
844 : * otherwise, number only and will determine year, month, day, or
845 : * concatenated fields later...
846 : */
847 : else
848 15480 : ftype[nf] = DTK_NUMBER;
849 : }
850 : /* Leading decimal point? Then fractional seconds... */
851 67162 : else if (*cp == '.')
852 : {
853 0 : APPEND_CHAR(bufp, bufend, *cp++);
854 0 : while (isdigit((unsigned char) *cp))
855 0 : APPEND_CHAR(bufp, bufend, *cp++);
856 :
857 0 : ftype[nf] = DTK_NUMBER;
858 : }
859 :
860 : /*
861 : * text? then date string, month, day of week, special, or timezone
862 : */
863 67162 : else if (isalpha((unsigned char) *cp))
864 : {
865 : bool is_date;
866 :
867 23972 : ftype[nf] = DTK_STRING;
868 23972 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
869 96050 : while (isalpha((unsigned char) *cp))
870 72078 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
871 :
872 : /*
873 : * Dates can have embedded '-', '/', or '.' separators. It could
874 : * also be a timezone name containing embedded '/', '+', '-', '_',
875 : * or ':' (but '_' or ':' can't be the first punctuation). If the
876 : * next character is a digit or '+', we need to check whether what
877 : * we have so far is a recognized non-timezone keyword --- if so,
878 : * don't believe that this is the start of a timezone.
879 : */
880 23972 : is_date = false;
881 23972 : if (*cp == '-' || *cp == '/' || *cp == '.')
882 1510 : is_date = true;
883 22462 : else if (*cp == '+' || isdigit((unsigned char) *cp))
884 : {
885 1812 : *bufp = '\0'; /* null-terminate current field value */
886 : /* we need search only the core token table, not TZ names */
887 1812 : if (datebsearch(field[nf], datetktbl, szdatetktbl) == NULL)
888 1506 : is_date = true;
889 : }
890 23972 : if (is_date)
891 : {
892 3016 : ftype[nf] = DTK_DATE;
893 : do
894 : {
895 14122 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
896 14122 : } while (*cp == '+' || *cp == '-' ||
897 13840 : *cp == '/' || *cp == '_' ||
898 13684 : *cp == '.' || *cp == ':' ||
899 27404 : isalnum((unsigned char) *cp));
900 : }
901 : }
902 : /* sign? then special or numeric timezone */
903 43190 : else if (*cp == '+' || *cp == '-')
904 : {
905 42638 : APPEND_CHAR(bufp, bufend, *cp++);
906 : /* soak up leading whitespace */
907 42662 : while (isspace((unsigned char) *cp))
908 24 : cp++;
909 : /* numeric timezone? */
910 : /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
911 42638 : if (isdigit((unsigned char) *cp))
912 : {
913 41796 : ftype[nf] = DTK_TZ;
914 41796 : APPEND_CHAR(bufp, bufend, *cp++);
915 97254 : while (isdigit((unsigned char) *cp) ||
916 43476 : *cp == ':' || *cp == '.' || *cp == '-')
917 55458 : APPEND_CHAR(bufp, bufend, *cp++);
918 : }
919 : /* special? */
920 842 : else if (isalpha((unsigned char) *cp))
921 : {
922 842 : ftype[nf] = DTK_SPECIAL;
923 842 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
924 6736 : while (isalpha((unsigned char) *cp))
925 5894 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
926 : }
927 : /* otherwise something wrong... */
928 : else
929 0 : return DTERR_BAD_FORMAT;
930 : }
931 : /* ignore other punctuation but use as delimiter */
932 552 : else if (ispunct((unsigned char) *cp))
933 : {
934 552 : cp++;
935 552 : continue;
936 : }
937 : /* otherwise, something is not right... */
938 : else
939 0 : return DTERR_BAD_FORMAT;
940 :
941 : /* force in a delimiter after each field */
942 210104 : *bufp++ = '\0';
943 210104 : nf++;
944 : }
945 :
946 84206 : *numfields = nf;
947 :
948 84206 : return 0;
949 : }
950 :
951 :
952 : /* DecodeDateTime()
953 : * Interpret previously parsed fields for general date and time.
954 : * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
955 : * (Currently, all callers treat 1 as an error return too.)
956 : *
957 : * Inputs are field[] and ftype[] arrays, of length nf.
958 : * Other arguments are outputs.
959 : *
960 : * External format(s):
961 : * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
962 : * "Fri Feb-7-1997 15:23:27"
963 : * "Feb-7-1997 15:23:27"
964 : * "2-7-1997 15:23:27"
965 : * "1997-2-7 15:23:27"
966 : * "1997.038 15:23:27" (day of year 1-366)
967 : * Also supports input in compact time:
968 : * "970207 152327"
969 : * "97038 152327"
970 : * "20011225T040506.789-07"
971 : *
972 : * Use the system-provided functions to get the current time zone
973 : * if not specified in the input string.
974 : *
975 : * If the date is outside the range of pg_time_t (in practice that could only
976 : * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
977 : * 1997-05-27
978 : */
979 : int
980 68476 : DecodeDateTime(char **field, int *ftype, int nf,
981 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
982 : DateTimeErrorExtra *extra)
983 : {
984 68476 : int fmask = 0,
985 : tmask,
986 : type;
987 68476 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
988 : int i;
989 : int val;
990 : int dterr;
991 68476 : int mer = HR24;
992 68476 : bool haveTextMonth = false;
993 68476 : bool isjulian = false;
994 68476 : bool is2digits = false;
995 68476 : bool bc = false;
996 68476 : pg_tz *namedTz = NULL;
997 68476 : pg_tz *abbrevTz = NULL;
998 : pg_tz *valtz;
999 68476 : char *abbrev = NULL;
1000 : struct pg_tm cur_tm;
1001 :
1002 : /*
1003 : * We'll insist on at least all of the date fields, but initialize the
1004 : * remaining fields in case they are not set later...
1005 : */
1006 68476 : *dtype = DTK_DATE;
1007 68476 : tm->tm_hour = 0;
1008 68476 : tm->tm_min = 0;
1009 68476 : tm->tm_sec = 0;
1010 68476 : *fsec = 0;
1011 : /* don't know daylight savings time status apriori */
1012 68476 : tm->tm_isdst = -1;
1013 68476 : if (tzp != NULL)
1014 68476 : *tzp = 0;
1015 :
1016 245534 : for (i = 0; i < nf; i++)
1017 : {
1018 177370 : switch (ftype[i])
1019 : {
1020 65152 : case DTK_DATE:
1021 :
1022 : /*
1023 : * Integral julian day with attached time zone? All other
1024 : * forms with JD will be separated into distinct fields, so we
1025 : * handle just this case here.
1026 : */
1027 65152 : if (ptype == DTK_JULIAN)
1028 : {
1029 : char *cp;
1030 : int jday;
1031 :
1032 6 : if (tzp == NULL)
1033 0 : return DTERR_BAD_FORMAT;
1034 :
1035 6 : errno = 0;
1036 6 : jday = strtoint(field[i], &cp, 10);
1037 6 : if (errno == ERANGE || jday < 0)
1038 0 : return DTERR_FIELD_OVERFLOW;
1039 :
1040 6 : j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1041 6 : isjulian = true;
1042 :
1043 : /* Get the time zone from the end of the string */
1044 6 : dterr = DecodeTimezone(cp, tzp);
1045 6 : if (dterr)
1046 0 : return dterr;
1047 :
1048 6 : tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1049 6 : ptype = 0;
1050 6 : break;
1051 : }
1052 :
1053 : /*
1054 : * Already have a date? Then this might be a time zone name
1055 : * with embedded punctuation (e.g. "America/New_York") or a
1056 : * run-together time with trailing time zone (e.g. hhmmss-zz).
1057 : * - thomas 2001-12-25
1058 : *
1059 : * We consider it a time zone if we already have month & day.
1060 : * This is to allow the form "mmm dd hhmmss tz year", which
1061 : * we've historically accepted.
1062 : */
1063 65146 : else if (ptype != 0 ||
1064 65134 : ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
1065 : (DTK_M(MONTH) | DTK_M(DAY))))
1066 : {
1067 : /* No time zone accepted? Then quit... */
1068 1410 : if (tzp == NULL)
1069 0 : return DTERR_BAD_FORMAT;
1070 :
1071 1410 : if (isdigit((unsigned char) *field[i]) || ptype != 0)
1072 18 : {
1073 : char *cp;
1074 :
1075 : /*
1076 : * Allow a preceding "t" field, but no other units.
1077 : */
1078 18 : if (ptype != 0)
1079 : {
1080 : /* Sanity check; should not fail this test */
1081 12 : if (ptype != DTK_TIME)
1082 0 : return DTERR_BAD_FORMAT;
1083 12 : ptype = 0;
1084 : }
1085 :
1086 : /*
1087 : * Starts with a digit but we already have a time
1088 : * field? Then we are in trouble with a date and time
1089 : * already...
1090 : */
1091 18 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1092 0 : return DTERR_BAD_FORMAT;
1093 :
1094 18 : if ((cp = strchr(field[i], '-')) == NULL)
1095 0 : return DTERR_BAD_FORMAT;
1096 :
1097 : /* Get the time zone from the end of the string */
1098 18 : dterr = DecodeTimezone(cp, tzp);
1099 18 : if (dterr)
1100 0 : return dterr;
1101 18 : *cp = '\0';
1102 :
1103 : /*
1104 : * Then read the rest of the field as a concatenated
1105 : * time
1106 : */
1107 18 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1108 : fmask,
1109 : &tmask, tm,
1110 : fsec, &is2digits);
1111 18 : if (dterr < 0)
1112 0 : return dterr;
1113 :
1114 : /*
1115 : * modify tmask after returning from
1116 : * DecodeNumberField()
1117 : */
1118 18 : tmask |= DTK_M(TZ);
1119 : }
1120 : else
1121 : {
1122 1392 : namedTz = pg_tzset(field[i]);
1123 1392 : if (!namedTz)
1124 : {
1125 36 : extra->dtee_timezone = field[i];
1126 36 : return DTERR_BAD_TIMEZONE;
1127 : }
1128 : /* we'll apply the zone setting below */
1129 1356 : tmask = DTK_M(TZ);
1130 : }
1131 : }
1132 : else
1133 : {
1134 63736 : dterr = DecodeDate(field[i], fmask,
1135 : &tmask, &is2digits, tm);
1136 63736 : if (dterr)
1137 36 : return dterr;
1138 : }
1139 65074 : break;
1140 :
1141 58366 : case DTK_TIME:
1142 :
1143 : /*
1144 : * This might be an ISO time following a "t" field.
1145 : */
1146 58366 : if (ptype != 0)
1147 : {
1148 : /* Sanity check; should not fail this test */
1149 12 : if (ptype != DTK_TIME)
1150 0 : return DTERR_BAD_FORMAT;
1151 12 : ptype = 0;
1152 : }
1153 58366 : dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
1154 : &tmask, tm, fsec);
1155 58366 : if (dterr)
1156 0 : return dterr;
1157 :
1158 : /* check for time overflow */
1159 58366 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec,
1160 : *fsec))
1161 0 : return DTERR_FIELD_OVERFLOW;
1162 58366 : break;
1163 :
1164 38528 : case DTK_TZ:
1165 : {
1166 : int tz;
1167 :
1168 38528 : if (tzp == NULL)
1169 12 : return DTERR_BAD_FORMAT;
1170 :
1171 38528 : dterr = DecodeTimezone(field[i], &tz);
1172 38528 : if (dterr)
1173 12 : return dterr;
1174 38516 : *tzp = tz;
1175 38516 : tmask = DTK_M(TZ);
1176 : }
1177 38516 : break;
1178 :
1179 6232 : case DTK_NUMBER:
1180 :
1181 : /*
1182 : * Deal with cases where previous field labeled this one
1183 : */
1184 6232 : if (ptype != 0)
1185 : {
1186 : char *cp;
1187 : int value;
1188 :
1189 132 : errno = 0;
1190 132 : value = strtoint(field[i], &cp, 10);
1191 132 : if (errno == ERANGE)
1192 12 : return DTERR_FIELD_OVERFLOW;
1193 132 : if (*cp != '.' && *cp != '\0')
1194 0 : return DTERR_BAD_FORMAT;
1195 :
1196 : switch (ptype)
1197 : {
1198 84 : case DTK_JULIAN:
1199 : /* previous field was a label for "julian date" */
1200 84 : if (value < 0)
1201 0 : return DTERR_FIELD_OVERFLOW;
1202 84 : tmask = DTK_DATE_M;
1203 84 : j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1204 84 : isjulian = true;
1205 :
1206 : /* fractional Julian Day? */
1207 84 : if (*cp == '.')
1208 : {
1209 : double time;
1210 :
1211 12 : dterr = ParseFraction(cp, &time);
1212 12 : if (dterr)
1213 0 : return dterr;
1214 12 : time *= USECS_PER_DAY;
1215 12 : dt2time(time,
1216 : &tm->tm_hour, &tm->tm_min,
1217 : &tm->tm_sec, fsec);
1218 12 : tmask |= DTK_TIME_M;
1219 : }
1220 84 : break;
1221 :
1222 36 : case DTK_TIME:
1223 : /* previous field was "t" for ISO time */
1224 36 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1225 : (fmask | DTK_DATE_M),
1226 : &tmask, tm,
1227 : fsec, &is2digits);
1228 36 : if (dterr < 0)
1229 0 : return dterr;
1230 36 : if (tmask != DTK_TIME_M)
1231 0 : return DTERR_BAD_FORMAT;
1232 36 : break;
1233 :
1234 12 : default:
1235 12 : return DTERR_BAD_FORMAT;
1236 : break;
1237 : }
1238 :
1239 120 : ptype = 0;
1240 120 : *dtype = DTK_DATE;
1241 : }
1242 : else
1243 : {
1244 : char *cp;
1245 : int flen;
1246 :
1247 6100 : flen = strlen(field[i]);
1248 6100 : cp = strchr(field[i], '.');
1249 :
1250 : /* Embedded decimal and no date yet? */
1251 6100 : if (cp != NULL && !(fmask & DTK_DATE_M))
1252 : {
1253 30 : dterr = DecodeDate(field[i], fmask,
1254 : &tmask, &is2digits, tm);
1255 30 : if (dterr)
1256 0 : return dterr;
1257 : }
1258 : /* embedded decimal and several digits before? */
1259 6070 : else if (cp != NULL && flen - strlen(cp) > 2)
1260 : {
1261 : /*
1262 : * Interpret as a concatenated date or time Set the
1263 : * type field to allow decoding other fields later.
1264 : * Example: 20011223 or 040506
1265 : */
1266 12 : dterr = DecodeNumberField(flen, field[i], fmask,
1267 : &tmask, tm,
1268 : fsec, &is2digits);
1269 12 : if (dterr < 0)
1270 0 : return dterr;
1271 : }
1272 :
1273 : /*
1274 : * Is this a YMD or HMS specification, or a year number?
1275 : * YMD and HMS are required to be six digits or more, so
1276 : * if it is 5 digits, it is a year. If it is six or more
1277 : * digits, we assume it is YMD or HMS unless no date and
1278 : * no time values have been specified. This forces 6+
1279 : * digit years to be at the end of the string, or to use
1280 : * the ISO date specification.
1281 : */
1282 6058 : else if (flen >= 6 && (!(fmask & DTK_DATE_M) ||
1283 96 : !(fmask & DTK_TIME_M)))
1284 : {
1285 338 : dterr = DecodeNumberField(flen, field[i], fmask,
1286 : &tmask, tm,
1287 : fsec, &is2digits);
1288 338 : if (dterr < 0)
1289 0 : return dterr;
1290 : }
1291 : /* otherwise it is a single date/time field... */
1292 : else
1293 : {
1294 5720 : dterr = DecodeNumber(flen, field[i],
1295 : haveTextMonth, fmask,
1296 : &tmask, tm,
1297 : fsec, &is2digits);
1298 5720 : if (dterr)
1299 12 : return dterr;
1300 : }
1301 : }
1302 6208 : break;
1303 :
1304 9092 : case DTK_STRING:
1305 : case DTK_SPECIAL:
1306 : /* timezone abbrevs take precedence over built-in tokens */
1307 9092 : dterr = DecodeTimezoneAbbrev(i, field[i],
1308 : &type, &val, &valtz, extra);
1309 9092 : if (dterr)
1310 0 : return dterr;
1311 9092 : if (type == UNKNOWN_FIELD)
1312 6700 : type = DecodeSpecial(i, field[i], &val);
1313 9092 : if (type == IGNORE_DTF)
1314 0 : continue;
1315 :
1316 9092 : tmask = DTK_M(type);
1317 9092 : switch (type)
1318 : {
1319 1618 : case RESERV:
1320 1618 : switch (val)
1321 : {
1322 114 : case DTK_NOW:
1323 114 : tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1324 114 : *dtype = DTK_DATE;
1325 114 : GetCurrentTimeUsec(tm, fsec, tzp);
1326 114 : break;
1327 :
1328 102 : case DTK_YESTERDAY:
1329 102 : tmask = DTK_DATE_M;
1330 102 : *dtype = DTK_DATE;
1331 102 : GetCurrentDateTime(&cur_tm);
1332 102 : j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
1333 : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1334 102 : break;
1335 :
1336 144 : case DTK_TODAY:
1337 144 : tmask = DTK_DATE_M;
1338 144 : *dtype = DTK_DATE;
1339 144 : GetCurrentDateTime(&cur_tm);
1340 144 : tm->tm_year = cur_tm.tm_year;
1341 144 : tm->tm_mon = cur_tm.tm_mon;
1342 144 : tm->tm_mday = cur_tm.tm_mday;
1343 144 : break;
1344 :
1345 150 : case DTK_TOMORROW:
1346 150 : tmask = DTK_DATE_M;
1347 150 : *dtype = DTK_DATE;
1348 150 : GetCurrentDateTime(&cur_tm);
1349 150 : j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
1350 : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1351 150 : break;
1352 :
1353 6 : case DTK_ZULU:
1354 6 : tmask = (DTK_TIME_M | DTK_M(TZ));
1355 6 : *dtype = DTK_DATE;
1356 6 : tm->tm_hour = 0;
1357 6 : tm->tm_min = 0;
1358 6 : tm->tm_sec = 0;
1359 6 : if (tzp != NULL)
1360 6 : *tzp = 0;
1361 6 : break;
1362 :
1363 1102 : case DTK_EPOCH:
1364 : case DTK_LATE:
1365 : case DTK_EARLY:
1366 1102 : tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1367 1102 : *dtype = val;
1368 : /* caller ignores tm for these dtype codes */
1369 1102 : break;
1370 :
1371 0 : default:
1372 0 : elog(ERROR, "unrecognized RESERV datetime token: %d",
1373 : val);
1374 : }
1375 :
1376 1618 : break;
1377 :
1378 2620 : case MONTH:
1379 :
1380 : /*
1381 : * already have a (numeric) month? then see if we can
1382 : * substitute...
1383 : */
1384 2620 : if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
1385 92 : !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 &&
1386 80 : tm->tm_mon <= 31)
1387 : {
1388 74 : tm->tm_mday = tm->tm_mon;
1389 74 : tmask = DTK_M(DAY);
1390 : }
1391 2620 : haveTextMonth = true;
1392 2620 : tm->tm_mon = val;
1393 2620 : break;
1394 :
1395 6 : case DTZMOD:
1396 :
1397 : /*
1398 : * daylight savings time modifier (solves "MET DST"
1399 : * syntax)
1400 : */
1401 6 : tmask |= DTK_M(DTZ);
1402 6 : tm->tm_isdst = 1;
1403 6 : if (tzp == NULL)
1404 0 : return DTERR_BAD_FORMAT;
1405 6 : *tzp -= val;
1406 6 : break;
1407 :
1408 44 : case DTZ:
1409 :
1410 : /*
1411 : * set mask for TZ here _or_ check for DTZ later when
1412 : * getting default timezone
1413 : */
1414 44 : tmask |= DTK_M(TZ);
1415 44 : tm->tm_isdst = 1;
1416 44 : if (tzp == NULL)
1417 0 : return DTERR_BAD_FORMAT;
1418 44 : *tzp = -val;
1419 44 : break;
1420 :
1421 2264 : case TZ:
1422 2264 : tm->tm_isdst = 0;
1423 2264 : if (tzp == NULL)
1424 0 : return DTERR_BAD_FORMAT;
1425 2264 : *tzp = -val;
1426 2264 : break;
1427 :
1428 84 : case DYNTZ:
1429 84 : tmask |= DTK_M(TZ);
1430 84 : if (tzp == NULL)
1431 0 : return DTERR_BAD_FORMAT;
1432 : /* we'll determine the actual offset later */
1433 84 : abbrevTz = valtz;
1434 84 : abbrev = field[i];
1435 84 : break;
1436 :
1437 24 : case AMPM:
1438 24 : mer = val;
1439 24 : break;
1440 :
1441 270 : case ADBC:
1442 270 : bc = (val == BC);
1443 270 : break;
1444 :
1445 1928 : case DOW:
1446 1928 : tm->tm_wday = val;
1447 1928 : break;
1448 :
1449 126 : case UNITS:
1450 126 : tmask = 0;
1451 : /* reject consecutive unhandled units */
1452 126 : if (ptype != 0)
1453 12 : return DTERR_BAD_FORMAT;
1454 114 : ptype = val;
1455 114 : break;
1456 :
1457 60 : case ISOTIME:
1458 :
1459 : /*
1460 : * This is a filler field "t" indicating that the next
1461 : * field is time. Try to verify that this is sensible.
1462 : */
1463 60 : tmask = 0;
1464 :
1465 : /* No preceding date? Then quit... */
1466 60 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1467 0 : return DTERR_BAD_FORMAT;
1468 :
1469 : /* reject consecutive unhandled units */
1470 60 : if (ptype != 0)
1471 0 : return DTERR_BAD_FORMAT;
1472 60 : ptype = val;
1473 60 : break;
1474 :
1475 48 : case UNKNOWN_FIELD:
1476 :
1477 : /*
1478 : * Before giving up and declaring error, check to see
1479 : * if it is an all-alpha timezone name.
1480 : */
1481 48 : namedTz = pg_tzset(field[i]);
1482 48 : if (!namedTz)
1483 48 : return DTERR_BAD_FORMAT;
1484 : /* we'll apply the zone setting below */
1485 0 : tmask = DTK_M(TZ);
1486 0 : break;
1487 :
1488 0 : default:
1489 0 : return DTERR_BAD_FORMAT;
1490 : }
1491 9032 : break;
1492 :
1493 0 : default:
1494 0 : return DTERR_BAD_FORMAT;
1495 : }
1496 :
1497 177202 : if (tmask & fmask)
1498 144 : return DTERR_BAD_FORMAT;
1499 177058 : fmask |= tmask;
1500 : } /* end loop over fields */
1501 :
1502 : /* reject if prefix type appeared and was never handled */
1503 68164 : if (ptype != 0)
1504 0 : return DTERR_BAD_FORMAT;
1505 :
1506 : /* do additional checking for normal date specs (but not "infinity" etc) */
1507 68164 : if (*dtype == DTK_DATE)
1508 : {
1509 : /* do final checking/adjustment of Y/M/D fields */
1510 67206 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
1511 67206 : if (dterr)
1512 198 : return dterr;
1513 :
1514 : /* handle AM/PM */
1515 67008 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
1516 0 : return DTERR_FIELD_OVERFLOW;
1517 67008 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
1518 0 : tm->tm_hour = 0;
1519 67008 : else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
1520 24 : tm->tm_hour += HOURS_PER_DAY / 2;
1521 :
1522 : /* check for incomplete input */
1523 67008 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1524 : {
1525 6 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1526 0 : return 1;
1527 6 : return DTERR_BAD_FORMAT;
1528 : }
1529 :
1530 : /*
1531 : * If we had a full timezone spec, compute the offset (we could not do
1532 : * it before, because we need the date to resolve DST status).
1533 : */
1534 67002 : if (namedTz != NULL)
1535 : {
1536 : /* daylight savings time modifier disallowed with full TZ */
1537 1356 : if (fmask & DTK_M(DTZMOD))
1538 0 : return DTERR_BAD_FORMAT;
1539 :
1540 1356 : *tzp = DetermineTimeZoneOffset(tm, namedTz);
1541 : }
1542 :
1543 : /*
1544 : * Likewise, if we had a dynamic timezone abbreviation, resolve it
1545 : * now.
1546 : */
1547 67002 : if (abbrevTz != NULL)
1548 : {
1549 : /* daylight savings time modifier disallowed with dynamic TZ */
1550 84 : if (fmask & DTK_M(DTZMOD))
1551 0 : return DTERR_BAD_FORMAT;
1552 :
1553 84 : *tzp = DetermineTimeZoneAbbrevOffset(tm, abbrev, abbrevTz);
1554 : }
1555 :
1556 : /* timezone not specified? then use session timezone */
1557 67002 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
1558 : {
1559 : /*
1560 : * daylight savings time modifier but no standard timezone? then
1561 : * error
1562 : */
1563 24618 : if (fmask & DTK_M(DTZMOD))
1564 0 : return DTERR_BAD_FORMAT;
1565 :
1566 24618 : *tzp = DetermineTimeZoneOffset(tm, session_timezone);
1567 : }
1568 : }
1569 :
1570 67960 : return 0;
1571 : }
1572 :
1573 :
1574 : /* DetermineTimeZoneOffset()
1575 : *
1576 : * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min,
1577 : * and tm_sec fields are set, and a zic-style time zone definition, determine
1578 : * the applicable GMT offset and daylight-savings status at that time.
1579 : * Set the struct pg_tm's tm_isdst field accordingly, and return the GMT
1580 : * offset as the function result.
1581 : *
1582 : * Note: if the date is out of the range we can deal with, we return zero
1583 : * as the GMT offset and set tm_isdst = 0. We don't throw an error here,
1584 : * though probably some higher-level code will.
1585 : */
1586 : int
1587 52606 : DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
1588 : {
1589 : pg_time_t t;
1590 :
1591 52606 : return DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1592 : }
1593 :
1594 :
1595 : /* DetermineTimeZoneOffsetInternal()
1596 : *
1597 : * As above, but also return the actual UTC time imputed to the date/time
1598 : * into *tp.
1599 : *
1600 : * In event of an out-of-range date, we punt by returning zero into *tp.
1601 : * This is okay for the immediate callers but is a good reason for not
1602 : * exposing this worker function globally.
1603 : *
1604 : * Note: it might seem that we should use mktime() for this, but bitter
1605 : * experience teaches otherwise. This code is much faster than most versions
1606 : * of mktime(), anyway.
1607 : */
1608 : static int
1609 52786 : DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, pg_time_t *tp)
1610 : {
1611 : int date,
1612 : sec;
1613 : pg_time_t day,
1614 : mytime,
1615 : prevtime,
1616 : boundary,
1617 : beforetime,
1618 : aftertime;
1619 : long int before_gmtoff,
1620 : after_gmtoff;
1621 : int before_isdst,
1622 : after_isdst;
1623 : int res;
1624 :
1625 : /*
1626 : * First, generate the pg_time_t value corresponding to the given
1627 : * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the
1628 : * timezone is GMT. (For a valid Julian date, integer overflow should be
1629 : * impossible with 64-bit pg_time_t, but let's check for safety.)
1630 : */
1631 52786 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1632 24 : goto overflow;
1633 52762 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
1634 :
1635 52762 : day = ((pg_time_t) date) * SECS_PER_DAY;
1636 52762 : if (day / SECS_PER_DAY != date)
1637 0 : goto overflow;
1638 52762 : sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
1639 52762 : mytime = day + sec;
1640 : /* since sec >= 0, overflow could only be from +day to -mytime */
1641 52762 : if (mytime < 0 && day > 0)
1642 0 : goto overflow;
1643 :
1644 : /*
1645 : * Find the DST time boundary just before or following the target time. We
1646 : * assume that all zones have GMT offsets less than 24 hours, and that DST
1647 : * boundaries can't be closer together than 48 hours, so backing up 24
1648 : * hours and finding the "next" boundary will work.
1649 : */
1650 52762 : prevtime = mytime - SECS_PER_DAY;
1651 52762 : if (mytime < 0 && prevtime > 0)
1652 0 : goto overflow;
1653 :
1654 52762 : res = pg_next_dst_boundary(&prevtime,
1655 : &before_gmtoff, &before_isdst,
1656 : &boundary,
1657 : &after_gmtoff, &after_isdst,
1658 : tzp);
1659 52762 : if (res < 0)
1660 0 : goto overflow; /* failure? */
1661 :
1662 52762 : if (res == 0)
1663 : {
1664 : /* Non-DST zone, life is simple */
1665 2730 : tm->tm_isdst = before_isdst;
1666 2730 : *tp = mytime - before_gmtoff;
1667 2730 : return -(int) before_gmtoff;
1668 : }
1669 :
1670 : /*
1671 : * Form the candidate pg_time_t values with local-time adjustment
1672 : */
1673 50032 : beforetime = mytime - before_gmtoff;
1674 50032 : if ((before_gmtoff > 0 &&
1675 12 : mytime < 0 && beforetime > 0) ||
1676 50032 : (before_gmtoff <= 0 &&
1677 37242 : mytime > 0 && beforetime < 0))
1678 0 : goto overflow;
1679 50032 : aftertime = mytime - after_gmtoff;
1680 50032 : if ((after_gmtoff > 0 &&
1681 12 : mytime < 0 && aftertime > 0) ||
1682 50032 : (after_gmtoff <= 0 &&
1683 37242 : mytime > 0 && aftertime < 0))
1684 0 : goto overflow;
1685 :
1686 : /*
1687 : * If both before or both after the boundary time, we know what to do. The
1688 : * boundary time itself is considered to be after the transition, which
1689 : * means we can accept aftertime == boundary in the second case.
1690 : */
1691 50032 : if (beforetime < boundary && aftertime < boundary)
1692 : {
1693 49454 : tm->tm_isdst = before_isdst;
1694 49454 : *tp = beforetime;
1695 49454 : return -(int) before_gmtoff;
1696 : }
1697 578 : if (beforetime > boundary && aftertime >= boundary)
1698 : {
1699 428 : tm->tm_isdst = after_isdst;
1700 428 : *tp = aftertime;
1701 428 : return -(int) after_gmtoff;
1702 : }
1703 :
1704 : /*
1705 : * It's an invalid or ambiguous time due to timezone transition. In a
1706 : * spring-forward transition, prefer the "before" interpretation; in a
1707 : * fall-back transition, prefer "after". (We used to define and implement
1708 : * this test as "prefer the standard-time interpretation", but that rule
1709 : * does not help to resolve the behavior when both times are reported as
1710 : * standard time; which does happen, eg Europe/Moscow in Oct 2014. Also,
1711 : * in some zones such as Europe/Dublin, there is widespread confusion
1712 : * about which time offset is "standard" time, so it's fortunate that our
1713 : * behavior doesn't depend on that.)
1714 : */
1715 150 : if (beforetime > aftertime)
1716 : {
1717 72 : tm->tm_isdst = before_isdst;
1718 72 : *tp = beforetime;
1719 72 : return -(int) before_gmtoff;
1720 : }
1721 78 : tm->tm_isdst = after_isdst;
1722 78 : *tp = aftertime;
1723 78 : return -(int) after_gmtoff;
1724 :
1725 24 : overflow:
1726 : /* Given date is out of range, so assume UTC */
1727 24 : tm->tm_isdst = 0;
1728 24 : *tp = 0;
1729 24 : return 0;
1730 : }
1731 :
1732 :
1733 : /* DetermineTimeZoneAbbrevOffset()
1734 : *
1735 : * Determine the GMT offset and DST flag to be attributed to a dynamic
1736 : * time zone abbreviation, that is one whose meaning has changed over time.
1737 : * *tm contains the local time at which the meaning should be determined,
1738 : * and tm->tm_isdst receives the DST flag.
1739 : *
1740 : * This differs from the behavior of DetermineTimeZoneOffset() in that a
1741 : * standard-time or daylight-time abbreviation forces use of the corresponding
1742 : * GMT offset even when the zone was then in DS or standard time respectively.
1743 : * (However, that happens only if we can match the given abbreviation to some
1744 : * abbreviation that appears in the IANA timezone data. Otherwise, we fall
1745 : * back to doing DetermineTimeZoneOffset().)
1746 : */
1747 : int
1748 180 : DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp)
1749 : {
1750 : pg_time_t t;
1751 : int zone_offset;
1752 : int abbr_offset;
1753 : int abbr_isdst;
1754 :
1755 : /*
1756 : * Compute the UTC time we want to probe at. (In event of overflow, we'll
1757 : * probe at the epoch, which is a bit random but probably doesn't matter.)
1758 : */
1759 180 : zone_offset = DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1760 :
1761 : /*
1762 : * Try to match the abbreviation to something in the zone definition.
1763 : */
1764 180 : if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1765 : &abbr_offset, &abbr_isdst))
1766 : {
1767 : /* Success, so use the abbrev-specific answers. */
1768 180 : tm->tm_isdst = abbr_isdst;
1769 180 : return abbr_offset;
1770 : }
1771 :
1772 : /*
1773 : * No match, so use the answers we already got from
1774 : * DetermineTimeZoneOffsetInternal.
1775 : */
1776 0 : return zone_offset;
1777 : }
1778 :
1779 :
1780 : /* DetermineTimeZoneAbbrevOffsetTS()
1781 : *
1782 : * As above but the probe time is specified as a TimestampTz (hence, UTC time),
1783 : * and DST status is returned into *isdst rather than into tm->tm_isdst.
1784 : */
1785 : int
1786 966 : DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1787 : pg_tz *tzp, int *isdst)
1788 : {
1789 966 : pg_time_t t = timestamptz_to_time_t(ts);
1790 : int zone_offset;
1791 : int abbr_offset;
1792 : int tz;
1793 : struct pg_tm tm;
1794 : fsec_t fsec;
1795 :
1796 : /*
1797 : * If the abbrev matches anything in the zone data, this is pretty easy.
1798 : */
1799 966 : if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1800 : &abbr_offset, isdst))
1801 90 : return abbr_offset;
1802 :
1803 : /*
1804 : * Else, break down the timestamp so we can use DetermineTimeZoneOffset.
1805 : */
1806 876 : if (timestamp2tm(ts, &tz, &tm, &fsec, NULL, tzp) != 0)
1807 0 : ereport(ERROR,
1808 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1809 : errmsg("timestamp out of range")));
1810 :
1811 876 : zone_offset = DetermineTimeZoneOffset(&tm, tzp);
1812 876 : *isdst = tm.tm_isdst;
1813 876 : return zone_offset;
1814 : }
1815 :
1816 :
1817 : /* DetermineTimeZoneAbbrevOffsetInternal()
1818 : *
1819 : * Workhorse for above two functions: work from a pg_time_t probe instant.
1820 : * On success, return GMT offset and DST status into *offset and *isdst.
1821 : */
1822 : static bool
1823 1146 : DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1824 : int *offset, int *isdst)
1825 : {
1826 : char upabbr[TZ_STRLEN_MAX + 1];
1827 : unsigned char *p;
1828 : long int gmtoff;
1829 :
1830 : /* We need to force the abbrev to upper case */
1831 1146 : strlcpy(upabbr, abbr, sizeof(upabbr));
1832 5352 : for (p = (unsigned char *) upabbr; *p; p++)
1833 4206 : *p = pg_toupper(*p);
1834 :
1835 : /* Look up the abbrev's meaning at this time in this zone */
1836 1146 : if (pg_interpret_timezone_abbrev(upabbr,
1837 : &t,
1838 : &gmtoff,
1839 : isdst,
1840 : tzp))
1841 : {
1842 : /* Change sign to agree with DetermineTimeZoneOffset() */
1843 270 : *offset = (int) -gmtoff;
1844 270 : return true;
1845 : }
1846 876 : return false;
1847 : }
1848 :
1849 :
1850 : /* DecodeTimeOnly()
1851 : * Interpret parsed string as time fields only.
1852 : * Returns 0 if successful, DTERR code if bogus input detected.
1853 : *
1854 : * Inputs are field[] and ftype[] arrays, of length nf.
1855 : * Other arguments are outputs.
1856 : *
1857 : * Note that support for time zone is here for
1858 : * SQL TIME WITH TIME ZONE, but it reveals
1859 : * bogosity with SQL date/time standards, since
1860 : * we must infer a time zone from current time.
1861 : * - thomas 2000-03-10
1862 : * Allow specifying date to get a better time zone,
1863 : * if time zones are allowed. - thomas 2001-12-26
1864 : */
1865 : int
1866 4042 : DecodeTimeOnly(char **field, int *ftype, int nf,
1867 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1868 : DateTimeErrorExtra *extra)
1869 : {
1870 4042 : int fmask = 0,
1871 : tmask,
1872 : type;
1873 4042 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
1874 : int i;
1875 : int val;
1876 : int dterr;
1877 4042 : bool isjulian = false;
1878 4042 : bool is2digits = false;
1879 4042 : bool bc = false;
1880 4042 : int mer = HR24;
1881 4042 : pg_tz *namedTz = NULL;
1882 4042 : pg_tz *abbrevTz = NULL;
1883 4042 : char *abbrev = NULL;
1884 : pg_tz *valtz;
1885 :
1886 4042 : *dtype = DTK_TIME;
1887 4042 : tm->tm_hour = 0;
1888 4042 : tm->tm_min = 0;
1889 4042 : tm->tm_sec = 0;
1890 4042 : *fsec = 0;
1891 : /* don't know daylight savings time status apriori */
1892 4042 : tm->tm_isdst = -1;
1893 :
1894 4042 : if (tzp != NULL)
1895 4042 : *tzp = 0;
1896 :
1897 10368 : for (i = 0; i < nf; i++)
1898 : {
1899 6338 : switch (ftype[i])
1900 : {
1901 1390 : case DTK_DATE:
1902 :
1903 : /*
1904 : * Time zone not allowed? Then should not accept dates or time
1905 : * zones no matter what else!
1906 : */
1907 1390 : if (tzp == NULL)
1908 0 : return DTERR_BAD_FORMAT;
1909 :
1910 : /* Under limited circumstances, we will accept a date... */
1911 1390 : if (i == 0 && nf >= 2 &&
1912 198 : (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
1913 : {
1914 198 : dterr = DecodeDate(field[i], fmask,
1915 : &tmask, &is2digits, tm);
1916 198 : if (dterr)
1917 0 : return dterr;
1918 : }
1919 : /* otherwise, this is a time and/or time zone */
1920 : else
1921 : {
1922 1192 : if (isdigit((unsigned char) *field[i]))
1923 : {
1924 : char *cp;
1925 :
1926 : /*
1927 : * Starts with a digit but we already have a time
1928 : * field? Then we are in trouble with time already...
1929 : */
1930 0 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1931 0 : return DTERR_BAD_FORMAT;
1932 :
1933 : /*
1934 : * Should not get here and fail. Sanity check only...
1935 : */
1936 0 : if ((cp = strchr(field[i], '-')) == NULL)
1937 0 : return DTERR_BAD_FORMAT;
1938 :
1939 : /* Get the time zone from the end of the string */
1940 0 : dterr = DecodeTimezone(cp, tzp);
1941 0 : if (dterr)
1942 0 : return dterr;
1943 0 : *cp = '\0';
1944 :
1945 : /*
1946 : * Then read the rest of the field as a concatenated
1947 : * time
1948 : */
1949 0 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1950 : (fmask | DTK_DATE_M),
1951 : &tmask, tm,
1952 : fsec, &is2digits);
1953 0 : if (dterr < 0)
1954 0 : return dterr;
1955 0 : ftype[i] = dterr;
1956 :
1957 0 : tmask |= DTK_M(TZ);
1958 : }
1959 : else
1960 : {
1961 1192 : namedTz = pg_tzset(field[i]);
1962 1192 : if (!namedTz)
1963 : {
1964 0 : extra->dtee_timezone = field[i];
1965 0 : return DTERR_BAD_TIMEZONE;
1966 : }
1967 : /* we'll apply the zone setting below */
1968 1192 : ftype[i] = DTK_TZ;
1969 1192 : tmask = DTK_M(TZ);
1970 : }
1971 : }
1972 1390 : break;
1973 :
1974 3952 : case DTK_TIME:
1975 3952 : dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
1976 : INTERVAL_FULL_RANGE,
1977 : &tmask, tm, fsec);
1978 3952 : if (dterr)
1979 0 : return dterr;
1980 3952 : break;
1981 :
1982 540 : case DTK_TZ:
1983 : {
1984 : int tz;
1985 :
1986 540 : if (tzp == NULL)
1987 0 : return DTERR_BAD_FORMAT;
1988 :
1989 540 : dterr = DecodeTimezone(field[i], &tz);
1990 540 : if (dterr)
1991 0 : return dterr;
1992 540 : *tzp = tz;
1993 540 : tmask = DTK_M(TZ);
1994 : }
1995 540 : break;
1996 :
1997 96 : case DTK_NUMBER:
1998 :
1999 : /*
2000 : * Deal with cases where previous field labeled this one
2001 : */
2002 96 : if (ptype != 0)
2003 : {
2004 : char *cp;
2005 : int value;
2006 :
2007 72 : errno = 0;
2008 72 : value = strtoint(field[i], &cp, 10);
2009 72 : if (errno == ERANGE)
2010 12 : return DTERR_FIELD_OVERFLOW;
2011 72 : if (*cp != '.' && *cp != '\0')
2012 0 : return DTERR_BAD_FORMAT;
2013 :
2014 : switch (ptype)
2015 : {
2016 6 : case DTK_JULIAN:
2017 : /* previous field was a label for "julian date" */
2018 6 : if (tzp == NULL)
2019 0 : return DTERR_BAD_FORMAT;
2020 6 : if (value < 0)
2021 0 : return DTERR_FIELD_OVERFLOW;
2022 6 : tmask = DTK_DATE_M;
2023 6 : j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2024 6 : isjulian = true;
2025 :
2026 6 : if (*cp == '.')
2027 : {
2028 : double time;
2029 :
2030 0 : dterr = ParseFraction(cp, &time);
2031 0 : if (dterr)
2032 0 : return dterr;
2033 0 : time *= USECS_PER_DAY;
2034 0 : dt2time(time,
2035 : &tm->tm_hour, &tm->tm_min,
2036 : &tm->tm_sec, fsec);
2037 0 : tmask |= DTK_TIME_M;
2038 : }
2039 6 : break;
2040 :
2041 54 : case DTK_TIME:
2042 : /* previous field was "t" for ISO time */
2043 54 : dterr = DecodeNumberField(strlen(field[i]), field[i],
2044 : (fmask | DTK_DATE_M),
2045 : &tmask, tm,
2046 : fsec, &is2digits);
2047 54 : if (dterr < 0)
2048 0 : return dterr;
2049 54 : ftype[i] = dterr;
2050 :
2051 54 : if (tmask != DTK_TIME_M)
2052 0 : return DTERR_BAD_FORMAT;
2053 54 : break;
2054 :
2055 12 : default:
2056 12 : return DTERR_BAD_FORMAT;
2057 : break;
2058 : }
2059 :
2060 60 : ptype = 0;
2061 60 : *dtype = DTK_DATE;
2062 : }
2063 : else
2064 : {
2065 : char *cp;
2066 : int flen;
2067 :
2068 24 : flen = strlen(field[i]);
2069 24 : cp = strchr(field[i], '.');
2070 :
2071 : /* Embedded decimal? */
2072 24 : if (cp != NULL)
2073 : {
2074 : /*
2075 : * Under limited circumstances, we will accept a
2076 : * date...
2077 : */
2078 24 : if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
2079 : {
2080 0 : dterr = DecodeDate(field[i], fmask,
2081 : &tmask, &is2digits, tm);
2082 0 : if (dterr)
2083 0 : return dterr;
2084 : }
2085 : /* embedded decimal and several digits before? */
2086 24 : else if (flen - strlen(cp) > 2)
2087 : {
2088 : /*
2089 : * Interpret as a concatenated date or time Set
2090 : * the type field to allow decoding other fields
2091 : * later. Example: 20011223 or 040506
2092 : */
2093 24 : dterr = DecodeNumberField(flen, field[i],
2094 : (fmask | DTK_DATE_M),
2095 : &tmask, tm,
2096 : fsec, &is2digits);
2097 24 : if (dterr < 0)
2098 0 : return dterr;
2099 24 : ftype[i] = dterr;
2100 : }
2101 : else
2102 0 : return DTERR_BAD_FORMAT;
2103 : }
2104 0 : else if (flen > 4)
2105 : {
2106 0 : dterr = DecodeNumberField(flen, field[i],
2107 : (fmask | DTK_DATE_M),
2108 : &tmask, tm,
2109 : fsec, &is2digits);
2110 0 : if (dterr < 0)
2111 0 : return dterr;
2112 0 : ftype[i] = dterr;
2113 : }
2114 : /* otherwise it is a single date/time field... */
2115 : else
2116 : {
2117 0 : dterr = DecodeNumber(flen, field[i],
2118 : false,
2119 : (fmask | DTK_DATE_M),
2120 : &tmask, tm,
2121 : fsec, &is2digits);
2122 0 : if (dterr)
2123 0 : return dterr;
2124 : }
2125 : }
2126 84 : break;
2127 :
2128 360 : case DTK_STRING:
2129 : case DTK_SPECIAL:
2130 : /* timezone abbrevs take precedence over built-in tokens */
2131 360 : dterr = DecodeTimezoneAbbrev(i, field[i],
2132 : &type, &val, &valtz, extra);
2133 360 : if (dterr)
2134 0 : return dterr;
2135 360 : if (type == UNKNOWN_FIELD)
2136 96 : type = DecodeSpecial(i, field[i], &val);
2137 360 : if (type == IGNORE_DTF)
2138 0 : continue;
2139 :
2140 360 : tmask = DTK_M(type);
2141 360 : switch (type)
2142 : {
2143 12 : case RESERV:
2144 12 : switch (val)
2145 : {
2146 12 : case DTK_NOW:
2147 12 : tmask = DTK_TIME_M;
2148 12 : *dtype = DTK_TIME;
2149 12 : GetCurrentTimeUsec(tm, fsec, NULL);
2150 12 : break;
2151 :
2152 0 : case DTK_ZULU:
2153 0 : tmask = (DTK_TIME_M | DTK_M(TZ));
2154 0 : *dtype = DTK_TIME;
2155 0 : tm->tm_hour = 0;
2156 0 : tm->tm_min = 0;
2157 0 : tm->tm_sec = 0;
2158 0 : tm->tm_isdst = 0;
2159 0 : break;
2160 :
2161 0 : default:
2162 0 : return DTERR_BAD_FORMAT;
2163 : }
2164 :
2165 12 : break;
2166 :
2167 0 : case DTZMOD:
2168 :
2169 : /*
2170 : * daylight savings time modifier (solves "MET DST"
2171 : * syntax)
2172 : */
2173 0 : tmask |= DTK_M(DTZ);
2174 0 : tm->tm_isdst = 1;
2175 0 : if (tzp == NULL)
2176 0 : return DTERR_BAD_FORMAT;
2177 0 : *tzp -= val;
2178 0 : break;
2179 :
2180 198 : case DTZ:
2181 :
2182 : /*
2183 : * set mask for TZ here _or_ check for DTZ later when
2184 : * getting default timezone
2185 : */
2186 198 : tmask |= DTK_M(TZ);
2187 198 : tm->tm_isdst = 1;
2188 198 : if (tzp == NULL)
2189 0 : return DTERR_BAD_FORMAT;
2190 198 : *tzp = -val;
2191 198 : ftype[i] = DTK_TZ;
2192 198 : break;
2193 :
2194 60 : case TZ:
2195 60 : tm->tm_isdst = 0;
2196 60 : if (tzp == NULL)
2197 0 : return DTERR_BAD_FORMAT;
2198 60 : *tzp = -val;
2199 60 : ftype[i] = DTK_TZ;
2200 60 : break;
2201 :
2202 6 : case DYNTZ:
2203 6 : tmask |= DTK_M(TZ);
2204 6 : if (tzp == NULL)
2205 0 : return DTERR_BAD_FORMAT;
2206 : /* we'll determine the actual offset later */
2207 6 : abbrevTz = valtz;
2208 6 : abbrev = field[i];
2209 6 : ftype[i] = DTK_TZ;
2210 6 : break;
2211 :
2212 12 : case AMPM:
2213 12 : mer = val;
2214 12 : break;
2215 :
2216 0 : case ADBC:
2217 0 : bc = (val == BC);
2218 0 : break;
2219 :
2220 18 : case UNITS:
2221 18 : tmask = 0;
2222 : /* reject consecutive unhandled units */
2223 18 : if (ptype != 0)
2224 0 : return DTERR_BAD_FORMAT;
2225 18 : ptype = val;
2226 18 : break;
2227 :
2228 54 : case ISOTIME:
2229 54 : tmask = 0;
2230 : /* reject consecutive unhandled units */
2231 54 : if (ptype != 0)
2232 0 : return DTERR_BAD_FORMAT;
2233 54 : ptype = val;
2234 54 : break;
2235 :
2236 0 : case UNKNOWN_FIELD:
2237 :
2238 : /*
2239 : * Before giving up and declaring error, check to see
2240 : * if it is an all-alpha timezone name.
2241 : */
2242 0 : namedTz = pg_tzset(field[i]);
2243 0 : if (!namedTz)
2244 0 : return DTERR_BAD_FORMAT;
2245 : /* we'll apply the zone setting below */
2246 0 : tmask = DTK_M(TZ);
2247 0 : break;
2248 :
2249 0 : default:
2250 0 : return DTERR_BAD_FORMAT;
2251 : }
2252 360 : break;
2253 :
2254 0 : default:
2255 0 : return DTERR_BAD_FORMAT;
2256 : }
2257 :
2258 6326 : if (tmask & fmask)
2259 0 : return DTERR_BAD_FORMAT;
2260 6326 : fmask |= tmask;
2261 : } /* end loop over fields */
2262 :
2263 : /* reject if prefix type appeared and was never handled */
2264 4030 : if (ptype != 0)
2265 0 : return DTERR_BAD_FORMAT;
2266 :
2267 : /* do final checking/adjustment of Y/M/D fields */
2268 4030 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
2269 4030 : if (dterr)
2270 0 : return dterr;
2271 :
2272 : /* handle AM/PM */
2273 4030 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
2274 0 : return DTERR_FIELD_OVERFLOW;
2275 4030 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
2276 0 : tm->tm_hour = 0;
2277 4030 : else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
2278 12 : tm->tm_hour += HOURS_PER_DAY / 2;
2279 :
2280 : /* check for time overflow */
2281 4030 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec))
2282 72 : return DTERR_FIELD_OVERFLOW;
2283 :
2284 3958 : if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2285 0 : return DTERR_BAD_FORMAT;
2286 :
2287 : /*
2288 : * If we had a full timezone spec, compute the offset (we could not do it
2289 : * before, because we may need the date to resolve DST status).
2290 : */
2291 3958 : if (namedTz != NULL)
2292 : {
2293 : long int gmtoff;
2294 :
2295 : /* daylight savings time modifier disallowed with full TZ */
2296 1192 : if (fmask & DTK_M(DTZMOD))
2297 42 : return DTERR_BAD_FORMAT;
2298 :
2299 : /* if non-DST zone, we do not need to know the date */
2300 1192 : if (pg_get_timezone_offset(namedTz, &gmtoff))
2301 : {
2302 1114 : *tzp = -(int) gmtoff;
2303 : }
2304 : else
2305 : {
2306 : /* a date has to be specified */
2307 78 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2308 42 : return DTERR_BAD_FORMAT;
2309 36 : *tzp = DetermineTimeZoneOffset(tm, namedTz);
2310 : }
2311 : }
2312 :
2313 : /*
2314 : * Likewise, if we had a dynamic timezone abbreviation, resolve it now.
2315 : */
2316 3916 : if (abbrevTz != NULL)
2317 : {
2318 : struct pg_tm tt,
2319 0 : *tmp = &tt;
2320 :
2321 : /*
2322 : * daylight savings time modifier but no standard timezone? then error
2323 : */
2324 0 : if (fmask & DTK_M(DTZMOD))
2325 0 : return DTERR_BAD_FORMAT;
2326 :
2327 0 : if ((fmask & DTK_DATE_M) == 0)
2328 0 : GetCurrentDateTime(tmp);
2329 : else
2330 : {
2331 : /* a date has to be specified */
2332 0 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2333 0 : return DTERR_BAD_FORMAT;
2334 0 : tmp->tm_year = tm->tm_year;
2335 0 : tmp->tm_mon = tm->tm_mon;
2336 0 : tmp->tm_mday = tm->tm_mday;
2337 : }
2338 0 : tmp->tm_hour = tm->tm_hour;
2339 0 : tmp->tm_min = tm->tm_min;
2340 0 : tmp->tm_sec = tm->tm_sec;
2341 0 : *tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
2342 0 : tm->tm_isdst = tmp->tm_isdst;
2343 : }
2344 :
2345 : /* timezone not specified? then use session timezone */
2346 3916 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
2347 : {
2348 : struct pg_tm tt,
2349 2004 : *tmp = &tt;
2350 :
2351 : /*
2352 : * daylight savings time modifier but no standard timezone? then error
2353 : */
2354 2004 : if (fmask & DTK_M(DTZMOD))
2355 0 : return DTERR_BAD_FORMAT;
2356 :
2357 2004 : if ((fmask & DTK_DATE_M) == 0)
2358 1926 : GetCurrentDateTime(tmp);
2359 : else
2360 : {
2361 : /* a date has to be specified */
2362 78 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2363 0 : return DTERR_BAD_FORMAT;
2364 78 : tmp->tm_year = tm->tm_year;
2365 78 : tmp->tm_mon = tm->tm_mon;
2366 78 : tmp->tm_mday = tm->tm_mday;
2367 : }
2368 2004 : tmp->tm_hour = tm->tm_hour;
2369 2004 : tmp->tm_min = tm->tm_min;
2370 2004 : tmp->tm_sec = tm->tm_sec;
2371 2004 : *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
2372 2004 : tm->tm_isdst = tmp->tm_isdst;
2373 : }
2374 :
2375 3916 : return 0;
2376 : }
2377 :
2378 : /* DecodeDate()
2379 : * Decode date string which includes delimiters.
2380 : * Return 0 if okay, a DTERR code if not.
2381 : *
2382 : * str: field to be parsed
2383 : * fmask: bitmask for field types already seen
2384 : * *tmask: receives bitmask for fields found here
2385 : * *is2digits: set to true if we find 2-digit year
2386 : * *tm: field values are stored into appropriate members of this struct
2387 : */
2388 : static int
2389 63964 : DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
2390 : struct pg_tm *tm)
2391 : {
2392 : fsec_t fsec;
2393 63964 : int nf = 0;
2394 : int i,
2395 : len;
2396 : int dterr;
2397 63964 : bool haveTextMonth = false;
2398 : int type,
2399 : val,
2400 63964 : dmask = 0;
2401 : char *field[MAXDATEFIELDS];
2402 :
2403 63964 : *tmask = 0;
2404 :
2405 : /* parse this string... */
2406 255790 : while (*str != '\0' && nf < MAXDATEFIELDS)
2407 : {
2408 : /* skip field separators */
2409 191826 : while (*str != '\0' && !isalnum((unsigned char) *str))
2410 0 : str++;
2411 :
2412 191826 : if (*str == '\0')
2413 0 : return DTERR_BAD_FORMAT; /* end of string after separator */
2414 :
2415 191826 : field[nf] = str;
2416 191826 : if (isdigit((unsigned char) *str))
2417 : {
2418 703000 : while (isdigit((unsigned char) *str))
2419 511318 : str++;
2420 : }
2421 144 : else if (isalpha((unsigned char) *str))
2422 : {
2423 576 : while (isalpha((unsigned char) *str))
2424 432 : str++;
2425 : }
2426 :
2427 : /* Just get rid of any non-digit, non-alpha characters... */
2428 191826 : if (*str != '\0')
2429 127898 : *str++ = '\0';
2430 191826 : nf++;
2431 : }
2432 :
2433 : /* look first for text fields, since that will be unambiguous month */
2434 255790 : for (i = 0; i < nf; i++)
2435 : {
2436 191826 : if (isalpha((unsigned char) *field[i]))
2437 : {
2438 144 : type = DecodeSpecial(i, field[i], &val);
2439 144 : if (type == IGNORE_DTF)
2440 0 : continue;
2441 :
2442 144 : dmask = DTK_M(type);
2443 144 : switch (type)
2444 : {
2445 144 : case MONTH:
2446 144 : tm->tm_mon = val;
2447 144 : haveTextMonth = true;
2448 144 : break;
2449 :
2450 0 : default:
2451 0 : return DTERR_BAD_FORMAT;
2452 : }
2453 144 : if (fmask & dmask)
2454 0 : return DTERR_BAD_FORMAT;
2455 :
2456 144 : fmask |= dmask;
2457 144 : *tmask |= dmask;
2458 :
2459 : /* mark this field as being completed */
2460 144 : field[i] = NULL;
2461 : }
2462 : }
2463 :
2464 : /* now pick up remaining numeric fields */
2465 255790 : for (i = 0; i < nf; i++)
2466 : {
2467 191826 : if (field[i] == NULL)
2468 144 : continue;
2469 :
2470 191682 : if ((len = strlen(field[i])) <= 0)
2471 0 : return DTERR_BAD_FORMAT;
2472 :
2473 191682 : dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
2474 : &dmask, tm,
2475 : &fsec, is2digits);
2476 191682 : if (dterr)
2477 0 : return dterr;
2478 :
2479 191682 : if (fmask & dmask)
2480 0 : return DTERR_BAD_FORMAT;
2481 :
2482 191682 : fmask |= dmask;
2483 191682 : *tmask |= dmask;
2484 : }
2485 :
2486 63964 : if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2487 36 : return DTERR_BAD_FORMAT;
2488 :
2489 : /* validation of the field values must wait until ValidateDate() */
2490 :
2491 63928 : return 0;
2492 : }
2493 :
2494 : /* ValidateDate()
2495 : * Check valid year/month/day values, handle BC and DOY cases
2496 : * Return 0 if okay, a DTERR code if not.
2497 : */
2498 : int
2499 74278 : ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
2500 : struct pg_tm *tm)
2501 : {
2502 74278 : if (fmask & DTK_M(YEAR))
2503 : {
2504 70440 : if (isjulian)
2505 : {
2506 : /* tm_year is correct and should not be touched */
2507 : }
2508 67554 : else if (bc)
2509 : {
2510 : /* there is no year zero in AD/BC notation */
2511 270 : if (tm->tm_year <= 0)
2512 0 : return DTERR_FIELD_OVERFLOW;
2513 : /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
2514 270 : tm->tm_year = -(tm->tm_year - 1);
2515 : }
2516 67284 : else if (is2digits)
2517 : {
2518 : /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */
2519 354 : if (tm->tm_year < 0) /* just paranoia */
2520 0 : return DTERR_FIELD_OVERFLOW;
2521 354 : if (tm->tm_year < 70)
2522 174 : tm->tm_year += 2000;
2523 180 : else if (tm->tm_year < 100)
2524 180 : tm->tm_year += 1900;
2525 : }
2526 : else
2527 : {
2528 : /* there is no year zero in AD/BC notation */
2529 66930 : if (tm->tm_year <= 0)
2530 12 : return DTERR_FIELD_OVERFLOW;
2531 : }
2532 : }
2533 :
2534 : /* now that we have correct year, decode DOY */
2535 74266 : if (fmask & DTK_M(DOY))
2536 : {
2537 30 : j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
2538 : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2539 : }
2540 :
2541 : /* check for valid month */
2542 74266 : if (fmask & DTK_M(MONTH))
2543 : {
2544 70404 : if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
2545 78 : return DTERR_MD_FIELD_OVERFLOW;
2546 : }
2547 :
2548 : /* minimal check for valid day */
2549 74188 : if (fmask & DTK_M(DAY))
2550 : {
2551 70290 : if (tm->tm_mday < 1 || tm->tm_mday > 31)
2552 138 : return DTERR_MD_FIELD_OVERFLOW;
2553 : }
2554 :
2555 74050 : if ((fmask & DTK_DATE_M) == DTK_DATE_M)
2556 : {
2557 : /*
2558 : * Check for valid day of month, now that we know for sure the month
2559 : * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
2560 : * unlikely that "Feb 29" is a YMD-order error.
2561 : */
2562 70134 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2563 48 : return DTERR_FIELD_OVERFLOW;
2564 : }
2565 :
2566 74002 : return 0;
2567 : }
2568 :
2569 :
2570 : /* DecodeTimeCommon()
2571 : * Decode time string which includes delimiters.
2572 : * Return 0 if okay, a DTERR code if not.
2573 : * tmask and itm are output parameters.
2574 : *
2575 : * This code is shared between the timestamp and interval cases.
2576 : * We return a struct pg_itm (of which only the tm_usec, tm_sec, tm_min,
2577 : * and tm_hour fields are used) and let the wrapper functions below
2578 : * convert and range-check as necessary.
2579 : */
2580 : static int
2581 64308 : DecodeTimeCommon(char *str, int fmask, int range,
2582 : int *tmask, struct pg_itm *itm)
2583 : {
2584 : char *cp;
2585 : int dterr;
2586 64308 : fsec_t fsec = 0;
2587 :
2588 64308 : *tmask = DTK_TIME_M;
2589 :
2590 64308 : errno = 0;
2591 64308 : itm->tm_hour = strtoi64(str, &cp, 10);
2592 64308 : if (errno == ERANGE)
2593 0 : return DTERR_FIELD_OVERFLOW;
2594 64308 : if (*cp != ':')
2595 0 : return DTERR_BAD_FORMAT;
2596 64308 : errno = 0;
2597 64308 : itm->tm_min = strtoint(cp + 1, &cp, 10);
2598 64308 : if (errno == ERANGE)
2599 0 : return DTERR_FIELD_OVERFLOW;
2600 64308 : if (*cp == '\0')
2601 : {
2602 1578 : itm->tm_sec = 0;
2603 : /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
2604 1578 : if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
2605 : {
2606 18 : if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2607 0 : return DTERR_FIELD_OVERFLOW;
2608 18 : itm->tm_sec = itm->tm_min;
2609 18 : itm->tm_min = (int) itm->tm_hour;
2610 18 : itm->tm_hour = 0;
2611 : }
2612 : }
2613 62730 : else if (*cp == '.')
2614 : {
2615 : /* always assume mm:ss.sss is MINUTE TO SECOND */
2616 48 : dterr = ParseFractionalSecond(cp, &fsec);
2617 48 : if (dterr)
2618 12 : return dterr;
2619 36 : if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2620 0 : return DTERR_FIELD_OVERFLOW;
2621 36 : itm->tm_sec = itm->tm_min;
2622 36 : itm->tm_min = (int) itm->tm_hour;
2623 36 : itm->tm_hour = 0;
2624 : }
2625 62682 : else if (*cp == ':')
2626 : {
2627 62682 : errno = 0;
2628 62682 : itm->tm_sec = strtoint(cp + 1, &cp, 10);
2629 62682 : if (errno == ERANGE)
2630 0 : return DTERR_FIELD_OVERFLOW;
2631 62682 : if (*cp == '.')
2632 : {
2633 22566 : dterr = ParseFractionalSecond(cp, &fsec);
2634 22566 : if (dterr)
2635 0 : return dterr;
2636 : }
2637 40116 : else if (*cp != '\0')
2638 0 : return DTERR_BAD_FORMAT;
2639 : }
2640 : else
2641 0 : return DTERR_BAD_FORMAT;
2642 :
2643 : /* do a sanity check; but caller must check the range of tm_hour */
2644 64296 : if (itm->tm_hour < 0 ||
2645 64296 : itm->tm_min < 0 || itm->tm_min > MINS_PER_HOUR - 1 ||
2646 64296 : itm->tm_sec < 0 || itm->tm_sec > SECS_PER_MINUTE ||
2647 64296 : fsec < 0 || fsec > USECS_PER_SEC)
2648 0 : return DTERR_FIELD_OVERFLOW;
2649 :
2650 64296 : itm->tm_usec = (int) fsec;
2651 :
2652 64296 : return 0;
2653 : }
2654 :
2655 : /* DecodeTime()
2656 : * Decode time string which includes delimiters.
2657 : * Return 0 if okay, a DTERR code if not.
2658 : *
2659 : * This version is used for timestamps. The results are returned into
2660 : * the tm_hour/tm_min/tm_sec fields of *tm, and microseconds into *fsec.
2661 : */
2662 : static int
2663 62318 : DecodeTime(char *str, int fmask, int range,
2664 : int *tmask, struct pg_tm *tm, fsec_t *fsec)
2665 : {
2666 : struct pg_itm itm;
2667 : int dterr;
2668 :
2669 62318 : dterr = DecodeTimeCommon(str, fmask, range,
2670 : tmask, &itm);
2671 62318 : if (dterr)
2672 0 : return dterr;
2673 :
2674 62318 : if (itm.tm_hour > INT_MAX)
2675 0 : return DTERR_FIELD_OVERFLOW;
2676 62318 : tm->tm_hour = (int) itm.tm_hour;
2677 62318 : tm->tm_min = itm.tm_min;
2678 62318 : tm->tm_sec = itm.tm_sec;
2679 62318 : *fsec = itm.tm_usec;
2680 :
2681 62318 : return 0;
2682 : }
2683 :
2684 : /* DecodeTimeForInterval()
2685 : * Decode time string which includes delimiters.
2686 : * Return 0 if okay, a DTERR code if not.
2687 : *
2688 : * This version is used for intervals. The results are returned into
2689 : * itm_in->tm_usec.
2690 : */
2691 : static int
2692 1990 : DecodeTimeForInterval(char *str, int fmask, int range,
2693 : int *tmask, struct pg_itm_in *itm_in)
2694 : {
2695 : struct pg_itm itm;
2696 : int dterr;
2697 :
2698 1990 : dterr = DecodeTimeCommon(str, fmask, range,
2699 : tmask, &itm);
2700 1990 : if (dterr)
2701 12 : return dterr;
2702 :
2703 1978 : itm_in->tm_usec = itm.tm_usec;
2704 1978 : if (!int64_multiply_add(itm.tm_hour, USECS_PER_HOUR, &itm_in->tm_usec) ||
2705 1978 : !int64_multiply_add(itm.tm_min, USECS_PER_MINUTE, &itm_in->tm_usec) ||
2706 1978 : !int64_multiply_add(itm.tm_sec, USECS_PER_SEC, &itm_in->tm_usec))
2707 6 : return DTERR_FIELD_OVERFLOW;
2708 :
2709 1972 : return 0;
2710 : }
2711 :
2712 :
2713 : /* DecodeNumber()
2714 : * Interpret plain numeric field as a date value in context.
2715 : * Return 0 if okay, a DTERR code if not.
2716 : */
2717 : static int
2718 197402 : DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
2719 : int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2720 : {
2721 : int val;
2722 : char *cp;
2723 : int dterr;
2724 :
2725 197402 : *tmask = 0;
2726 :
2727 197402 : errno = 0;
2728 197402 : val = strtoint(str, &cp, 10);
2729 197402 : if (errno == ERANGE)
2730 0 : return DTERR_FIELD_OVERFLOW;
2731 197402 : if (cp == str)
2732 0 : return DTERR_BAD_FORMAT;
2733 :
2734 197402 : if (*cp == '.')
2735 : {
2736 : /*
2737 : * More than two digits before decimal point? Then could be a date or
2738 : * a run-together time: 2001.360 20011225 040506.789
2739 : */
2740 0 : if (cp - str > 2)
2741 : {
2742 0 : dterr = DecodeNumberField(flen, str,
2743 : (fmask | DTK_DATE_M),
2744 : tmask, tm,
2745 : fsec, is2digits);
2746 0 : if (dterr < 0)
2747 0 : return dterr;
2748 0 : return 0;
2749 : }
2750 :
2751 0 : dterr = ParseFractionalSecond(cp, fsec);
2752 0 : if (dterr)
2753 0 : return dterr;
2754 : }
2755 197402 : else if (*cp != '\0')
2756 0 : return DTERR_BAD_FORMAT;
2757 :
2758 : /* Special case for day of year */
2759 197402 : if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&
2760 : val <= 366)
2761 : {
2762 54 : *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2763 54 : tm->tm_yday = val;
2764 : /* tm_mon and tm_mday can't actually be set yet ... */
2765 54 : return 0;
2766 : }
2767 :
2768 : /* Switch based on what we have so far */
2769 197348 : switch (fmask & DTK_DATE_M)
2770 : {
2771 64154 : case 0:
2772 :
2773 : /*
2774 : * Nothing so far; make a decision about what we think the input
2775 : * is. There used to be lots of heuristics here, but the
2776 : * consensus now is to be paranoid. It *must* be either
2777 : * YYYY-MM-DD (with a more-than-two-digit year field), or the
2778 : * field order defined by DateOrder.
2779 : */
2780 64154 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
2781 : {
2782 62462 : *tmask = DTK_M(YEAR);
2783 62462 : tm->tm_year = val;
2784 : }
2785 1692 : else if (DateOrder == DATEORDER_DMY)
2786 : {
2787 164 : *tmask = DTK_M(DAY);
2788 164 : tm->tm_mday = val;
2789 : }
2790 : else
2791 : {
2792 1528 : *tmask = DTK_M(MONTH);
2793 1528 : tm->tm_mon = val;
2794 : }
2795 64154 : break;
2796 :
2797 62354 : case (DTK_M(YEAR)):
2798 : /* Must be at second field of YY-MM-DD */
2799 62354 : *tmask = DTK_M(MONTH);
2800 62354 : tm->tm_mon = val;
2801 62354 : break;
2802 :
2803 4072 : case (DTK_M(MONTH)):
2804 4072 : if (haveTextMonth)
2805 : {
2806 : /*
2807 : * We are at the first numeric field of a date that included a
2808 : * textual month name. We want to support the variants
2809 : * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2810 : * inputs. We will also accept MON-DD-YY or DD-MON-YY in
2811 : * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2812 : */
2813 2600 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
2814 : {
2815 72 : *tmask = DTK_M(YEAR);
2816 72 : tm->tm_year = val;
2817 : }
2818 : else
2819 : {
2820 2528 : *tmask = DTK_M(DAY);
2821 2528 : tm->tm_mday = val;
2822 : }
2823 : }
2824 : else
2825 : {
2826 : /* Must be at second field of MM-DD-YY */
2827 1472 : *tmask = DTK_M(DAY);
2828 1472 : tm->tm_mday = val;
2829 : }
2830 4072 : break;
2831 :
2832 62432 : case (DTK_M(YEAR) | DTK_M(MONTH)):
2833 62432 : if (haveTextMonth)
2834 : {
2835 : /* Need to accept DD-MON-YYYY even in YMD mode */
2836 126 : if (flen >= 3 && *is2digits)
2837 : {
2838 : /* Guess that first numeric field is day was wrong */
2839 30 : *tmask = DTK_M(DAY); /* YEAR is already set */
2840 30 : tm->tm_mday = tm->tm_year;
2841 30 : tm->tm_year = val;
2842 30 : *is2digits = false;
2843 : }
2844 : else
2845 : {
2846 96 : *tmask = DTK_M(DAY);
2847 96 : tm->tm_mday = val;
2848 : }
2849 : }
2850 : else
2851 : {
2852 : /* Must be at third field of YY-MM-DD */
2853 62306 : *tmask = DTK_M(DAY);
2854 62306 : tm->tm_mday = val;
2855 : }
2856 62432 : break;
2857 :
2858 146 : case (DTK_M(DAY)):
2859 : /* Must be at second field of DD-MM-YY */
2860 146 : *tmask = DTK_M(MONTH);
2861 146 : tm->tm_mon = val;
2862 146 : break;
2863 :
2864 4178 : case (DTK_M(MONTH) | DTK_M(DAY)):
2865 : /* Must be at third field of DD-MM-YY or MM-DD-YY */
2866 4178 : *tmask = DTK_M(YEAR);
2867 4178 : tm->tm_year = val;
2868 4178 : break;
2869 :
2870 12 : case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
2871 : /* we have all the date, so it must be a time field */
2872 12 : dterr = DecodeNumberField(flen, str, fmask,
2873 : tmask, tm,
2874 : fsec, is2digits);
2875 12 : if (dterr < 0)
2876 12 : return dterr;
2877 0 : return 0;
2878 :
2879 0 : default:
2880 : /* Anything else is bogus input */
2881 0 : return DTERR_BAD_FORMAT;
2882 : }
2883 :
2884 : /*
2885 : * When processing a year field, mark it for adjustment if it's only one
2886 : * or two digits.
2887 : */
2888 197336 : if (*tmask == DTK_M(YEAR))
2889 66712 : *is2digits = (flen <= 2);
2890 :
2891 197336 : return 0;
2892 : }
2893 :
2894 :
2895 : /* DecodeNumberField()
2896 : * Interpret numeric string as a concatenated date or time field.
2897 : * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2898 : *
2899 : * Use the context of previously decoded fields to help with
2900 : * the interpretation.
2901 : */
2902 : static int
2903 494 : DecodeNumberField(int len, char *str, int fmask,
2904 : int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2905 : {
2906 : char *cp;
2907 :
2908 : /*
2909 : * Have a decimal point? Then this is a date or something with a seconds
2910 : * field...
2911 : */
2912 494 : if ((cp = strchr(str, '.')) != NULL)
2913 : {
2914 : /*
2915 : * Can we use ParseFractionalSecond here? Not clear whether trailing
2916 : * junk should be rejected ...
2917 : */
2918 114 : if (cp[1] == '\0')
2919 : {
2920 : /* avoid assuming that strtod will accept "." */
2921 0 : *fsec = 0;
2922 : }
2923 : else
2924 : {
2925 : double frac;
2926 :
2927 114 : errno = 0;
2928 114 : frac = strtod(cp, NULL);
2929 114 : if (errno != 0)
2930 0 : return DTERR_BAD_FORMAT;
2931 114 : *fsec = rint(frac * 1000000);
2932 : }
2933 : /* Now truncate off the fraction for further processing */
2934 114 : *cp = '\0';
2935 114 : len = strlen(str);
2936 : }
2937 : /* No decimal point and no complete date yet? */
2938 380 : else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2939 : {
2940 248 : if (len >= 6)
2941 : {
2942 248 : *tmask = DTK_DATE_M;
2943 :
2944 : /*
2945 : * Start from end and consider first 2 as Day, next 2 as Month,
2946 : * and the rest as Year.
2947 : */
2948 248 : tm->tm_mday = atoi(str + (len - 2));
2949 248 : *(str + (len - 2)) = '\0';
2950 248 : tm->tm_mon = atoi(str + (len - 4));
2951 248 : *(str + (len - 4)) = '\0';
2952 248 : tm->tm_year = atoi(str);
2953 248 : if ((len - 4) == 2)
2954 18 : *is2digits = true;
2955 :
2956 248 : return DTK_DATE;
2957 : }
2958 : }
2959 :
2960 : /* not all time fields are specified? */
2961 246 : if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2962 : {
2963 : /* hhmmss */
2964 246 : if (len == 6)
2965 : {
2966 234 : *tmask = DTK_TIME_M;
2967 234 : tm->tm_sec = atoi(str + 4);
2968 234 : *(str + 4) = '\0';
2969 234 : tm->tm_min = atoi(str + 2);
2970 234 : *(str + 2) = '\0';
2971 234 : tm->tm_hour = atoi(str);
2972 :
2973 234 : return DTK_TIME;
2974 : }
2975 : /* hhmm? */
2976 12 : else if (len == 4)
2977 : {
2978 0 : *tmask = DTK_TIME_M;
2979 0 : tm->tm_sec = 0;
2980 0 : tm->tm_min = atoi(str + 2);
2981 0 : *(str + 2) = '\0';
2982 0 : tm->tm_hour = atoi(str);
2983 :
2984 0 : return DTK_TIME;
2985 : }
2986 : }
2987 :
2988 12 : return DTERR_BAD_FORMAT;
2989 : }
2990 :
2991 :
2992 : /* DecodeTimezone()
2993 : * Interpret string as a numeric timezone.
2994 : *
2995 : * Return 0 if okay (and set *tzp), a DTERR code if not okay.
2996 : */
2997 : int
2998 40784 : DecodeTimezone(const char *str, int *tzp)
2999 : {
3000 : int tz;
3001 : int hr,
3002 : min,
3003 40784 : sec = 0;
3004 : char *cp;
3005 :
3006 : /* leading character must be "+" or "-" */
3007 40784 : if (*str != '+' && *str != '-')
3008 60 : return DTERR_BAD_FORMAT;
3009 :
3010 40724 : errno = 0;
3011 40724 : hr = strtoint(str + 1, &cp, 10);
3012 40724 : if (errno == ERANGE)
3013 0 : return DTERR_TZDISP_OVERFLOW;
3014 :
3015 : /* explicit delimiter? */
3016 40724 : if (*cp == ':')
3017 : {
3018 1620 : errno = 0;
3019 1620 : min = strtoint(cp + 1, &cp, 10);
3020 1620 : if (errno == ERANGE)
3021 0 : return DTERR_TZDISP_OVERFLOW;
3022 1620 : if (*cp == ':')
3023 : {
3024 24 : errno = 0;
3025 24 : sec = strtoint(cp + 1, &cp, 10);
3026 24 : if (errno == ERANGE)
3027 0 : return DTERR_TZDISP_OVERFLOW;
3028 : }
3029 : }
3030 : /* otherwise, might have run things together... */
3031 39104 : else if (*cp == '\0' && strlen(str) > 3)
3032 : {
3033 72 : min = hr % 100;
3034 72 : hr = hr / 100;
3035 : /* we could, but don't, support a run-together hhmmss format */
3036 : }
3037 : else
3038 39032 : min = 0;
3039 :
3040 : /* Range-check the values; see notes in datatype/timestamp.h */
3041 40724 : if (hr < 0 || hr > MAX_TZDISP_HOUR)
3042 12 : return DTERR_TZDISP_OVERFLOW;
3043 40712 : if (min < 0 || min >= MINS_PER_HOUR)
3044 12 : return DTERR_TZDISP_OVERFLOW;
3045 40700 : if (sec < 0 || sec >= SECS_PER_MINUTE)
3046 0 : return DTERR_TZDISP_OVERFLOW;
3047 :
3048 40700 : tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
3049 40700 : if (*str == '-')
3050 982 : tz = -tz;
3051 :
3052 40700 : *tzp = -tz;
3053 :
3054 40700 : if (*cp != '\0')
3055 0 : return DTERR_BAD_FORMAT;
3056 :
3057 40700 : return 0;
3058 : }
3059 :
3060 :
3061 : /* DecodeTimezoneAbbrev()
3062 : * Interpret string as a timezone abbreviation, if possible.
3063 : *
3064 : * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3065 : * string is not any known abbreviation. On success, set *offset and *tz to
3066 : * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
3067 : * Note that full timezone names (such as America/New_York) are not handled
3068 : * here, mostly for historical reasons.
3069 : *
3070 : * The function result is 0 or a DTERR code; in the latter case, *extra
3071 : * is filled as needed. Note that unknown-abbreviation is not considered
3072 : * an error case. Also note that many callers assume that the DTERR code
3073 : * is one that DateTimeParseError does not require "str" or "datatype"
3074 : * strings for.
3075 : *
3076 : * Given string must be lowercased already.
3077 : *
3078 : * Implement a cache lookup since it is likely that dates
3079 : * will be related in format.
3080 : */
3081 : int
3082 10238 : DecodeTimezoneAbbrev(int field, const char *lowtoken,
3083 : int *ftype, int *offset, pg_tz **tz,
3084 : DateTimeErrorExtra *extra)
3085 : {
3086 : const datetkn *tp;
3087 :
3088 10238 : tp = abbrevcache[field];
3089 : /* use strncmp so that we match truncated tokens */
3090 10238 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3091 : {
3092 7542 : if (zoneabbrevtbl)
3093 7542 : tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3094 7542 : zoneabbrevtbl->numabbrevs);
3095 : else
3096 0 : tp = NULL;
3097 : }
3098 10238 : if (tp == NULL)
3099 : {
3100 7138 : *ftype = UNKNOWN_FIELD;
3101 7138 : *offset = 0;
3102 7138 : *tz = NULL;
3103 : }
3104 : else
3105 : {
3106 3100 : abbrevcache[field] = tp;
3107 3100 : *ftype = tp->type;
3108 3100 : if (tp->type == DYNTZ)
3109 : {
3110 264 : *offset = 0;
3111 264 : *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp, extra);
3112 264 : if (*tz == NULL)
3113 0 : return DTERR_BAD_ZONE_ABBREV;
3114 : }
3115 : else
3116 : {
3117 2836 : *offset = tp->value;
3118 2836 : *tz = NULL;
3119 : }
3120 : }
3121 :
3122 10238 : return 0;
3123 : }
3124 :
3125 :
3126 : /* DecodeSpecial()
3127 : * Decode text string using lookup table.
3128 : *
3129 : * Recognizes the keywords listed in datetktbl.
3130 : * Note: at one time this would also recognize timezone abbreviations,
3131 : * but no more; use DecodeTimezoneAbbrev for that.
3132 : *
3133 : * Given string must be lowercased already.
3134 : *
3135 : * Implement a cache lookup since it is likely that dates
3136 : * will be related in format.
3137 : */
3138 : int
3139 41948 : DecodeSpecial(int field, const char *lowtoken, int *val)
3140 : {
3141 : int type;
3142 : const datetkn *tp;
3143 :
3144 41948 : tp = datecache[field];
3145 : /* use strncmp so that we match truncated tokens */
3146 41948 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3147 : {
3148 9694 : tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
3149 : }
3150 41948 : if (tp == NULL)
3151 : {
3152 108 : type = UNKNOWN_FIELD;
3153 108 : *val = 0;
3154 : }
3155 : else
3156 : {
3157 41840 : datecache[field] = tp;
3158 41840 : type = tp->type;
3159 41840 : *val = tp->value;
3160 : }
3161 :
3162 41948 : return type;
3163 : }
3164 :
3165 :
3166 : /* DecodeTimezoneName()
3167 : * Interpret string as a timezone abbreviation or name.
3168 : * Throw error if the name is not recognized.
3169 : *
3170 : * The return value indicates what kind of zone identifier it is:
3171 : * TZNAME_FIXED_OFFSET: fixed offset from UTC
3172 : * TZNAME_DYNTZ: dynamic timezone abbreviation
3173 : * TZNAME_ZONE: full tzdb zone name
3174 : *
3175 : * For TZNAME_FIXED_OFFSET, *offset receives the UTC offset (in seconds,
3176 : * with ISO sign convention: positive is east of Greenwich).
3177 : * For the other two cases, *tz receives the timezone struct representing
3178 : * the zone name or the abbreviation's underlying zone.
3179 : */
3180 : int
3181 786 : DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz)
3182 : {
3183 : char *lowzone;
3184 : int dterr,
3185 : type;
3186 : DateTimeErrorExtra extra;
3187 :
3188 : /*
3189 : * First we look in the timezone abbreviation table (to handle cases like
3190 : * "EST"), and if that fails, we look in the timezone database (to handle
3191 : * cases like "America/New_York"). This matches the order in which
3192 : * timestamp input checks the cases; it's important because the timezone
3193 : * database unwisely uses a few zone names that are identical to offset
3194 : * abbreviations.
3195 : */
3196 :
3197 : /* DecodeTimezoneAbbrev requires lowercase input */
3198 786 : lowzone = downcase_truncate_identifier(tzname,
3199 786 : strlen(tzname),
3200 : false);
3201 :
3202 786 : dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
3203 786 : if (dterr)
3204 0 : DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
3205 :
3206 786 : if (type == TZ || type == DTZ)
3207 : {
3208 : /* fixed-offset abbreviation, return the offset */
3209 270 : return TZNAME_FIXED_OFFSET;
3210 : }
3211 516 : else if (type == DYNTZ)
3212 : {
3213 : /* dynamic-offset abbreviation, return its referenced timezone */
3214 174 : return TZNAME_DYNTZ;
3215 : }
3216 : else
3217 : {
3218 : /* try it as a full zone name */
3219 342 : *tz = pg_tzset(tzname);
3220 342 : if (*tz == NULL)
3221 12 : ereport(ERROR,
3222 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3223 : errmsg("time zone \"%s\" not recognized", tzname)));
3224 330 : return TZNAME_ZONE;
3225 : }
3226 : }
3227 :
3228 : /* DecodeTimezoneNameToTz()
3229 : * Interpret string as a timezone abbreviation or name.
3230 : * Throw error if the name is not recognized.
3231 : *
3232 : * This is a simple wrapper for DecodeTimezoneName that produces a pg_tz *
3233 : * result in all cases.
3234 : */
3235 : pg_tz *
3236 72 : DecodeTimezoneNameToTz(const char *tzname)
3237 : {
3238 : pg_tz *result;
3239 : int offset;
3240 :
3241 72 : if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
3242 : {
3243 : /* fixed-offset abbreviation, get a pg_tz descriptor for that */
3244 6 : result = pg_tzset_offset(-offset); /* flip to POSIX sign convention */
3245 : }
3246 72 : return result;
3247 : }
3248 :
3249 :
3250 : /* ClearPgItmIn
3251 : *
3252 : * Zero out a pg_itm_in
3253 : */
3254 : static inline void
3255 12300 : ClearPgItmIn(struct pg_itm_in *itm_in)
3256 : {
3257 12300 : itm_in->tm_usec = 0;
3258 12300 : itm_in->tm_mday = 0;
3259 12300 : itm_in->tm_mon = 0;
3260 12300 : itm_in->tm_year = 0;
3261 12300 : }
3262 :
3263 :
3264 : /* DecodeInterval()
3265 : * Interpret previously parsed fields for general time interval.
3266 : * Returns 0 if successful, DTERR code if bogus input detected.
3267 : * dtype and itm_in are output parameters.
3268 : *
3269 : * Allow "date" field DTK_DATE since this could be just
3270 : * an unsigned floating point number. - thomas 1997-11-16
3271 : *
3272 : * Allow ISO-style time span, with implicit units on number of days
3273 : * preceding an hh:mm:ss field. - thomas 1998-04-30
3274 : *
3275 : * itm_in remains undefined for infinite interval values for which dtype alone
3276 : * suffices.
3277 : */
3278 : int
3279 11688 : DecodeInterval(char **field, int *ftype, int nf, int range,
3280 : int *dtype, struct pg_itm_in *itm_in)
3281 : {
3282 11688 : bool force_negative = false;
3283 11688 : bool is_before = false;
3284 11688 : bool parsing_unit_val = false;
3285 : char *cp;
3286 11688 : int fmask = 0,
3287 : tmask,
3288 : type,
3289 : uval;
3290 : int i;
3291 : int dterr;
3292 : int64 val;
3293 : double fval;
3294 :
3295 11688 : *dtype = DTK_DELTA;
3296 11688 : type = IGNORE_DTF;
3297 11688 : ClearPgItmIn(itm_in);
3298 :
3299 : /*----------
3300 : * The SQL standard defines the interval literal
3301 : * '-1 1:00:00'
3302 : * to mean "negative 1 days and negative 1 hours", while Postgres
3303 : * traditionally treats this as meaning "negative 1 days and positive
3304 : * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
3305 : * to all fields if there are no other explicit signs.
3306 : *
3307 : * We leave the signs alone if there are additional explicit signs.
3308 : * This protects us against misinterpreting postgres-style dump output,
3309 : * since the postgres-style output code has always put an explicit sign on
3310 : * all fields following a negative field. But note that SQL-spec output
3311 : * is ambiguous and can be misinterpreted on load! (So it's best practice
3312 : * to dump in postgres style, not SQL style.)
3313 : *----------
3314 : */
3315 11688 : if (IntervalStyle == INTSTYLE_SQL_STANDARD && nf > 0 && *field[0] == '-')
3316 : {
3317 38 : force_negative = true;
3318 : /* Check for additional explicit signs */
3319 244 : for (i = 1; i < nf; i++)
3320 : {
3321 224 : if (*field[i] == '-' || *field[i] == '+')
3322 : {
3323 18 : force_negative = false;
3324 18 : break;
3325 : }
3326 : }
3327 : }
3328 :
3329 : /* read through list backwards to pick up units before values */
3330 36716 : for (i = nf - 1; i >= 0; i--)
3331 : {
3332 26120 : switch (ftype[i])
3333 : {
3334 1228 : case DTK_TIME:
3335 1228 : dterr = DecodeTimeForInterval(field[i], fmask, range,
3336 : &tmask, itm_in);
3337 1228 : if (dterr)
3338 18 : return dterr;
3339 1210 : if (force_negative &&
3340 2 : itm_in->tm_usec > 0)
3341 2 : itm_in->tm_usec = -itm_in->tm_usec;
3342 1210 : type = DTK_DAY;
3343 1210 : parsing_unit_val = false;
3344 1210 : break;
3345 :
3346 2716 : case DTK_TZ:
3347 :
3348 : /*
3349 : * Timezone means a token with a leading sign character and at
3350 : * least one digit; there could be ':', '.', '-' embedded in
3351 : * it as well.
3352 : */
3353 : Assert(*field[i] == '-' || *field[i] == '+');
3354 :
3355 : /*
3356 : * Check for signed hh:mm or hh:mm:ss. If so, process exactly
3357 : * like DTK_TIME case above, plus handling the sign.
3358 : */
3359 3478 : if (strchr(field[i] + 1, ':') != NULL &&
3360 762 : DecodeTimeForInterval(field[i] + 1, fmask, range,
3361 : &tmask, itm_in) == 0)
3362 : {
3363 762 : if (*field[i] == '-')
3364 : {
3365 : /* flip the sign on time field */
3366 702 : if (itm_in->tm_usec == PG_INT64_MIN)
3367 0 : return DTERR_FIELD_OVERFLOW;
3368 702 : itm_in->tm_usec = -itm_in->tm_usec;
3369 : }
3370 :
3371 762 : if (force_negative &&
3372 0 : itm_in->tm_usec > 0)
3373 0 : itm_in->tm_usec = -itm_in->tm_usec;
3374 :
3375 : /*
3376 : * Set the next type to be a day, if units are not
3377 : * specified. This handles the case of '1 +02:03' since we
3378 : * are reading right to left.
3379 : */
3380 762 : type = DTK_DAY;
3381 762 : parsing_unit_val = false;
3382 762 : break;
3383 : }
3384 :
3385 : /*
3386 : * Otherwise, fall through to DTK_NUMBER case, which can
3387 : * handle signed float numbers and signed year-month values.
3388 : */
3389 :
3390 : /* FALLTHROUGH */
3391 :
3392 : case DTK_DATE:
3393 : case DTK_NUMBER:
3394 11922 : if (type == IGNORE_DTF)
3395 : {
3396 : /* use typmod to decide what rightmost field is */
3397 : switch (range)
3398 : {
3399 6 : case INTERVAL_MASK(YEAR):
3400 6 : type = DTK_YEAR;
3401 6 : break;
3402 30 : case INTERVAL_MASK(MONTH):
3403 : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3404 30 : type = DTK_MONTH;
3405 30 : break;
3406 18 : case INTERVAL_MASK(DAY):
3407 18 : type = DTK_DAY;
3408 18 : break;
3409 24 : case INTERVAL_MASK(HOUR):
3410 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3411 24 : type = DTK_HOUR;
3412 24 : break;
3413 24 : case INTERVAL_MASK(MINUTE):
3414 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3415 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3416 24 : type = DTK_MINUTE;
3417 24 : break;
3418 60 : case INTERVAL_MASK(SECOND):
3419 : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3420 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3421 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3422 60 : type = DTK_SECOND;
3423 60 : break;
3424 438 : default:
3425 438 : type = DTK_SECOND;
3426 438 : break;
3427 : }
3428 11322 : }
3429 :
3430 11922 : errno = 0;
3431 11922 : val = strtoi64(field[i], &cp, 10);
3432 11922 : if (errno == ERANGE)
3433 12 : return DTERR_FIELD_OVERFLOW;
3434 :
3435 11910 : if (*cp == '-')
3436 : {
3437 : /* SQL "years-months" syntax */
3438 : int val2;
3439 :
3440 60 : val2 = strtoint(cp + 1, &cp, 10);
3441 60 : if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
3442 0 : return DTERR_FIELD_OVERFLOW;
3443 60 : if (*cp != '\0')
3444 0 : return DTERR_BAD_FORMAT;
3445 60 : type = DTK_MONTH;
3446 60 : if (*field[i] == '-')
3447 6 : val2 = -val2;
3448 60 : if (pg_mul_s64_overflow(val, MONTHS_PER_YEAR, &val))
3449 0 : return DTERR_FIELD_OVERFLOW;
3450 60 : if (pg_add_s64_overflow(val, val2, &val))
3451 0 : return DTERR_FIELD_OVERFLOW;
3452 60 : fval = 0;
3453 : }
3454 11850 : else if (*cp == '.')
3455 : {
3456 486 : dterr = ParseFraction(cp, &fval);
3457 486 : if (dterr)
3458 0 : return dterr;
3459 486 : if (*field[i] == '-')
3460 138 : fval = -fval;
3461 : }
3462 11364 : else if (*cp == '\0')
3463 10980 : fval = 0;
3464 : else
3465 384 : return DTERR_BAD_FORMAT;
3466 :
3467 11526 : tmask = 0; /* DTK_M(type); */
3468 :
3469 11526 : if (force_negative)
3470 : {
3471 : /* val and fval should be of same sign, but test anyway */
3472 80 : if (val > 0)
3473 60 : val = -val;
3474 80 : if (fval > 0)
3475 18 : fval = -fval;
3476 : }
3477 :
3478 : switch (type)
3479 : {
3480 318 : case DTK_MICROSEC:
3481 318 : if (!AdjustMicroseconds(val, fval, 1, itm_in))
3482 36 : return DTERR_FIELD_OVERFLOW;
3483 282 : tmask = DTK_M(MICROSECOND);
3484 282 : break;
3485 :
3486 102 : case DTK_MILLISEC:
3487 102 : if (!AdjustMicroseconds(val, fval, 1000, itm_in))
3488 12 : return DTERR_FIELD_OVERFLOW;
3489 90 : tmask = DTK_M(MILLISECOND);
3490 90 : break;
3491 :
3492 920 : case DTK_SECOND:
3493 920 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3494 12 : return DTERR_FIELD_OVERFLOW;
3495 :
3496 : /*
3497 : * If any subseconds were specified, consider this
3498 : * microsecond and millisecond input as well.
3499 : */
3500 908 : if (fval == 0)
3501 746 : tmask = DTK_M(SECOND);
3502 : else
3503 162 : tmask = DTK_ALL_SECS_M;
3504 908 : break;
3505 :
3506 346 : case DTK_MINUTE:
3507 346 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3508 12 : return DTERR_FIELD_OVERFLOW;
3509 334 : tmask = DTK_M(MINUTE);
3510 334 : break;
3511 :
3512 692 : case DTK_HOUR:
3513 692 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3514 12 : return DTERR_FIELD_OVERFLOW;
3515 680 : tmask = DTK_M(HOUR);
3516 680 : type = DTK_DAY; /* set for next field */
3517 680 : break;
3518 :
3519 6968 : case DTK_DAY:
3520 6968 : if (!AdjustDays(val, 1, itm_in) ||
3521 6896 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3522 90 : return DTERR_FIELD_OVERFLOW;
3523 6878 : tmask = DTK_M(DAY);
3524 6878 : break;
3525 :
3526 96 : case DTK_WEEK:
3527 96 : if (!AdjustDays(val, 7, itm_in) ||
3528 72 : !AdjustFractDays(fval, 7, itm_in))
3529 48 : return DTERR_FIELD_OVERFLOW;
3530 48 : tmask = DTK_M(WEEK);
3531 48 : break;
3532 :
3533 900 : case DTK_MONTH:
3534 900 : if (!AdjustMonths(val, itm_in) ||
3535 840 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3536 84 : return DTERR_FIELD_OVERFLOW;
3537 816 : tmask = DTK_M(MONTH);
3538 816 : break;
3539 :
3540 986 : case DTK_YEAR:
3541 986 : if (!AdjustYears(val, 1, itm_in) ||
3542 938 : !AdjustFractYears(fval, 1, itm_in))
3543 60 : return DTERR_FIELD_OVERFLOW;
3544 926 : tmask = DTK_M(YEAR);
3545 926 : break;
3546 :
3547 66 : case DTK_DECADE:
3548 66 : if (!AdjustYears(val, 10, itm_in) ||
3549 42 : !AdjustFractYears(fval, 10, itm_in))
3550 36 : return DTERR_FIELD_OVERFLOW;
3551 30 : tmask = DTK_M(DECADE);
3552 30 : break;
3553 :
3554 66 : case DTK_CENTURY:
3555 66 : if (!AdjustYears(val, 100, itm_in) ||
3556 42 : !AdjustFractYears(fval, 100, itm_in))
3557 36 : return DTERR_FIELD_OVERFLOW;
3558 30 : tmask = DTK_M(CENTURY);
3559 30 : break;
3560 :
3561 66 : case DTK_MILLENNIUM:
3562 66 : if (!AdjustYears(val, 1000, itm_in) ||
3563 42 : !AdjustFractYears(fval, 1000, itm_in))
3564 36 : return DTERR_FIELD_OVERFLOW;
3565 30 : tmask = DTK_M(MILLENNIUM);
3566 30 : break;
3567 :
3568 0 : default:
3569 0 : return DTERR_BAD_FORMAT;
3570 : }
3571 11052 : parsing_unit_val = false;
3572 11052 : break;
3573 :
3574 12208 : case DTK_STRING:
3575 : case DTK_SPECIAL:
3576 : /* reject consecutive unhandled units */
3577 12208 : if (parsing_unit_val)
3578 12 : return DTERR_BAD_FORMAT;
3579 12196 : type = DecodeUnits(i, field[i], &uval);
3580 12196 : if (type == UNKNOWN_FIELD)
3581 1062 : type = DecodeSpecial(i, field[i], &uval);
3582 12196 : if (type == IGNORE_DTF)
3583 0 : continue;
3584 :
3585 12196 : tmask = 0; /* DTK_M(type); */
3586 : switch (type)
3587 : {
3588 11032 : case UNITS:
3589 11032 : type = uval;
3590 11032 : parsing_unit_val = true;
3591 11032 : break;
3592 :
3593 102 : case AGO:
3594 :
3595 : /*
3596 : * "ago" is only allowed to appear at the end of the
3597 : * interval.
3598 : */
3599 102 : if (i != nf - 1)
3600 12 : return DTERR_BAD_FORMAT;
3601 90 : is_before = true;
3602 90 : type = uval;
3603 90 : break;
3604 :
3605 1026 : case RESERV:
3606 1026 : tmask = (DTK_DATE_M | DTK_TIME_M);
3607 :
3608 : /*
3609 : * Only reserved words corresponding to infinite
3610 : * intervals are accepted.
3611 : */
3612 1026 : if (uval != DTK_LATE && uval != DTK_EARLY)
3613 36 : return DTERR_BAD_FORMAT;
3614 :
3615 : /*
3616 : * Infinity cannot be followed by anything else. We
3617 : * could allow "ago" to reverse the sign of infinity
3618 : * but using signed infinity is more intuitive.
3619 : */
3620 990 : if (i != nf - 1)
3621 12 : return DTERR_BAD_FORMAT;
3622 :
3623 978 : *dtype = uval;
3624 978 : break;
3625 :
3626 36 : default:
3627 36 : return DTERR_BAD_FORMAT;
3628 : }
3629 12100 : break;
3630 :
3631 0 : default:
3632 0 : return DTERR_BAD_FORMAT;
3633 : }
3634 :
3635 25124 : if (tmask & fmask)
3636 96 : return DTERR_BAD_FORMAT;
3637 25028 : fmask |= tmask;
3638 : }
3639 :
3640 : /* ensure that at least one time field has been found */
3641 10596 : if (fmask == 0)
3642 6 : return DTERR_BAD_FORMAT;
3643 :
3644 : /* reject if unit appeared and was never handled */
3645 10590 : if (parsing_unit_val)
3646 6 : return DTERR_BAD_FORMAT;
3647 :
3648 : /* finally, AGO negates everything */
3649 10584 : if (is_before)
3650 : {
3651 42 : if (itm_in->tm_usec == PG_INT64_MIN ||
3652 30 : itm_in->tm_mday == INT_MIN ||
3653 24 : itm_in->tm_mon == INT_MIN ||
3654 18 : itm_in->tm_year == INT_MIN)
3655 24 : return DTERR_FIELD_OVERFLOW;
3656 :
3657 18 : itm_in->tm_usec = -itm_in->tm_usec;
3658 18 : itm_in->tm_mday = -itm_in->tm_mday;
3659 18 : itm_in->tm_mon = -itm_in->tm_mon;
3660 18 : itm_in->tm_year = -itm_in->tm_year;
3661 : }
3662 :
3663 10560 : return 0;
3664 : }
3665 :
3666 :
3667 : /*
3668 : * Helper functions to avoid duplicated code in DecodeISO8601Interval.
3669 : *
3670 : * Parse a decimal value and break it into integer and fractional parts.
3671 : * Set *endptr to end+1 of the parsed substring.
3672 : * Returns 0 or DTERR code.
3673 : */
3674 : static int
3675 954 : ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
3676 : {
3677 : double val;
3678 :
3679 : /*
3680 : * Historically this has accepted anything that strtod() would take,
3681 : * notably including "e" notation, so continue doing that. This is
3682 : * slightly annoying because the precision of double is less than that of
3683 : * int64, so we would lose accuracy for inputs larger than 2^53 or so.
3684 : * However, historically we rejected inputs outside the int32 range,
3685 : * making that concern moot. What we do now is reject abs(val) above
3686 : * 1.0e15 (a round number a bit less than 2^50), so that any accepted
3687 : * value will have an exact integer part, and thereby a fraction part with
3688 : * abs(*fpart) less than 1. In the absence of field complaints it doesn't
3689 : * seem worth working harder.
3690 : */
3691 954 : if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
3692 0 : return DTERR_BAD_FORMAT;
3693 954 : errno = 0;
3694 954 : val = strtod(str, endptr);
3695 : /* did we not see anything that looks like a double? */
3696 954 : if (*endptr == str || errno != 0)
3697 6 : return DTERR_BAD_FORMAT;
3698 : /* watch out for overflow, including infinities; reject NaN too */
3699 948 : if (isnan(val) || val < -1.0e15 || val > 1.0e15)
3700 0 : return DTERR_FIELD_OVERFLOW;
3701 : /* be very sure we truncate towards zero (cf dtrunc()) */
3702 948 : if (val >= 0)
3703 732 : *ipart = (int64) floor(val);
3704 : else
3705 216 : *ipart = (int64) -floor(-val);
3706 948 : *fpart = val - *ipart;
3707 : /* Callers expect this to hold */
3708 : Assert(*fpart > -1.0 && *fpart < 1.0);
3709 948 : return 0;
3710 : }
3711 :
3712 : /*
3713 : * Determine number of integral digits in a valid ISO 8601 number field
3714 : * (we should ignore sign and any fraction part)
3715 : */
3716 : static int
3717 66 : ISO8601IntegerWidth(char *fieldstart)
3718 : {
3719 : /* We might have had a leading '-' */
3720 66 : if (*fieldstart == '-')
3721 18 : fieldstart++;
3722 66 : return strspn(fieldstart, "0123456789");
3723 : }
3724 :
3725 :
3726 : /* DecodeISO8601Interval()
3727 : * Decode an ISO 8601 time interval of the "format with designators"
3728 : * (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
3729 : * Examples: P1D for 1 day
3730 : * PT1H for 1 hour
3731 : * P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
3732 : * P0002-06-07T01:30:00 the same value in alternative format
3733 : *
3734 : * Returns 0 if successful, DTERR code if bogus input detected.
3735 : * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
3736 : * ISO8601, otherwise this could cause unexpected error messages.
3737 : * dtype and itm_in are output parameters.
3738 : *
3739 : * A couple exceptions from the spec:
3740 : * - a week field ('W') may coexist with other units
3741 : * - allows decimals in fields other than the least significant unit.
3742 : */
3743 : int
3744 612 : DecodeISO8601Interval(char *str,
3745 : int *dtype, struct pg_itm_in *itm_in)
3746 : {
3747 612 : bool datepart = true;
3748 612 : bool havefield = false;
3749 :
3750 612 : *dtype = DTK_DELTA;
3751 612 : ClearPgItmIn(itm_in);
3752 :
3753 612 : if (strlen(str) < 2 || str[0] != 'P')
3754 228 : return DTERR_BAD_FORMAT;
3755 :
3756 384 : str++;
3757 1122 : while (*str)
3758 : {
3759 : char *fieldstart;
3760 : int64 val;
3761 : double fval;
3762 : char unit;
3763 : int dterr;
3764 :
3765 1014 : if (*str == 'T') /* T indicates the beginning of the time part */
3766 : {
3767 198 : datepart = false;
3768 198 : havefield = false;
3769 198 : str++;
3770 240 : continue;
3771 : }
3772 :
3773 816 : fieldstart = str;
3774 816 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3775 816 : if (dterr)
3776 276 : return dterr;
3777 :
3778 : /*
3779 : * Note: we could step off the end of the string here. Code below
3780 : * *must* exit the loop if unit == '\0'.
3781 : */
3782 810 : unit = *str++;
3783 :
3784 810 : if (datepart)
3785 : {
3786 468 : switch (unit) /* before T: Y M W D */
3787 : {
3788 84 : case 'Y':
3789 84 : if (!AdjustYears(val, 1, itm_in) ||
3790 84 : !AdjustFractYears(fval, 1, itm_in))
3791 12 : return DTERR_FIELD_OVERFLOW;
3792 72 : break;
3793 108 : case 'M':
3794 108 : if (!AdjustMonths(val, itm_in) ||
3795 96 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3796 24 : return DTERR_FIELD_OVERFLOW;
3797 84 : break;
3798 54 : case 'W':
3799 54 : if (!AdjustDays(val, 7, itm_in) ||
3800 42 : !AdjustFractDays(fval, 7, itm_in))
3801 24 : return DTERR_FIELD_OVERFLOW;
3802 30 : break;
3803 132 : case 'D':
3804 132 : if (!AdjustDays(val, 1, itm_in) ||
3805 96 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3806 36 : return DTERR_FIELD_OVERFLOW;
3807 96 : break;
3808 30 : case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
3809 : case '\0':
3810 30 : if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
3811 : {
3812 6 : if (!AdjustYears(val / 10000, 1, itm_in) ||
3813 6 : !AdjustMonths((val / 100) % 100, itm_in) ||
3814 6 : !AdjustDays(val % 100, 1, itm_in) ||
3815 6 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3816 0 : return DTERR_FIELD_OVERFLOW;
3817 6 : if (unit == '\0')
3818 0 : return 0;
3819 6 : datepart = false;
3820 6 : havefield = false;
3821 6 : continue;
3822 : }
3823 : /* Else fall through to extended alternative format */
3824 : /* FALLTHROUGH */
3825 : case '-': /* ISO 8601 4.4.3.3 Alternative Format,
3826 : * Extended */
3827 84 : if (havefield)
3828 0 : return DTERR_BAD_FORMAT;
3829 :
3830 84 : if (!AdjustYears(val, 1, itm_in) ||
3831 72 : !AdjustFractYears(fval, 1, itm_in))
3832 12 : return DTERR_FIELD_OVERFLOW;
3833 72 : if (unit == '\0')
3834 6 : return 0;
3835 66 : if (unit == 'T')
3836 : {
3837 6 : datepart = false;
3838 6 : havefield = false;
3839 6 : continue;
3840 : }
3841 :
3842 60 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3843 60 : if (dterr)
3844 0 : return dterr;
3845 60 : if (!AdjustMonths(val, itm_in) ||
3846 54 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3847 6 : return DTERR_FIELD_OVERFLOW;
3848 54 : if (*str == '\0')
3849 6 : return 0;
3850 48 : if (*str == 'T')
3851 : {
3852 6 : datepart = false;
3853 6 : havefield = false;
3854 6 : continue;
3855 : }
3856 42 : if (*str != '-')
3857 0 : return DTERR_BAD_FORMAT;
3858 42 : str++;
3859 :
3860 42 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3861 42 : if (dterr)
3862 0 : return dterr;
3863 42 : if (!AdjustDays(val, 1, itm_in) ||
3864 36 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3865 6 : return DTERR_FIELD_OVERFLOW;
3866 36 : if (*str == '\0')
3867 12 : return 0;
3868 24 : if (*str == 'T')
3869 : {
3870 24 : datepart = false;
3871 24 : havefield = false;
3872 24 : continue;
3873 : }
3874 0 : return DTERR_BAD_FORMAT;
3875 0 : default:
3876 : /* not a valid date unit suffix */
3877 0 : return DTERR_BAD_FORMAT;
3878 : }
3879 : }
3880 : else
3881 : {
3882 342 : switch (unit) /* after T: H M S */
3883 : {
3884 108 : case 'H':
3885 108 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3886 36 : return DTERR_FIELD_OVERFLOW;
3887 72 : break;
3888 60 : case 'M':
3889 60 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3890 0 : return DTERR_FIELD_OVERFLOW;
3891 60 : break;
3892 96 : case 'S':
3893 96 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3894 12 : return DTERR_FIELD_OVERFLOW;
3895 84 : break;
3896 36 : case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
3897 36 : if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
3898 : {
3899 6 : if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
3900 6 : !AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
3901 6 : !AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
3902 6 : !AdjustFractMicroseconds(fval, 1, itm_in))
3903 0 : return DTERR_FIELD_OVERFLOW;
3904 6 : return 0;
3905 : }
3906 : /* Else fall through to extended alternative format */
3907 : /* FALLTHROUGH */
3908 : case ':': /* ISO 8601 4.4.3.3 Alternative Format,
3909 : * Extended */
3910 72 : if (havefield)
3911 0 : return DTERR_BAD_FORMAT;
3912 :
3913 72 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3914 30 : return DTERR_FIELD_OVERFLOW;
3915 42 : if (unit == '\0')
3916 18 : return 0;
3917 :
3918 24 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3919 24 : if (dterr)
3920 0 : return dterr;
3921 24 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3922 6 : return DTERR_FIELD_OVERFLOW;
3923 18 : if (*str == '\0')
3924 6 : return 0;
3925 12 : if (*str != ':')
3926 0 : return DTERR_BAD_FORMAT;
3927 12 : str++;
3928 :
3929 12 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3930 12 : if (dterr)
3931 0 : return dterr;
3932 12 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3933 0 : return DTERR_FIELD_OVERFLOW;
3934 12 : if (*str == '\0')
3935 12 : return 0;
3936 0 : return DTERR_BAD_FORMAT;
3937 :
3938 0 : default:
3939 : /* not a valid time unit suffix */
3940 0 : return DTERR_BAD_FORMAT;
3941 : }
3942 : }
3943 :
3944 498 : havefield = true;
3945 : }
3946 :
3947 108 : return 0;
3948 : }
3949 :
3950 :
3951 : /* DecodeUnits()
3952 : * Decode text string using lookup table.
3953 : *
3954 : * This routine recognizes keywords associated with time interval units.
3955 : *
3956 : * Given string must be lowercased already.
3957 : *
3958 : * Implement a cache lookup since it is likely that dates
3959 : * will be related in format.
3960 : */
3961 : int
3962 65198 : DecodeUnits(int field, const char *lowtoken, int *val)
3963 : {
3964 : int type;
3965 : const datetkn *tp;
3966 :
3967 65198 : tp = deltacache[field];
3968 : /* use strncmp so that we match truncated tokens */
3969 65198 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3970 : {
3971 53258 : tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3972 : }
3973 65198 : if (tp == NULL)
3974 : {
3975 35008 : type = UNKNOWN_FIELD;
3976 35008 : *val = 0;
3977 : }
3978 : else
3979 : {
3980 30190 : deltacache[field] = tp;
3981 30190 : type = tp->type;
3982 30190 : *val = tp->value;
3983 : }
3984 :
3985 65198 : return type;
3986 : } /* DecodeUnits() */
3987 :
3988 : /*
3989 : * Report an error detected by one of the datetime input processing routines.
3990 : *
3991 : * dterr is the error code, and *extra contains any auxiliary info we need
3992 : * for the error report. extra can be NULL if not needed for the particular
3993 : * dterr value.
3994 : *
3995 : * str is the original input string, and datatype is the name of the datatype
3996 : * we were trying to accept. (For some DTERR codes, these are not used and
3997 : * can be NULL.)
3998 : *
3999 : * If escontext points to an ErrorSaveContext node, that is filled instead
4000 : * of throwing an error.
4001 : *
4002 : * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
4003 : * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
4004 : * separate SQLSTATE codes, so ...
4005 : */
4006 : void
4007 1680 : DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
4008 : const char *str, const char *datatype,
4009 : Node *escontext)
4010 : {
4011 1680 : switch (dterr)
4012 : {
4013 174 : case DTERR_FIELD_OVERFLOW:
4014 174 : errsave(escontext,
4015 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4016 : errmsg("date/time field value out of range: \"%s\"",
4017 : str)));
4018 24 : break;
4019 180 : case DTERR_MD_FIELD_OVERFLOW:
4020 : /* <nanny>same as above, but add hint about DateStyle</nanny> */
4021 180 : errsave(escontext,
4022 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4023 : errmsg("date/time field value out of range: \"%s\"",
4024 : str),
4025 : errhint("Perhaps you need a different \"datestyle\" setting.")));
4026 0 : break;
4027 720 : case DTERR_INTERVAL_OVERFLOW:
4028 720 : errsave(escontext,
4029 : (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
4030 : errmsg("interval field value out of range: \"%s\"",
4031 : str)));
4032 0 : break;
4033 12 : case DTERR_TZDISP_OVERFLOW:
4034 12 : errsave(escontext,
4035 : (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
4036 : errmsg("time zone displacement out of range: \"%s\"",
4037 : str)));
4038 0 : break;
4039 36 : case DTERR_BAD_TIMEZONE:
4040 36 : errsave(escontext,
4041 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4042 : errmsg("time zone \"%s\" not recognized",
4043 : extra->dtee_timezone)));
4044 24 : break;
4045 0 : case DTERR_BAD_ZONE_ABBREV:
4046 0 : errsave(escontext,
4047 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
4048 : errmsg("time zone \"%s\" not recognized",
4049 : extra->dtee_timezone),
4050 : errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4051 : extra->dtee_abbrev)));
4052 0 : break;
4053 558 : case DTERR_BAD_FORMAT:
4054 : default:
4055 558 : errsave(escontext,
4056 : (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
4057 : errmsg("invalid input syntax for type %s: \"%s\"",
4058 : datatype, str)));
4059 84 : break;
4060 : }
4061 132 : }
4062 :
4063 : /* datebsearch()
4064 : * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
4065 : * is WAY faster than the generic bsearch().
4066 : */
4067 : static const datetkn *
4068 72306 : datebsearch(const char *key, const datetkn *base, int nel)
4069 : {
4070 72306 : if (nel > 0)
4071 : {
4072 72306 : const datetkn *last = base + nel - 1,
4073 : *position;
4074 : int result;
4075 :
4076 458480 : while (last >= base)
4077 : {
4078 414720 : position = base + ((last - base) >> 1);
4079 : /* precheck the first character for a bit of extra speed */
4080 414720 : result = (int) key[0] - (int) position->token[0];
4081 414720 : if (result == 0)
4082 : {
4083 : /* use strncmp so that we match truncated tokens */
4084 100560 : result = strncmp(key, position->token, TOKMAXLEN);
4085 100560 : if (result == 0)
4086 28546 : return position;
4087 : }
4088 386174 : if (result < 0)
4089 195270 : last = position - 1;
4090 : else
4091 190904 : base = position + 1;
4092 : }
4093 : }
4094 43760 : return NULL;
4095 : }
4096 :
4097 : /* EncodeTimezone()
4098 : * Copies representation of a numeric timezone offset to str.
4099 : *
4100 : * Returns a pointer to the new end of string. No NUL terminator is put
4101 : * there; callers are responsible for NUL terminating str themselves.
4102 : */
4103 : static char *
4104 60702 : EncodeTimezone(char *str, int tz, int style)
4105 : {
4106 : int hour,
4107 : min,
4108 : sec;
4109 :
4110 60702 : sec = abs(tz);
4111 60702 : min = sec / SECS_PER_MINUTE;
4112 60702 : sec -= min * SECS_PER_MINUTE;
4113 60702 : hour = min / MINS_PER_HOUR;
4114 60702 : min -= hour * MINS_PER_HOUR;
4115 :
4116 : /* TZ is negated compared to sign we wish to display ... */
4117 60702 : *str++ = (tz <= 0 ? '+' : '-');
4118 :
4119 60702 : if (sec != 0)
4120 : {
4121 0 : str = pg_ultostr_zeropad(str, hour, 2);
4122 0 : *str++ = ':';
4123 0 : str = pg_ultostr_zeropad(str, min, 2);
4124 0 : *str++ = ':';
4125 0 : str = pg_ultostr_zeropad(str, sec, 2);
4126 : }
4127 60702 : else if (min != 0 || style == USE_XSD_DATES)
4128 : {
4129 324 : str = pg_ultostr_zeropad(str, hour, 2);
4130 324 : *str++ = ':';
4131 324 : str = pg_ultostr_zeropad(str, min, 2);
4132 : }
4133 : else
4134 60378 : str = pg_ultostr_zeropad(str, hour, 2);
4135 60702 : return str;
4136 : }
4137 :
4138 : /* EncodeDateOnly()
4139 : * Encode date as local time.
4140 : */
4141 : void
4142 8884 : EncodeDateOnly(struct pg_tm *tm, int style, char *str)
4143 : {
4144 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4145 :
4146 8884 : switch (style)
4147 : {
4148 4572 : case USE_ISO_DATES:
4149 : case USE_XSD_DATES:
4150 : /* compatible with ISO date formats */
4151 4572 : str = pg_ultostr_zeropad(str,
4152 4572 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4153 4572 : *str++ = '-';
4154 4572 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4155 4572 : *str++ = '-';
4156 4572 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4157 4572 : break;
4158 :
4159 0 : case USE_SQL_DATES:
4160 : /* compatible with Oracle/Ingres date formats */
4161 0 : if (DateOrder == DATEORDER_DMY)
4162 : {
4163 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4164 0 : *str++ = '/';
4165 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4166 : }
4167 : else
4168 : {
4169 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4170 0 : *str++ = '/';
4171 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4172 : }
4173 0 : *str++ = '/';
4174 0 : str = pg_ultostr_zeropad(str,
4175 0 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4176 0 : break;
4177 :
4178 8 : case USE_GERMAN_DATES:
4179 : /* German-style date format */
4180 8 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4181 8 : *str++ = '.';
4182 8 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4183 8 : *str++ = '.';
4184 8 : str = pg_ultostr_zeropad(str,
4185 8 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4186 8 : break;
4187 :
4188 4304 : case USE_POSTGRES_DATES:
4189 : default:
4190 : /* traditional date-only style for Postgres */
4191 4304 : if (DateOrder == DATEORDER_DMY)
4192 : {
4193 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4194 0 : *str++ = '-';
4195 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4196 : }
4197 : else
4198 : {
4199 4304 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4200 4304 : *str++ = '-';
4201 4304 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4202 : }
4203 4304 : *str++ = '-';
4204 4304 : str = pg_ultostr_zeropad(str,
4205 4304 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4206 4304 : break;
4207 : }
4208 :
4209 8884 : if (tm->tm_year <= 0)
4210 : {
4211 80 : memcpy(str, " BC", 3); /* Don't copy NUL */
4212 80 : str += 3;
4213 : }
4214 8884 : *str = '\0';
4215 8884 : }
4216 :
4217 :
4218 : /* EncodeTimeOnly()
4219 : * Encode time fields only.
4220 : *
4221 : * tm and fsec are the value to encode, print_tz determines whether to include
4222 : * a time zone (the difference between time and timetz types), tz is the
4223 : * numeric time zone offset, style is the date style, str is where to write the
4224 : * output.
4225 : */
4226 : void
4227 12000 : EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
4228 : {
4229 12000 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4230 12000 : *str++ = ':';
4231 12000 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4232 12000 : *str++ = ':';
4233 12000 : str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
4234 12000 : if (print_tz)
4235 6428 : str = EncodeTimezone(str, tz, style);
4236 12000 : *str = '\0';
4237 12000 : }
4238 :
4239 :
4240 : /* EncodeDateTime()
4241 : * Encode date and time interpreted as local time.
4242 : *
4243 : * tm and fsec are the value to encode, print_tz determines whether to include
4244 : * a time zone (the difference between timestamp and timestamptz types), tz is
4245 : * the numeric time zone offset, tzn is the textual time zone, which if
4246 : * specified will be used instead of tz by some styles, style is the date
4247 : * style, str is where to write the output.
4248 : *
4249 : * Supported date styles:
4250 : * Postgres - day mon hh:mm:ss yyyy tz
4251 : * SQL - mm/dd/yyyy hh:mm:ss.ss tz
4252 : * ISO - yyyy-mm-dd hh:mm:ss+/-tz
4253 : * German - dd.mm.yyyy hh:mm:ss tz
4254 : * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
4255 : */
4256 : void
4257 116020 : EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
4258 : {
4259 : int day;
4260 :
4261 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4262 :
4263 : /*
4264 : * Negative tm_isdst means we have no valid time zone translation.
4265 : */
4266 116020 : if (tm->tm_isdst < 0)
4267 43378 : print_tz = false;
4268 :
4269 116020 : switch (style)
4270 : {
4271 86740 : case USE_ISO_DATES:
4272 : case USE_XSD_DATES:
4273 : /* Compatible with ISO-8601 date formats */
4274 86740 : str = pg_ultostr_zeropad(str,
4275 86740 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4276 86740 : *str++ = '-';
4277 86740 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4278 86740 : *str++ = '-';
4279 86740 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4280 86740 : *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
4281 86740 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4282 86740 : *str++ = ':';
4283 86740 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4284 86740 : *str++ = ':';
4285 86740 : str = AppendTimestampSeconds(str, tm, fsec);
4286 86740 : if (print_tz)
4287 54274 : str = EncodeTimezone(str, tz, style);
4288 86740 : break;
4289 :
4290 780 : case USE_SQL_DATES:
4291 : /* Compatible with Oracle/Ingres date formats */
4292 780 : if (DateOrder == DATEORDER_DMY)
4293 : {
4294 384 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4295 384 : *str++ = '/';
4296 384 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4297 : }
4298 : else
4299 : {
4300 396 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4301 396 : *str++ = '/';
4302 396 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4303 : }
4304 780 : *str++ = '/';
4305 780 : str = pg_ultostr_zeropad(str,
4306 780 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4307 780 : *str++ = ' ';
4308 780 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4309 780 : *str++ = ':';
4310 780 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4311 780 : *str++ = ':';
4312 780 : str = AppendTimestampSeconds(str, tm, fsec);
4313 :
4314 : /*
4315 : * Note: the uses of %.*s in this function would be risky if the
4316 : * timezone names ever contain non-ASCII characters, since we are
4317 : * not being careful to do encoding-aware clipping. However, all
4318 : * TZ abbreviations in the IANA database are plain ASCII.
4319 : */
4320 780 : if (print_tz)
4321 : {
4322 18 : if (tzn)
4323 : {
4324 18 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4325 18 : str += strlen(str);
4326 : }
4327 : else
4328 0 : str = EncodeTimezone(str, tz, style);
4329 : }
4330 780 : break;
4331 :
4332 24 : case USE_GERMAN_DATES:
4333 : /* German variant on European style */
4334 24 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4335 24 : *str++ = '.';
4336 24 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4337 24 : *str++ = '.';
4338 24 : str = pg_ultostr_zeropad(str,
4339 24 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4340 24 : *str++ = ' ';
4341 24 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4342 24 : *str++ = ':';
4343 24 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4344 24 : *str++ = ':';
4345 24 : str = AppendTimestampSeconds(str, tm, fsec);
4346 :
4347 24 : if (print_tz)
4348 : {
4349 24 : if (tzn)
4350 : {
4351 24 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4352 24 : str += strlen(str);
4353 : }
4354 : else
4355 0 : str = EncodeTimezone(str, tz, style);
4356 : }
4357 24 : break;
4358 :
4359 28476 : case USE_POSTGRES_DATES:
4360 : default:
4361 : /* Backward-compatible with traditional Postgres abstime dates */
4362 28476 : day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4363 28476 : tm->tm_wday = j2day(day);
4364 28476 : memcpy(str, days[tm->tm_wday], 3);
4365 28476 : str += 3;
4366 28476 : *str++ = ' ';
4367 28476 : if (DateOrder == DATEORDER_DMY)
4368 : {
4369 398 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4370 398 : *str++ = ' ';
4371 398 : memcpy(str, months[tm->tm_mon - 1], 3);
4372 398 : str += 3;
4373 : }
4374 : else
4375 : {
4376 28078 : memcpy(str, months[tm->tm_mon - 1], 3);
4377 28078 : str += 3;
4378 28078 : *str++ = ' ';
4379 28078 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4380 : }
4381 28476 : *str++ = ' ';
4382 28476 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4383 28476 : *str++ = ':';
4384 28476 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4385 28476 : *str++ = ':';
4386 28476 : str = AppendTimestampSeconds(str, tm, fsec);
4387 28476 : *str++ = ' ';
4388 28476 : str = pg_ultostr_zeropad(str,
4389 28476 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4390 :
4391 28476 : if (print_tz)
4392 : {
4393 18326 : if (tzn)
4394 : {
4395 18326 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4396 18326 : str += strlen(str);
4397 : }
4398 : else
4399 : {
4400 : /*
4401 : * We have a time zone, but no string version. Use the
4402 : * numeric form, but be sure to include a leading space to
4403 : * avoid formatting something which would be rejected by
4404 : * the date/time parser later. - thomas 2001-10-19
4405 : */
4406 0 : *str++ = ' ';
4407 0 : str = EncodeTimezone(str, tz, style);
4408 : }
4409 : }
4410 28476 : break;
4411 : }
4412 :
4413 116020 : if (tm->tm_year <= 0)
4414 : {
4415 274 : memcpy(str, " BC", 3); /* Don't copy NUL */
4416 274 : str += 3;
4417 : }
4418 116020 : *str = '\0';
4419 116020 : }
4420 :
4421 :
4422 : /*
4423 : * Helper functions to avoid duplicated code in EncodeInterval.
4424 : */
4425 :
4426 : /* Append an ISO-8601-style interval field, but only if value isn't zero */
4427 : static char *
4428 210 : AddISO8601IntPart(char *cp, int64 value, char units)
4429 : {
4430 210 : if (value == 0)
4431 54 : return cp;
4432 156 : sprintf(cp, "%lld%c", (long long) value, units);
4433 156 : return cp + strlen(cp);
4434 : }
4435 :
4436 : /* Append a postgres-style interval field, but only if value isn't zero */
4437 : static char *
4438 14436 : AddPostgresIntPart(char *cp, int64 value, const char *units,
4439 : bool *is_zero, bool *is_before)
4440 : {
4441 14436 : if (value == 0)
4442 7998 : return cp;
4443 19314 : sprintf(cp, "%s%s%lld %s%s",
4444 6438 : (!*is_zero) ? " " : "",
4445 6438 : (*is_before && value > 0) ? "+" : "",
4446 : (long long) value,
4447 : units,
4448 : (value != 1) ? "s" : "");
4449 :
4450 : /*
4451 : * Each nonzero field sets is_before for (only) the next one. This is a
4452 : * tad bizarre but it's how it worked before...
4453 : */
4454 6438 : *is_before = (value < 0);
4455 6438 : *is_zero = false;
4456 6438 : return cp + strlen(cp);
4457 : }
4458 :
4459 : /* Append a verbose-style interval field, but only if value isn't zero */
4460 : static char *
4461 42190 : AddVerboseIntPart(char *cp, int64 value, const char *units,
4462 : bool *is_zero, bool *is_before)
4463 : {
4464 42190 : if (value == 0)
4465 27980 : return cp;
4466 : /* first nonzero value sets is_before */
4467 14210 : if (*is_zero)
4468 : {
4469 7820 : *is_before = (value < 0);
4470 7820 : value = i64abs(value);
4471 : }
4472 6390 : else if (*is_before)
4473 1332 : value = -value;
4474 14210 : sprintf(cp, " %lld %s%s", (long long) value, units, (value == 1) ? "" : "s");
4475 14210 : *is_zero = false;
4476 14210 : return cp + strlen(cp);
4477 : }
4478 :
4479 :
4480 : /* EncodeInterval()
4481 : * Interpret time structure as a delta time and convert to string.
4482 : *
4483 : * Support "traditional Postgres" and ISO-8601 styles.
4484 : * Actually, afaik ISO does not address time interval formatting,
4485 : * but this looks similar to the spec for absolute date/time.
4486 : * - thomas 1998-04-30
4487 : *
4488 : * Actually, afaik, ISO 8601 does specify formats for "time
4489 : * intervals...[of the]...format with time-unit designators", which
4490 : * are pretty ugly. The format looks something like
4491 : * P1Y1M1DT1H1M1.12345S
4492 : * but useful for exchanging data with computers instead of humans.
4493 : * - ron 2003-07-14
4494 : *
4495 : * And ISO's SQL 2008 standard specifies standards for
4496 : * "year-month literal"s (that look like '2-3') and
4497 : * "day-time literal"s (that look like ('4 5:6:7')
4498 : */
4499 : void
4500 13424 : EncodeInterval(struct pg_itm *itm, int style, char *str)
4501 : {
4502 13424 : char *cp = str;
4503 13424 : int year = itm->tm_year;
4504 13424 : int mon = itm->tm_mon;
4505 13424 : int64 mday = itm->tm_mday; /* tm_mday could be INT_MIN */
4506 13424 : int64 hour = itm->tm_hour;
4507 13424 : int min = itm->tm_min;
4508 13424 : int sec = itm->tm_sec;
4509 13424 : int fsec = itm->tm_usec;
4510 13424 : bool is_before = false;
4511 13424 : bool is_zero = true;
4512 :
4513 : /*
4514 : * The sign of year and month are guaranteed to match, since they are
4515 : * stored internally as "month". But we'll need to check for is_before and
4516 : * is_zero when determining the signs of day and hour/minute/seconds
4517 : * fields.
4518 : */
4519 13424 : switch (style)
4520 : {
4521 : /* SQL Standard interval format */
4522 126 : case INTSTYLE_SQL_STANDARD:
4523 : {
4524 96 : bool has_negative = year < 0 || mon < 0 ||
4525 66 : mday < 0 || hour < 0 ||
4526 222 : min < 0 || sec < 0 || fsec < 0;
4527 102 : bool has_positive = year > 0 || mon > 0 ||
4528 66 : mday > 0 || hour > 0 ||
4529 228 : min > 0 || sec > 0 || fsec > 0;
4530 126 : bool has_year_month = year != 0 || mon != 0;
4531 42 : bool has_day_time = mday != 0 || hour != 0 ||
4532 168 : min != 0 || sec != 0 || fsec != 0;
4533 126 : bool has_day = mday != 0;
4534 222 : bool sql_standard_value = !(has_negative && has_positive) &&
4535 96 : !(has_year_month && has_day_time);
4536 :
4537 : /*
4538 : * SQL Standard wants only 1 "<sign>" preceding the whole
4539 : * interval ... but can't do that if mixed signs.
4540 : */
4541 126 : if (has_negative && sql_standard_value)
4542 : {
4543 30 : *cp++ = '-';
4544 30 : year = -year;
4545 30 : mon = -mon;
4546 30 : mday = -mday;
4547 30 : hour = -hour;
4548 30 : min = -min;
4549 30 : sec = -sec;
4550 30 : fsec = -fsec;
4551 : }
4552 :
4553 126 : if (!has_negative && !has_positive)
4554 : {
4555 12 : sprintf(cp, "0");
4556 : }
4557 114 : else if (!sql_standard_value)
4558 : {
4559 : /*
4560 : * For non sql-standard interval values, force outputting
4561 : * the signs to avoid ambiguities with intervals with
4562 : * mixed sign components.
4563 : */
4564 54 : char year_sign = (year < 0 || mon < 0) ? '-' : '+';
4565 54 : char day_sign = (mday < 0) ? '-' : '+';
4566 78 : char sec_sign = (hour < 0 || min < 0 ||
4567 24 : sec < 0 || fsec < 0) ? '-' : '+';
4568 :
4569 54 : sprintf(cp, "%c%d-%d %c%lld %c%lld:%02d:",
4570 : year_sign, abs(year), abs(mon),
4571 54 : day_sign, (long long) i64abs(mday),
4572 54 : sec_sign, (long long) i64abs(hour), abs(min));
4573 54 : cp += strlen(cp);
4574 54 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4575 54 : *cp = '\0';
4576 : }
4577 60 : else if (has_year_month)
4578 : {
4579 18 : sprintf(cp, "%d-%d", year, mon);
4580 : }
4581 42 : else if (has_day)
4582 : {
4583 30 : sprintf(cp, "%lld %lld:%02d:",
4584 : (long long) mday, (long long) hour, min);
4585 30 : cp += strlen(cp);
4586 30 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4587 30 : *cp = '\0';
4588 : }
4589 : else
4590 : {
4591 12 : sprintf(cp, "%lld:%02d:", (long long) hour, min);
4592 12 : cp += strlen(cp);
4593 12 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4594 12 : *cp = '\0';
4595 : }
4596 : }
4597 126 : break;
4598 :
4599 : /* ISO 8601 "time-intervals by duration only" */
4600 48 : case INTSTYLE_ISO_8601:
4601 : /* special-case zero to avoid printing nothing */
4602 48 : if (year == 0 && mon == 0 && mday == 0 &&
4603 6 : hour == 0 && min == 0 && sec == 0 && fsec == 0)
4604 : {
4605 6 : sprintf(cp, "PT0S");
4606 6 : break;
4607 : }
4608 42 : *cp++ = 'P';
4609 42 : cp = AddISO8601IntPart(cp, year, 'Y');
4610 42 : cp = AddISO8601IntPart(cp, mon, 'M');
4611 42 : cp = AddISO8601IntPart(cp, mday, 'D');
4612 42 : if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
4613 36 : *cp++ = 'T';
4614 42 : cp = AddISO8601IntPart(cp, hour, 'H');
4615 42 : cp = AddISO8601IntPart(cp, min, 'M');
4616 42 : if (sec != 0 || fsec != 0)
4617 : {
4618 36 : if (sec < 0 || fsec < 0)
4619 12 : *cp++ = '-';
4620 36 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4621 36 : *cp++ = 'S';
4622 36 : *cp++ = '\0';
4623 : }
4624 42 : break;
4625 :
4626 : /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
4627 4812 : case INTSTYLE_POSTGRES:
4628 4812 : cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
4629 :
4630 : /*
4631 : * Ideally we should spell out "month" like we do for "year" and
4632 : * "day". However, for backward compatibility, we can't easily
4633 : * fix this. bjm 2011-05-24
4634 : */
4635 4812 : cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
4636 4812 : cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
4637 4812 : if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
4638 : {
4639 3868 : bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
4640 :
4641 7736 : sprintf(cp, "%s%s%02lld:%02d:",
4642 3868 : is_zero ? "" : " ",
4643 3654 : (minus ? "-" : (is_before ? "+" : "")),
4644 3868 : (long long) i64abs(hour), abs(min));
4645 3868 : cp += strlen(cp);
4646 3868 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4647 3868 : *cp = '\0';
4648 : }
4649 4812 : break;
4650 :
4651 : /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
4652 8438 : case INTSTYLE_POSTGRES_VERBOSE:
4653 : default:
4654 8438 : strcpy(cp, "@");
4655 8438 : cp++;
4656 8438 : cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
4657 8438 : cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
4658 8438 : cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
4659 8438 : cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
4660 8438 : cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
4661 8438 : if (sec != 0 || fsec != 0)
4662 : {
4663 3264 : *cp++ = ' ';
4664 3264 : if (sec < 0 || (sec == 0 && fsec < 0))
4665 : {
4666 1008 : if (is_zero)
4667 342 : is_before = true;
4668 666 : else if (!is_before)
4669 6 : *cp++ = '-';
4670 : }
4671 2256 : else if (is_before)
4672 12 : *cp++ = '-';
4673 3264 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4674 : /* We output "ago", not negatives, so use abs(). */
4675 3264 : sprintf(cp, " sec%s",
4676 3264 : (abs(sec) != 1 || fsec != 0) ? "s" : "");
4677 3264 : is_zero = false;
4678 : }
4679 : /* identically zero? then put in a unitless zero... */
4680 8438 : if (is_zero)
4681 230 : strcat(cp, " 0");
4682 8438 : if (is_before)
4683 1354 : strcat(cp, " ago");
4684 8438 : break;
4685 : }
4686 13424 : }
4687 :
4688 :
4689 : /*
4690 : * We've been burnt by stupid errors in the ordering of the datetkn tables
4691 : * once too often. Arrange to check them during postmaster start.
4692 : */
4693 : static bool
4694 2584 : CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
4695 : {
4696 2584 : bool ok = true;
4697 : int i;
4698 :
4699 174420 : for (i = 0; i < nel; i++)
4700 : {
4701 : /* check for token strings that don't fit */
4702 171836 : if (strlen(base[i].token) > TOKMAXLEN)
4703 : {
4704 : /* %.*s is safe since all our tokens are ASCII */
4705 0 : elog(LOG, "token too long in %s table: \"%.*s\"",
4706 : tablename,
4707 : TOKMAXLEN + 1, base[i].token);
4708 0 : ok = false;
4709 0 : break; /* don't risk applying strcmp */
4710 : }
4711 : /* check for out of order */
4712 171836 : if (i > 0 &&
4713 169252 : strcmp(base[i - 1].token, base[i].token) >= 0)
4714 : {
4715 0 : elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4716 : tablename,
4717 : base[i - 1].token,
4718 : base[i].token);
4719 0 : ok = false;
4720 : }
4721 : }
4722 2584 : return ok;
4723 : }
4724 :
4725 : bool
4726 1292 : CheckDateTokenTables(void)
4727 : {
4728 1292 : bool ok = true;
4729 :
4730 : Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
4731 : Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
4732 :
4733 1292 : ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
4734 1292 : ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4735 1292 : return ok;
4736 : }
4737 :
4738 : /*
4739 : * Common code for temporal prosupport functions: simplify, if possible,
4740 : * a call to a temporal type's length-coercion function.
4741 : *
4742 : * Types time, timetz, timestamp and timestamptz each have a range of allowed
4743 : * precisions. An unspecified precision is rigorously equivalent to the
4744 : * highest specifiable precision. We can replace the function call with a
4745 : * no-op RelabelType if it is coercing to the same or higher precision as the
4746 : * input is known to have.
4747 : *
4748 : * The input Node is always a FuncExpr, but to reduce the #include footprint
4749 : * of datetime.h, we declare it as Node *.
4750 : *
4751 : * Note: timestamp_scale throws an error when the typmod is out of range, but
4752 : * we can't get there from a cast: our typmodin will have caught it already.
4753 : */
4754 : Node *
4755 24 : TemporalSimplify(int32 max_precis, Node *node)
4756 : {
4757 24 : FuncExpr *expr = castNode(FuncExpr, node);
4758 24 : Node *ret = NULL;
4759 : Node *typmod;
4760 :
4761 : Assert(list_length(expr->args) >= 2);
4762 :
4763 24 : typmod = (Node *) lsecond(expr->args);
4764 :
4765 24 : if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
4766 : {
4767 24 : Node *source = (Node *) linitial(expr->args);
4768 24 : int32 old_precis = exprTypmod(source);
4769 24 : int32 new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
4770 :
4771 24 : if (new_precis < 0 || new_precis == max_precis ||
4772 0 : (old_precis >= 0 && new_precis >= old_precis))
4773 0 : ret = relabel_to_typmod(source, new_precis);
4774 : }
4775 :
4776 24 : return ret;
4777 : }
4778 :
4779 : /*
4780 : * This function gets called during timezone config file load or reload
4781 : * to create the final array of timezone tokens. The argument array
4782 : * is already sorted in name order.
4783 : *
4784 : * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
4785 : * chunk) or NULL on alloc failure. No other error conditions are defined.
4786 : */
4787 : TimeZoneAbbrevTable *
4788 11316 : ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
4789 : {
4790 : TimeZoneAbbrevTable *tbl;
4791 : Size tbl_size;
4792 : int i;
4793 :
4794 : /* Space for fixed fields and datetkn array */
4795 11316 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4796 11316 : n * sizeof(datetkn);
4797 11316 : tbl_size = MAXALIGN(tbl_size);
4798 : /* Count up space for dynamic abbreviations */
4799 2217948 : for (i = 0; i < n; i++)
4800 : {
4801 2206632 : struct tzEntry *abbr = abbrevs + i;
4802 :
4803 2206632 : if (abbr->zone != NULL)
4804 : {
4805 : Size dsize;
4806 :
4807 565794 : dsize = offsetof(DynamicZoneAbbrev, zone) +
4808 565794 : strlen(abbr->zone) + 1;
4809 565794 : tbl_size += MAXALIGN(dsize);
4810 : }
4811 : }
4812 :
4813 : /* Alloc the result ... */
4814 11316 : tbl = guc_malloc(LOG, tbl_size);
4815 11316 : if (!tbl)
4816 0 : return NULL;
4817 :
4818 : /* ... and fill it in */
4819 11316 : tbl->tblsize = tbl_size;
4820 11316 : tbl->numabbrevs = n;
4821 : /* in this loop, tbl_size reprises the space calculation above */
4822 11316 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4823 11316 : n * sizeof(datetkn);
4824 11316 : tbl_size = MAXALIGN(tbl_size);
4825 2217948 : for (i = 0; i < n; i++)
4826 : {
4827 2206632 : struct tzEntry *abbr = abbrevs + i;
4828 2206632 : datetkn *dtoken = tbl->abbrevs + i;
4829 :
4830 : /* use strlcpy to truncate name if necessary */
4831 2206632 : strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
4832 2206632 : if (abbr->zone != NULL)
4833 : {
4834 : /* Allocate a DynamicZoneAbbrev for this abbreviation */
4835 : DynamicZoneAbbrev *dtza;
4836 : Size dsize;
4837 :
4838 565794 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
4839 565794 : dtza->tz = NULL;
4840 565794 : strcpy(dtza->zone, abbr->zone);
4841 :
4842 565794 : dtoken->type = DYNTZ;
4843 : /* value is offset from table start to DynamicZoneAbbrev */
4844 565794 : dtoken->value = (int32) tbl_size;
4845 :
4846 565794 : dsize = offsetof(DynamicZoneAbbrev, zone) +
4847 565794 : strlen(abbr->zone) + 1;
4848 565794 : tbl_size += MAXALIGN(dsize);
4849 : }
4850 : else
4851 : {
4852 1640838 : dtoken->type = abbr->is_dst ? DTZ : TZ;
4853 1640838 : dtoken->value = abbr->offset;
4854 : }
4855 : }
4856 :
4857 : /* Assert the two loops above agreed on size calculations */
4858 : Assert(tbl->tblsize == tbl_size);
4859 :
4860 : /* Check the ordering, if testing */
4861 : Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
4862 :
4863 11316 : return tbl;
4864 : }
4865 :
4866 : /*
4867 : * Install a TimeZoneAbbrevTable as the active table.
4868 : *
4869 : * Caller is responsible that the passed table doesn't go away while in use.
4870 : */
4871 : void
4872 11142 : InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
4873 : {
4874 11142 : zoneabbrevtbl = tbl;
4875 : /* reset abbrevcache, which may contain pointers into old table */
4876 11142 : memset(abbrevcache, 0, sizeof(abbrevcache));
4877 11142 : }
4878 :
4879 : /*
4880 : * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
4881 : *
4882 : * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
4883 : */
4884 : static pg_tz *
4885 1158 : FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
4886 : DateTimeErrorExtra *extra)
4887 : {
4888 : DynamicZoneAbbrev *dtza;
4889 :
4890 : /* Just some sanity checks to prevent indexing off into nowhere */
4891 : Assert(tp->type == DYNTZ);
4892 : Assert(tp->value > 0 && tp->value < tbl->tblsize);
4893 :
4894 1158 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
4895 :
4896 : /* Look up the underlying zone if we haven't already */
4897 1158 : if (dtza->tz == NULL)
4898 : {
4899 912 : dtza->tz = pg_tzset(dtza->zone);
4900 912 : if (dtza->tz == NULL)
4901 : {
4902 : /* Ooops, bogus zone name in config file entry */
4903 0 : extra->dtee_timezone = dtza->zone;
4904 0 : extra->dtee_abbrev = tp->token;
4905 : }
4906 : }
4907 1158 : return dtza->tz;
4908 : }
4909 :
4910 :
4911 : /*
4912 : * This set-returning function reads all the available time zone abbreviations
4913 : * and returns a set of (abbrev, utc_offset, is_dst).
4914 : */
4915 : Datum
4916 3540 : pg_timezone_abbrevs(PG_FUNCTION_ARGS)
4917 : {
4918 : FuncCallContext *funcctx;
4919 : int *pindex;
4920 : Datum result;
4921 : HeapTuple tuple;
4922 : Datum values[3];
4923 3540 : bool nulls[3] = {0};
4924 : const datetkn *tp;
4925 : char buffer[TOKMAXLEN + 1];
4926 : int gmtoffset;
4927 : bool is_dst;
4928 : unsigned char *p;
4929 : struct pg_itm_in itm_in;
4930 : Interval *resInterval;
4931 :
4932 : /* stuff done only on the first call of the function */
4933 3540 : if (SRF_IS_FIRSTCALL())
4934 : {
4935 : TupleDesc tupdesc;
4936 : MemoryContext oldcontext;
4937 :
4938 : /* create a function context for cross-call persistence */
4939 18 : funcctx = SRF_FIRSTCALL_INIT();
4940 :
4941 : /*
4942 : * switch to memory context appropriate for multiple function calls
4943 : */
4944 18 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4945 :
4946 : /* allocate memory for user context */
4947 18 : pindex = (int *) palloc(sizeof(int));
4948 18 : *pindex = 0;
4949 18 : funcctx->user_fctx = (void *) pindex;
4950 :
4951 18 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
4952 0 : elog(ERROR, "return type must be a row type");
4953 18 : funcctx->tuple_desc = tupdesc;
4954 :
4955 18 : MemoryContextSwitchTo(oldcontext);
4956 : }
4957 :
4958 : /* stuff done on every call of the function */
4959 3540 : funcctx = SRF_PERCALL_SETUP();
4960 3540 : pindex = (int *) funcctx->user_fctx;
4961 :
4962 3540 : if (zoneabbrevtbl == NULL ||
4963 3540 : *pindex >= zoneabbrevtbl->numabbrevs)
4964 18 : SRF_RETURN_DONE(funcctx);
4965 :
4966 3522 : tp = zoneabbrevtbl->abbrevs + *pindex;
4967 :
4968 3522 : switch (tp->type)
4969 : {
4970 1764 : case TZ:
4971 1764 : gmtoffset = tp->value;
4972 1764 : is_dst = false;
4973 1764 : break;
4974 864 : case DTZ:
4975 864 : gmtoffset = tp->value;
4976 864 : is_dst = true;
4977 864 : break;
4978 894 : case DYNTZ:
4979 : {
4980 : /* Determine the current meaning of the abbrev */
4981 : pg_tz *tzp;
4982 : DateTimeErrorExtra extra;
4983 : TimestampTz now;
4984 : int isdst;
4985 :
4986 894 : tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
4987 894 : if (tzp == NULL)
4988 0 : DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
4989 : NULL, NULL, NULL);
4990 894 : now = GetCurrentTransactionStartTimestamp();
4991 1788 : gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
4992 894 : tp->token,
4993 : tzp,
4994 : &isdst);
4995 894 : is_dst = (bool) isdst;
4996 894 : break;
4997 : }
4998 0 : default:
4999 0 : elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
5000 : gmtoffset = 0; /* keep compiler quiet */
5001 : is_dst = false;
5002 : break;
5003 : }
5004 :
5005 : /*
5006 : * Convert name to text, using upcasing conversion that is the inverse of
5007 : * what ParseDateTime() uses.
5008 : */
5009 3522 : strlcpy(buffer, tp->token, sizeof(buffer));
5010 16320 : for (p = (unsigned char *) buffer; *p; p++)
5011 12798 : *p = pg_toupper(*p);
5012 :
5013 3522 : values[0] = CStringGetTextDatum(buffer);
5014 :
5015 : /* Convert offset (in seconds) to an interval; can't overflow */
5016 14088 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5017 3522 : itm_in.tm_usec = (int64) gmtoffset * USECS_PER_SEC;
5018 3522 : resInterval = (Interval *) palloc(sizeof(Interval));
5019 3522 : (void) itmin2interval(&itm_in, resInterval);
5020 3522 : values[1] = IntervalPGetDatum(resInterval);
5021 :
5022 3522 : values[2] = BoolGetDatum(is_dst);
5023 :
5024 3522 : (*pindex)++;
5025 :
5026 3522 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
5027 3522 : result = HeapTupleGetDatum(tuple);
5028 :
5029 3522 : SRF_RETURN_NEXT(funcctx, result);
5030 : }
5031 :
5032 : /*
5033 : * This set-returning function reads all the available full time zones
5034 : * and returns a set of (name, abbrev, utc_offset, is_dst).
5035 : */
5036 : Datum
5037 16 : pg_timezone_names(PG_FUNCTION_ARGS)
5038 : {
5039 16 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
5040 : pg_tzenum *tzenum;
5041 : pg_tz *tz;
5042 : Datum values[4];
5043 16 : bool nulls[4] = {0};
5044 : int tzoff;
5045 : struct pg_tm tm;
5046 : fsec_t fsec;
5047 : const char *tzn;
5048 : Interval *resInterval;
5049 : struct pg_itm_in itm_in;
5050 :
5051 16 : InitMaterializedSRF(fcinfo, 0);
5052 :
5053 : /* initialize timezone scanning code */
5054 16 : tzenum = pg_tzenumerate_start();
5055 :
5056 : /* search for another zone to display */
5057 : for (;;)
5058 : {
5059 9568 : tz = pg_tzenumerate_next(tzenum);
5060 9568 : if (!tz)
5061 16 : break;
5062 :
5063 : /* Convert now() to local time in this zone */
5064 9552 : if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
5065 : &tzoff, &tm, &fsec, &tzn, tz) != 0)
5066 0 : continue; /* ignore if conversion fails */
5067 :
5068 : /*
5069 : * IANA's rather silly "Factory" time zone used to emit ridiculously
5070 : * long "abbreviations" such as "Local time zone must be set--see zic
5071 : * manual page" or "Local time zone must be set--use tzsetup". While
5072 : * modern versions of tzdb emit the much saner "-00", it seems some
5073 : * benighted packagers are hacking the IANA data so that it continues
5074 : * to produce these strings. To prevent producing a weirdly wide
5075 : * abbrev column, reject ridiculously long abbreviations.
5076 : */
5077 9552 : if (tzn && strlen(tzn) > 31)
5078 0 : continue;
5079 :
5080 9552 : values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
5081 9552 : values[1] = CStringGetTextDatum(tzn ? tzn : "");
5082 :
5083 : /* Convert tzoff to an interval; can't overflow */
5084 38208 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5085 9552 : itm_in.tm_usec = (int64) -tzoff * USECS_PER_SEC;
5086 9552 : resInterval = (Interval *) palloc(sizeof(Interval));
5087 9552 : (void) itmin2interval(&itm_in, resInterval);
5088 9552 : values[2] = IntervalPGetDatum(resInterval);
5089 :
5090 9552 : values[3] = BoolGetDatum(tm.tm_isdst > 0);
5091 :
5092 9552 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
5093 : }
5094 :
5095 16 : pg_tzenumerate_end(tzenum);
5096 16 : return (Datum) 0;
5097 : }
|