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 331324 : date2j(int year, int month, int day)
289 : {
290 : int julian;
291 : int century;
292 :
293 331324 : if (month > 2)
294 : {
295 180072 : month += 1;
296 180072 : year += 4800;
297 : }
298 : else
299 : {
300 151252 : month += 13;
301 151252 : year += 4799;
302 : }
303 :
304 331324 : century = year / 100;
305 331324 : julian = year * 365 - 32167;
306 331324 : julian += year / 4 - century + century / 4;
307 331324 : julian += 7834 * month / 256 + day;
308 :
309 331324 : return julian;
310 : } /* date2j() */
311 :
312 : void
313 269236 : 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 269236 : julian = jd;
321 269236 : julian += 32044;
322 269236 : quad = julian / 146097;
323 269236 : extra = (julian - quad * 146097) * 4 + 3;
324 269236 : julian += 60 + quad * 3 + extra / 146097;
325 269236 : quad = julian / 1461;
326 269236 : julian -= quad * 1461;
327 269236 : y = julian * 4 / 1461;
328 538472 : julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
329 269236 : + 123;
330 269236 : y += quad * 4;
331 269236 : *year = y - 4800;
332 269236 : quad = julian * 2141 / 65536;
333 269236 : *day = julian - 7834 * quad / 256;
334 269236 : *month = (quad + 10) % MONTHS_PER_YEAR + 1;
335 269236 : } /* 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 50236 : j2day(int date)
347 : {
348 50236 : date += 1;
349 50236 : date %= 7;
350 : /* Cope if division truncates towards zero, as it probably does */
351 50236 : if (date < 0)
352 0 : date += 7;
353 :
354 50236 : 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 2372 : GetCurrentDateTime(struct pg_tm *tm)
369 : {
370 : fsec_t fsec;
371 :
372 2372 : GetCurrentTimeUsec(tm, &fsec, NULL);
373 2372 : }
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 2546 : GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
390 : {
391 2546 : 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 2546 : 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 670 : 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 670 : 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 670 : cache_ts = cur_ts;
428 670 : cache_timezone = session_timezone;
429 : }
430 :
431 2546 : *tm = cache_tm;
432 2546 : *fsec = cache_fsec;
433 2546 : if (tzp != NULL)
434 162 : *tzp = cache_tz;
435 2546 : }
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 133000 : AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
451 : {
452 : Assert(precision >= 0);
453 :
454 133000 : if (fillzeros)
455 129724 : cp = pg_ultostr_zeropad(cp, abs(sec), 2);
456 : else
457 3276 : cp = pg_ultostr(cp, abs(sec));
458 :
459 : /* fsec_t is just an int32 */
460 133000 : if (fsec != 0)
461 : {
462 20884 : int32 value = abs(fsec);
463 20884 : char *end = &cp[precision + 1];
464 20884 : bool gotnonzero = false;
465 :
466 20884 : *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 146188 : while (precision--)
474 : {
475 125304 : int32 oldval = value;
476 : int32 remainder;
477 :
478 125304 : value /= 10;
479 125304 : remainder = oldval - value * 10;
480 :
481 : /* check if we got a non-zero */
482 125304 : if (remainder)
483 107866 : gotnonzero = true;
484 :
485 125304 : if (gotnonzero)
486 109414 : cp[precision] = '0' + remainder;
487 : else
488 15890 : 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 20884 : if (value)
497 0 : return pg_ultostr(cp, abs(fsec));
498 :
499 20884 : return end;
500 : }
501 : else
502 112116 : 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 114992 : AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
514 : {
515 114992 : 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 8154 : int64_multiply_add(int64 val, int64 multiplier, int64 *sum)
525 : {
526 : int64 product;
527 :
528 16248 : if (pg_mul_s64_overflow(val, multiplier, &product) ||
529 8094 : pg_add_s64_overflow(*sum, product, sum))
530 156 : return false;
531 7998 : 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 8798 : AdjustFractMicroseconds(double frac, int64 scale,
540 : struct pg_itm_in *itm_in)
541 : {
542 : int64 usec;
543 :
544 : /* Fast path for common case */
545 8798 : if (frac == 0)
546 8336 : 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 918 : 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 918 : if (frac == 0)
578 732 : 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 898 : 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 898 : int extra_months = (int) rint(frac * scale * MONTHS_PER_YEAR);
611 :
612 898 : 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 2274 : AdjustMicroseconds(int64 val, double fval, int64 scale,
621 : struct pg_itm_in *itm_in)
622 : {
623 : /* Handle the integer part */
624 2274 : if (!int64_multiply_add(val, scale, &itm_in->tm_usec))
625 150 : return false;
626 : /* Handle the float part */
627 2124 : 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 6794 : AdjustDays(int64 val, int scale, struct pg_itm_in *itm_in)
636 : {
637 : int days;
638 :
639 6794 : if (val < INT_MIN || val > INT_MAX)
640 36 : return false;
641 13504 : return !pg_mul_s32_overflow((int32) val, scale, &days) &&
642 6746 : !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 888 : AdjustMonths(int64 val, struct pg_itm_in *itm_in)
652 : {
653 888 : if (val < INT_MIN || val > INT_MAX)
654 12 : return false;
655 876 : 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 1036 : AdjustYears(int64 val, int scale,
664 : struct pg_itm_in *itm_in)
665 : {
666 : int years;
667 :
668 1036 : if (val < INT_MIN || val > INT_MAX)
669 24 : return false;
670 1988 : return !pg_mul_s32_overflow((int32) val, scale, &years) &&
671 976 : !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 23084 : 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 23084 : if (cp[1] == '\0')
692 : {
693 0 : *frac = 0;
694 : }
695 : else
696 : {
697 23084 : errno = 0;
698 23084 : *frac = strtod(cp, &cp);
699 : /* check for parse failure */
700 23084 : if (*cp != '\0' || errno != 0)
701 12 : return DTERR_BAD_FORMAT;
702 : }
703 23072 : 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 22586 : ParseFractionalSecond(char *cp, fsec_t *fsec)
712 : {
713 : double frac;
714 : int dterr;
715 :
716 22586 : dterr = ParseFraction(cp, &frac);
717 22586 : if (dterr)
718 12 : return dterr;
719 22574 : *fsec = rint(frac * 1000000);
720 22574 : 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 80882 : ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
757 : char **field, int *ftype, int maxfields, int *numfields)
758 : {
759 80882 : int nf = 0;
760 80882 : const char *cp = timestr;
761 80882 : char *bufp = workbuf;
762 80882 : 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 370226 : while (*cp != '\0')
780 : {
781 : /* Ignore spaces between fields */
782 289344 : if (isspace((unsigned char) *cp))
783 : {
784 84382 : cp++;
785 84382 : continue;
786 : }
787 :
788 : /* Record start of current field */
789 204962 : if (nf >= maxfields)
790 0 : return DTERR_BAD_FORMAT;
791 204962 : field[nf] = bufp;
792 :
793 : /* leading digit? then date or time */
794 204962 : if (isdigit((unsigned char) *cp))
795 : {
796 141544 : APPEND_CHAR(bufp, bufend, *cp++);
797 417094 : while (isdigit((unsigned char) *cp))
798 275550 : APPEND_CHAR(bufp, bufend, *cp++);
799 :
800 : /* time field? */
801 141544 : if (*cp == ':')
802 : {
803 63170 : ftype[nf] = DTK_TIME;
804 63170 : APPEND_CHAR(bufp, bufend, *cp++);
805 531084 : while (isdigit((unsigned char) *cp) ||
806 147450 : (*cp == ':') || (*cp == '.'))
807 467914 : APPEND_CHAR(bufp, bufend, *cp++);
808 : }
809 : /* date field? allow embedded text month */
810 78374 : else if (*cp == '-' || *cp == '/' || *cp == '.')
811 64070 : {
812 : /* save delimiting character to use later */
813 64070 : char delim = *cp;
814 :
815 64070 : APPEND_CHAR(bufp, bufend, *cp++);
816 : /* second field is all digits? then no embedded text month */
817 64070 : if (isdigit((unsigned char) *cp))
818 : {
819 63974 : ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
820 191958 : while (isdigit((unsigned char) *cp))
821 127984 : APPEND_CHAR(bufp, bufend, *cp++);
822 :
823 : /*
824 : * insist that the delimiters match to get a three-field
825 : * date.
826 : */
827 63974 : if (*cp == delim)
828 : {
829 63392 : ftype[nf] = DTK_DATE;
830 63392 : APPEND_CHAR(bufp, bufend, *cp++);
831 193096 : while (isdigit((unsigned char) *cp) || *cp == delim)
832 129704 : 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 14304 : ftype[nf] = DTK_NUMBER;
849 : }
850 : /* Leading decimal point? Then fractional seconds... */
851 63418 : 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 63418 : else if (isalpha((unsigned char) *cp))
864 : {
865 : bool is_date;
866 :
867 21392 : ftype[nf] = DTK_STRING;
868 21392 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
869 81028 : while (isalpha((unsigned char) *cp))
870 59636 : 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 21392 : is_date = false;
881 21392 : if (*cp == '-' || *cp == '/' || *cp == '.')
882 1486 : is_date = true;
883 19906 : 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 21392 : if (is_date)
891 : {
892 2992 : ftype[nf] = DTK_DATE;
893 : do
894 : {
895 13906 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
896 13906 : } while (*cp == '+' || *cp == '-' ||
897 13624 : *cp == '/' || *cp == '_' ||
898 13492 : *cp == '.' || *cp == ':' ||
899 26996 : isalnum((unsigned char) *cp));
900 : }
901 : }
902 : /* sign? then special or numeric timezone */
903 42026 : else if (*cp == '+' || *cp == '-')
904 : {
905 41546 : APPEND_CHAR(bufp, bufend, *cp++);
906 : /* soak up leading whitespace */
907 41570 : 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 41546 : if (isdigit((unsigned char) *cp))
912 : {
913 41352 : ftype[nf] = DTK_TZ;
914 41352 : APPEND_CHAR(bufp, bufend, *cp++);
915 93214 : while (isdigit((unsigned char) *cp) ||
916 43020 : *cp == ':' || *cp == '.' || *cp == '-')
917 51862 : APPEND_CHAR(bufp, bufend, *cp++);
918 : }
919 : /* special? */
920 194 : else if (isalpha((unsigned char) *cp))
921 : {
922 194 : ftype[nf] = DTK_SPECIAL;
923 194 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
924 1552 : while (isalpha((unsigned char) *cp))
925 1358 : 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 480 : else if (ispunct((unsigned char) *cp))
933 : {
934 480 : cp++;
935 480 : 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 204482 : *bufp++ = '\0';
943 204482 : nf++;
944 : }
945 :
946 80882 : *numfields = nf;
947 :
948 80882 : 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 67570 : DecodeDateTime(char **field, int *ftype, int nf,
981 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
982 : DateTimeErrorExtra *extra)
983 : {
984 67570 : int fmask = 0,
985 : tmask,
986 : type;
987 67570 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
988 : int i;
989 : int val;
990 : int dterr;
991 67570 : int mer = HR24;
992 67570 : bool haveTextMonth = false;
993 67570 : bool isjulian = false;
994 67570 : bool is2digits = false;
995 67570 : bool bc = false;
996 67570 : pg_tz *namedTz = NULL;
997 67570 : pg_tz *abbrevTz = NULL;
998 : pg_tz *valtz;
999 67570 : 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 67570 : *dtype = DTK_DATE;
1007 67570 : tm->tm_hour = 0;
1008 67570 : tm->tm_min = 0;
1009 67570 : tm->tm_sec = 0;
1010 67570 : *fsec = 0;
1011 : /* don't know daylight savings time status apriori */
1012 67570 : tm->tm_isdst = -1;
1013 67570 : if (tzp != NULL)
1014 67570 : *tzp = 0;
1015 :
1016 243272 : for (i = 0; i < nf; i++)
1017 : {
1018 176014 : switch (ftype[i])
1019 : {
1020 64730 : 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 64730 : 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 64724 : else if (ptype != 0 ||
1064 64712 : ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
1065 : (DTK_M(MONTH) | DTK_M(DAY))))
1066 : {
1067 : /* No time zone accepted? Then quit... */
1068 1386 : if (tzp == NULL)
1069 0 : return DTERR_BAD_FORMAT;
1070 :
1071 1386 : 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 1368 : namedTz = pg_tzset(field[i]);
1123 1368 : 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 1332 : tmask = DTK_M(TZ);
1130 : }
1131 : }
1132 : else
1133 : {
1134 63338 : dterr = DecodeDate(field[i], fmask,
1135 : &tmask, &is2digits, tm);
1136 63338 : if (dterr)
1137 36 : return dterr;
1138 : }
1139 64652 : break;
1140 :
1141 58092 : case DTK_TIME:
1142 :
1143 : /*
1144 : * This might be an ISO time following a "t" field.
1145 : */
1146 58092 : 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 58092 : dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
1154 : &tmask, tm, fsec);
1155 58092 : if (dterr)
1156 0 : return dterr;
1157 :
1158 : /* check for time overflow */
1159 58092 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec,
1160 : *fsec))
1161 0 : return DTERR_FIELD_OVERFLOW;
1162 58092 : break;
1163 :
1164 38506 : case DTK_TZ:
1165 : {
1166 : int tz;
1167 :
1168 38506 : if (tzp == NULL)
1169 12 : return DTERR_BAD_FORMAT;
1170 :
1171 38506 : dterr = DecodeTimezone(field[i], &tz);
1172 38506 : if (dterr)
1173 12 : return dterr;
1174 38494 : *tzp = tz;
1175 38494 : tmask = DTK_M(TZ);
1176 : }
1177 38494 : break;
1178 :
1179 6176 : case DTK_NUMBER:
1180 :
1181 : /*
1182 : * Deal with cases where previous field labeled this one
1183 : */
1184 6176 : 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 6044 : flen = strlen(field[i]);
1248 6044 : cp = strchr(field[i], '.');
1249 :
1250 : /* Embedded decimal and no date yet? */
1251 6044 : 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 6014 : 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 6002 : 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 5664 : dterr = DecodeNumber(flen, field[i],
1295 : haveTextMonth, fmask,
1296 : &tmask, tm,
1297 : fsec, &is2digits);
1298 5664 : if (dterr)
1299 12 : return dterr;
1300 : }
1301 : }
1302 6152 : break;
1303 :
1304 8510 : case DTK_STRING:
1305 : case DTK_SPECIAL:
1306 : /* timezone abbrevs take precedence over built-in tokens */
1307 8510 : dterr = DecodeTimezoneAbbrev(i, field[i],
1308 : &type, &val, &valtz, extra);
1309 8510 : if (dterr)
1310 0 : return dterr;
1311 8510 : if (type == UNKNOWN_FIELD)
1312 6152 : type = DecodeSpecial(i, field[i], &val);
1313 8510 : if (type == IGNORE_DTF)
1314 0 : continue;
1315 :
1316 8510 : tmask = DTK_M(type);
1317 8510 : switch (type)
1318 : {
1319 1138 : case RESERV:
1320 1138 : 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 622 : case DTK_EPOCH:
1364 : case DTK_LATE:
1365 : case DTK_EARLY:
1366 622 : tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1367 622 : *dtype = val;
1368 : /* caller ignores tm for these dtype codes */
1369 622 : break;
1370 :
1371 0 : default:
1372 0 : elog(ERROR, "unrecognized RESERV datetime token: %d",
1373 : val);
1374 : }
1375 :
1376 1138 : break;
1377 :
1378 2592 : case MONTH:
1379 :
1380 : /*
1381 : * already have a (numeric) month? then see if we can
1382 : * substitute...
1383 : */
1384 2592 : 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 2592 : haveTextMonth = true;
1392 2592 : tm->tm_mon = val;
1393 2592 : 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 1750 : case DTZ:
1409 :
1410 : /*
1411 : * set mask for TZ here _or_ check for DTZ later when
1412 : * getting default timezone
1413 : */
1414 1750 : tmask |= DTK_M(TZ);
1415 1750 : tm->tm_isdst = 1;
1416 1750 : if (tzp == NULL)
1417 0 : return DTERR_BAD_FORMAT;
1418 1750 : *tzp = -val;
1419 1750 : break;
1420 :
1421 524 : case TZ:
1422 524 : tm->tm_isdst = 0;
1423 524 : if (tzp == NULL)
1424 0 : return DTERR_BAD_FORMAT;
1425 524 : *tzp = -val;
1426 524 : 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 258 : case ADBC:
1442 258 : bc = (val == BC);
1443 258 : break;
1444 :
1445 1900 : case DOW:
1446 1900 : tm->tm_wday = val;
1447 1900 : 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 8450 : break;
1492 :
1493 0 : default:
1494 0 : return DTERR_BAD_FORMAT;
1495 : }
1496 :
1497 175846 : if (tmask & fmask)
1498 144 : return DTERR_BAD_FORMAT;
1499 175702 : fmask |= tmask;
1500 : } /* end loop over fields */
1501 :
1502 : /* reject if prefix type appeared and was never handled */
1503 67258 : if (ptype != 0)
1504 0 : return DTERR_BAD_FORMAT;
1505 :
1506 : /* do additional checking for normal date specs (but not "infinity" etc) */
1507 67258 : if (*dtype == DTK_DATE)
1508 : {
1509 : /* do final checking/adjustment of Y/M/D fields */
1510 66780 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
1511 66780 : if (dterr)
1512 198 : return dterr;
1513 :
1514 : /* handle AM/PM */
1515 66582 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
1516 0 : return DTERR_FIELD_OVERFLOW;
1517 66582 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
1518 0 : tm->tm_hour = 0;
1519 66582 : 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 66582 : 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 66576 : if (namedTz != NULL)
1535 : {
1536 : /* daylight savings time modifier disallowed with full TZ */
1537 1332 : if (fmask & DTK_M(DTZMOD))
1538 0 : return DTERR_BAD_FORMAT;
1539 :
1540 1332 : *tzp = DetermineTimeZoneOffset(tm, namedTz);
1541 : }
1542 :
1543 : /*
1544 : * Likewise, if we had a dynamic timezone abbreviation, resolve it
1545 : * now.
1546 : */
1547 66576 : 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 66576 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
1558 : {
1559 : /*
1560 : * daylight savings time modifier but no standard timezone? then
1561 : * error
1562 : */
1563 24272 : if (fmask & DTK_M(DTZMOD))
1564 0 : return DTERR_BAD_FORMAT;
1565 :
1566 24272 : *tzp = DetermineTimeZoneOffset(tm, session_timezone);
1567 : }
1568 : }
1569 :
1570 67054 : 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 52080 : DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
1588 : {
1589 : pg_time_t t;
1590 :
1591 52080 : 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 52260 : 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 52260 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1632 24 : goto overflow;
1633 52236 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
1634 :
1635 52236 : day = ((pg_time_t) date) * SECS_PER_DAY;
1636 52236 : if (day / SECS_PER_DAY != date)
1637 0 : goto overflow;
1638 52236 : sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
1639 52236 : mytime = day + sec;
1640 : /* since sec >= 0, overflow could only be from +day to -mytime */
1641 52236 : 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 52236 : prevtime = mytime - SECS_PER_DAY;
1651 52236 : if (mytime < 0 && prevtime > 0)
1652 0 : goto overflow;
1653 :
1654 52236 : res = pg_next_dst_boundary(&prevtime,
1655 : &before_gmtoff, &before_isdst,
1656 : &boundary,
1657 : &after_gmtoff, &after_isdst,
1658 : tzp);
1659 52236 : if (res < 0)
1660 0 : goto overflow; /* failure? */
1661 :
1662 52236 : if (res == 0)
1663 : {
1664 : /* Non-DST zone, life is simple */
1665 2724 : tm->tm_isdst = before_isdst;
1666 2724 : *tp = mytime - before_gmtoff;
1667 2724 : return -(int) before_gmtoff;
1668 : }
1669 :
1670 : /*
1671 : * Form the candidate pg_time_t values with local-time adjustment
1672 : */
1673 49512 : beforetime = mytime - before_gmtoff;
1674 49512 : if ((before_gmtoff > 0 &&
1675 12 : mytime < 0 && beforetime > 0) ||
1676 49512 : (before_gmtoff <= 0 &&
1677 36770 : mytime > 0 && beforetime < 0))
1678 0 : goto overflow;
1679 49512 : aftertime = mytime - after_gmtoff;
1680 49512 : if ((after_gmtoff > 0 &&
1681 12 : mytime < 0 && aftertime > 0) ||
1682 49512 : (after_gmtoff <= 0 &&
1683 36770 : 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 49512 : if (beforetime < boundary && aftertime < boundary)
1692 : {
1693 48934 : tm->tm_isdst = before_isdst;
1694 48934 : *tp = beforetime;
1695 48934 : 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 984 : DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1787 : pg_tz *tzp, int *isdst)
1788 : {
1789 984 : 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 984 : 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 894 : 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 894 : zone_offset = DetermineTimeZoneOffset(&tm, tzp);
1812 894 : *isdst = tm.tm_isdst;
1813 894 : 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 1164 : 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 1164 : strlcpy(upabbr, abbr, sizeof(upabbr));
1832 5442 : for (p = (unsigned char *) upabbr; *p; p++)
1833 4278 : *p = pg_toupper(*p);
1834 :
1835 : /* Look up the abbrev's meaning at this time in this zone */
1836 1164 : 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 894 : 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 3946 : DecodeTimeOnly(char **field, int *ftype, int nf,
1867 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1868 : DateTimeErrorExtra *extra)
1869 : {
1870 3946 : int fmask = 0,
1871 : tmask,
1872 : type;
1873 3946 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
1874 : int i;
1875 : int val;
1876 : int dterr;
1877 3946 : bool isjulian = false;
1878 3946 : bool is2digits = false;
1879 3946 : bool bc = false;
1880 3946 : int mer = HR24;
1881 3946 : pg_tz *namedTz = NULL;
1882 3946 : pg_tz *abbrevTz = NULL;
1883 3946 : char *abbrev = NULL;
1884 : pg_tz *valtz;
1885 :
1886 3946 : *dtype = DTK_TIME;
1887 3946 : tm->tm_hour = 0;
1888 3946 : tm->tm_min = 0;
1889 3946 : tm->tm_sec = 0;
1890 3946 : *fsec = 0;
1891 : /* don't know daylight savings time status apriori */
1892 3946 : tm->tm_isdst = -1;
1893 :
1894 3946 : if (tzp != NULL)
1895 3946 : *tzp = 0;
1896 :
1897 10164 : for (i = 0; i < nf; i++)
1898 : {
1899 6230 : 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 3856 : case DTK_TIME:
1975 3856 : dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
1976 : INTERVAL_FULL_RANGE,
1977 : &tmask, tm, fsec);
1978 3856 : if (dterr)
1979 0 : return dterr;
1980 3856 : 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 348 : case DTK_STRING:
2129 : case DTK_SPECIAL:
2130 : /* timezone abbrevs take precedence over built-in tokens */
2131 348 : dterr = DecodeTimezoneAbbrev(i, field[i],
2132 : &type, &val, &valtz, extra);
2133 348 : if (dterr)
2134 0 : return dterr;
2135 348 : if (type == UNKNOWN_FIELD)
2136 96 : type = DecodeSpecial(i, field[i], &val);
2137 348 : if (type == IGNORE_DTF)
2138 0 : continue;
2139 :
2140 348 : tmask = DTK_M(type);
2141 348 : 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 186 : case DTZ:
2181 :
2182 : /*
2183 : * set mask for TZ here _or_ check for DTZ later when
2184 : * getting default timezone
2185 : */
2186 186 : tmask |= DTK_M(TZ);
2187 186 : tm->tm_isdst = 1;
2188 186 : if (tzp == NULL)
2189 0 : return DTERR_BAD_FORMAT;
2190 186 : *tzp = -val;
2191 186 : ftype[i] = DTK_TZ;
2192 186 : 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 348 : break;
2253 :
2254 0 : default:
2255 0 : return DTERR_BAD_FORMAT;
2256 : }
2257 :
2258 6218 : if (tmask & fmask)
2259 0 : return DTERR_BAD_FORMAT;
2260 6218 : fmask |= tmask;
2261 : } /* end loop over fields */
2262 :
2263 : /* reject if prefix type appeared and was never handled */
2264 3934 : if (ptype != 0)
2265 0 : return DTERR_BAD_FORMAT;
2266 :
2267 : /* do final checking/adjustment of Y/M/D fields */
2268 3934 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
2269 3934 : if (dterr)
2270 0 : return dterr;
2271 :
2272 : /* handle AM/PM */
2273 3934 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
2274 0 : return DTERR_FIELD_OVERFLOW;
2275 3934 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
2276 0 : tm->tm_hour = 0;
2277 3934 : 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 3934 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec))
2282 72 : return DTERR_FIELD_OVERFLOW;
2283 :
2284 3862 : 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 3862 : 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 3820 : 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 3820 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
2347 : {
2348 : struct pg_tm tt,
2349 1920 : *tmp = &tt;
2350 :
2351 : /*
2352 : * daylight savings time modifier but no standard timezone? then error
2353 : */
2354 1920 : if (fmask & DTK_M(DTZMOD))
2355 0 : return DTERR_BAD_FORMAT;
2356 :
2357 1920 : if ((fmask & DTK_DATE_M) == 0)
2358 1842 : 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 1920 : tmp->tm_hour = tm->tm_hour;
2369 1920 : tmp->tm_min = tm->tm_min;
2370 1920 : tmp->tm_sec = tm->tm_sec;
2371 1920 : *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
2372 1920 : tm->tm_isdst = tmp->tm_isdst;
2373 : }
2374 :
2375 3820 : 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 63566 : DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
2390 : struct pg_tm *tm)
2391 : {
2392 : fsec_t fsec;
2393 63566 : int nf = 0;
2394 : int i,
2395 : len;
2396 : int dterr;
2397 63566 : bool haveTextMonth = false;
2398 : int type,
2399 : val,
2400 63566 : dmask = 0;
2401 : char *field[MAXDATEFIELDS];
2402 :
2403 63566 : *tmask = 0;
2404 :
2405 : /* parse this string... */
2406 254198 : while (*str != '\0' && nf < MAXDATEFIELDS)
2407 : {
2408 : /* skip field separators */
2409 190632 : while (*str != '\0' && !isalnum((unsigned char) *str))
2410 0 : str++;
2411 :
2412 190632 : if (*str == '\0')
2413 0 : return DTERR_BAD_FORMAT; /* end of string after separator */
2414 :
2415 190632 : field[nf] = str;
2416 190632 : if (isdigit((unsigned char) *str))
2417 : {
2418 698592 : while (isdigit((unsigned char) *str))
2419 508104 : 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 190632 : if (*str != '\0')
2429 127102 : *str++ = '\0';
2430 190632 : nf++;
2431 : }
2432 :
2433 : /* look first for text fields, since that will be unambiguous month */
2434 254198 : for (i = 0; i < nf; i++)
2435 : {
2436 190632 : 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 254198 : for (i = 0; i < nf; i++)
2466 : {
2467 190632 : if (field[i] == NULL)
2468 144 : continue;
2469 :
2470 190488 : if ((len = strlen(field[i])) <= 0)
2471 0 : return DTERR_BAD_FORMAT;
2472 :
2473 190488 : dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
2474 : &dmask, tm,
2475 : &fsec, is2digits);
2476 190488 : if (dterr)
2477 0 : return dterr;
2478 :
2479 190488 : if (fmask & dmask)
2480 0 : return DTERR_BAD_FORMAT;
2481 :
2482 190488 : fmask |= dmask;
2483 190488 : *tmask |= dmask;
2484 : }
2485 :
2486 63566 : 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 63530 : 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 73744 : ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
2500 : struct pg_tm *tm)
2501 : {
2502 73744 : if (fmask & DTK_M(YEAR))
2503 : {
2504 70002 : if (isjulian)
2505 : {
2506 : /* tm_year is correct and should not be touched */
2507 : }
2508 67128 : else if (bc)
2509 : {
2510 : /* there is no year zero in AD/BC notation */
2511 258 : 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 258 : tm->tm_year = -(tm->tm_year - 1);
2515 : }
2516 66870 : 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 66516 : if (tm->tm_year <= 0)
2530 12 : return DTERR_FIELD_OVERFLOW;
2531 : }
2532 : }
2533 :
2534 : /* now that we have correct year, decode DOY */
2535 73732 : 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 73732 : if (fmask & DTK_M(MONTH))
2543 : {
2544 69966 : 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 73654 : if (fmask & DTK_M(DAY))
2550 : {
2551 69852 : if (tm->tm_mday < 1 || tm->tm_mday > 31)
2552 138 : return DTERR_MD_FIELD_OVERFLOW;
2553 : }
2554 :
2555 73516 : 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 69696 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2563 48 : return DTERR_FIELD_OVERFLOW;
2564 : }
2565 :
2566 73468 : 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 63920 : DecodeTimeCommon(char *str, int fmask, int range,
2582 : int *tmask, struct pg_itm *itm)
2583 : {
2584 : char *cp;
2585 : int dterr;
2586 63920 : fsec_t fsec = 0;
2587 :
2588 63920 : *tmask = DTK_TIME_M;
2589 :
2590 63920 : errno = 0;
2591 63920 : itm->tm_hour = strtoi64(str, &cp, 10);
2592 63920 : if (errno == ERANGE)
2593 0 : return DTERR_FIELD_OVERFLOW;
2594 63920 : if (*cp != ':')
2595 0 : return DTERR_BAD_FORMAT;
2596 63920 : errno = 0;
2597 63920 : itm->tm_min = strtoint(cp + 1, &cp, 10);
2598 63920 : if (errno == ERANGE)
2599 0 : return DTERR_FIELD_OVERFLOW;
2600 63920 : if (*cp == '\0')
2601 : {
2602 1488 : itm->tm_sec = 0;
2603 : /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
2604 1488 : 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 62432 : 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 62384 : else if (*cp == ':')
2626 : {
2627 62384 : errno = 0;
2628 62384 : itm->tm_sec = strtoint(cp + 1, &cp, 10);
2629 62384 : if (errno == ERANGE)
2630 0 : return DTERR_FIELD_OVERFLOW;
2631 62384 : if (*cp == '.')
2632 : {
2633 22538 : dterr = ParseFractionalSecond(cp, &fsec);
2634 22538 : if (dterr)
2635 0 : return dterr;
2636 : }
2637 39846 : 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 63908 : if (itm->tm_hour < 0 ||
2645 63908 : itm->tm_min < 0 || itm->tm_min > MINS_PER_HOUR - 1 ||
2646 63908 : itm->tm_sec < 0 || itm->tm_sec > SECS_PER_MINUTE ||
2647 63908 : fsec < 0 || fsec > USECS_PER_SEC)
2648 0 : return DTERR_FIELD_OVERFLOW;
2649 :
2650 63908 : itm->tm_usec = (int) fsec;
2651 :
2652 63908 : 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 61948 : 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 61948 : dterr = DecodeTimeCommon(str, fmask, range,
2670 : tmask, &itm);
2671 61948 : if (dterr)
2672 0 : return dterr;
2673 :
2674 61948 : if (itm.tm_hour > INT_MAX)
2675 0 : return DTERR_FIELD_OVERFLOW;
2676 61948 : tm->tm_hour = (int) itm.tm_hour;
2677 61948 : tm->tm_min = itm.tm_min;
2678 61948 : tm->tm_sec = itm.tm_sec;
2679 61948 : *fsec = itm.tm_usec;
2680 :
2681 61948 : 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 1972 : 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 1972 : dterr = DecodeTimeCommon(str, fmask, range,
2699 : tmask, &itm);
2700 1972 : if (dterr)
2701 12 : return dterr;
2702 :
2703 1960 : itm_in->tm_usec = itm.tm_usec;
2704 1960 : if (!int64_multiply_add(itm.tm_hour, USECS_PER_HOUR, &itm_in->tm_usec) ||
2705 1960 : !int64_multiply_add(itm.tm_min, USECS_PER_MINUTE, &itm_in->tm_usec) ||
2706 1960 : !int64_multiply_add(itm.tm_sec, USECS_PER_SEC, &itm_in->tm_usec))
2707 6 : return DTERR_FIELD_OVERFLOW;
2708 :
2709 1954 : 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 196152 : 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 196152 : *tmask = 0;
2726 :
2727 196152 : errno = 0;
2728 196152 : val = strtoint(str, &cp, 10);
2729 196152 : if (errno == ERANGE)
2730 0 : return DTERR_FIELD_OVERFLOW;
2731 196152 : if (cp == str)
2732 0 : return DTERR_BAD_FORMAT;
2733 :
2734 196152 : 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 196152 : else if (*cp != '\0')
2756 0 : return DTERR_BAD_FORMAT;
2757 :
2758 : /* Special case for day of year */
2759 196152 : 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 196098 : switch (fmask & DTK_DATE_M)
2770 : {
2771 63756 : 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 63756 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
2781 : {
2782 62064 : *tmask = DTK_M(YEAR);
2783 62064 : 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 63756 : break;
2796 :
2797 61956 : case (DTK_M(YEAR)):
2798 : /* Must be at second field of YY-MM-DD */
2799 61956 : *tmask = DTK_M(MONTH);
2800 61956 : tm->tm_mon = val;
2801 61956 : break;
2802 :
2803 4044 : case (DTK_M(MONTH)):
2804 4044 : 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 2572 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
2814 : {
2815 72 : *tmask = DTK_M(YEAR);
2816 72 : tm->tm_year = val;
2817 : }
2818 : else
2819 : {
2820 2500 : *tmask = DTK_M(DAY);
2821 2500 : 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 4044 : break;
2831 :
2832 62034 : case (DTK_M(YEAR) | DTK_M(MONTH)):
2833 62034 : 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 61908 : *tmask = DTK_M(DAY);
2854 61908 : tm->tm_mday = val;
2855 : }
2856 62034 : 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 4150 : case (DTK_M(MONTH) | DTK_M(DAY)):
2865 : /* Must be at third field of DD-MM-YY or MM-DD-YY */
2866 4150 : *tmask = DTK_M(YEAR);
2867 4150 : tm->tm_year = val;
2868 4150 : 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 196086 : if (*tmask == DTK_M(YEAR))
2889 66286 : *is2digits = (flen <= 2);
2890 :
2891 196086 : 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 40750 : DecodeTimezone(const char *str, int *tzp)
2999 : {
3000 : int tz;
3001 : int hr,
3002 : min,
3003 40750 : sec = 0;
3004 : char *cp;
3005 :
3006 : /* leading character must be "+" or "-" */
3007 40750 : if (*str != '+' && *str != '-')
3008 60 : return DTERR_BAD_FORMAT;
3009 :
3010 40690 : errno = 0;
3011 40690 : hr = strtoint(str + 1, &cp, 10);
3012 40690 : if (errno == ERANGE)
3013 0 : return DTERR_TZDISP_OVERFLOW;
3014 :
3015 : /* explicit delimiter? */
3016 40690 : if (*cp == ':')
3017 : {
3018 1608 : errno = 0;
3019 1608 : min = strtoint(cp + 1, &cp, 10);
3020 1608 : if (errno == ERANGE)
3021 0 : return DTERR_TZDISP_OVERFLOW;
3022 1608 : 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 39082 : 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 39010 : min = 0;
3039 :
3040 : /* Range-check the values; see notes in datatype/timestamp.h */
3041 40690 : if (hr < 0 || hr > MAX_TZDISP_HOUR)
3042 12 : return DTERR_TZDISP_OVERFLOW;
3043 40678 : if (min < 0 || min >= MINS_PER_HOUR)
3044 12 : return DTERR_TZDISP_OVERFLOW;
3045 40666 : if (sec < 0 || sec >= SECS_PER_MINUTE)
3046 0 : return DTERR_TZDISP_OVERFLOW;
3047 :
3048 40666 : tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
3049 40666 : if (*str == '-')
3050 958 : tz = -tz;
3051 :
3052 40666 : *tzp = -tz;
3053 :
3054 40666 : if (*cp != '\0')
3055 0 : return DTERR_BAD_FORMAT;
3056 :
3057 40666 : 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 9344 : DecodeTimezoneAbbrev(int field, const char *lowtoken,
3083 : int *ftype, int *offset, pg_tz **tz,
3084 : DateTimeErrorExtra *extra)
3085 : {
3086 : const datetkn *tp;
3087 :
3088 9344 : tp = abbrevcache[field];
3089 : /* use strncmp so that we match truncated tokens */
3090 9344 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3091 : {
3092 6902 : if (zoneabbrevtbl)
3093 6902 : tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3094 6902 : zoneabbrevtbl->numabbrevs);
3095 : else
3096 0 : tp = NULL;
3097 : }
3098 9344 : if (tp == NULL)
3099 : {
3100 6518 : *ftype = UNKNOWN_FIELD;
3101 6518 : *offset = 0;
3102 6518 : *tz = NULL;
3103 : }
3104 : else
3105 : {
3106 2826 : abbrevcache[field] = tp;
3107 2826 : *ftype = tp->type;
3108 2826 : 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 2562 : *offset = tp->value;
3118 2562 : *tz = NULL;
3119 : }
3120 : }
3121 :
3122 9344 : 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 40254 : DecodeSpecial(int field, const char *lowtoken, int *val)
3140 : {
3141 : int type;
3142 : const datetkn *tp;
3143 :
3144 40254 : tp = datecache[field];
3145 : /* use strncmp so that we match truncated tokens */
3146 40254 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3147 : {
3148 8748 : tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
3149 : }
3150 40254 : if (tp == NULL)
3151 : {
3152 72 : type = UNKNOWN_FIELD;
3153 72 : *val = 0;
3154 : }
3155 : else
3156 : {
3157 40182 : datecache[field] = tp;
3158 40182 : type = tp->type;
3159 40182 : *val = tp->value;
3160 : }
3161 :
3162 40254 : 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 486 : 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 486 : lowzone = downcase_truncate_identifier(tzname,
3199 486 : strlen(tzname),
3200 : false);
3201 :
3202 486 : dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
3203 486 : if (dterr)
3204 0 : DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
3205 :
3206 486 : if (type == TZ || type == DTZ)
3207 : {
3208 : /* fixed-offset abbreviation, return the offset */
3209 42 : return TZNAME_FIXED_OFFSET;
3210 : }
3211 444 : 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 270 : *tz = pg_tzset(tzname);
3220 270 : if (*tz == NULL)
3221 12 : ereport(ERROR,
3222 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3223 : errmsg("time zone \"%s\" not recognized", tzname)));
3224 258 : 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 9900 : ClearPgItmIn(struct pg_itm_in *itm_in)
3256 : {
3257 9900 : itm_in->tm_usec = 0;
3258 9900 : itm_in->tm_mday = 0;
3259 9900 : itm_in->tm_mon = 0;
3260 9900 : itm_in->tm_year = 0;
3261 9900 : }
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 : int
3276 9366 : DecodeInterval(char **field, int *ftype, int nf, int range,
3277 : int *dtype, struct pg_itm_in *itm_in)
3278 : {
3279 9366 : bool force_negative = false;
3280 9366 : bool is_before = false;
3281 : char *cp;
3282 9366 : int fmask = 0,
3283 : tmask,
3284 : type,
3285 : uval;
3286 : int i;
3287 : int dterr;
3288 : int64 val;
3289 : double fval;
3290 :
3291 9366 : *dtype = DTK_DELTA;
3292 9366 : type = IGNORE_DTF;
3293 9366 : ClearPgItmIn(itm_in);
3294 :
3295 : /*----------
3296 : * The SQL standard defines the interval literal
3297 : * '-1 1:00:00'
3298 : * to mean "negative 1 days and negative 1 hours", while Postgres
3299 : * traditionally treats this as meaning "negative 1 days and positive
3300 : * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
3301 : * to all fields if there are no other explicit signs.
3302 : *
3303 : * We leave the signs alone if there are additional explicit signs.
3304 : * This protects us against misinterpreting postgres-style dump output,
3305 : * since the postgres-style output code has always put an explicit sign on
3306 : * all fields following a negative field. But note that SQL-spec output
3307 : * is ambiguous and can be misinterpreted on load! (So it's best practice
3308 : * to dump in postgres style, not SQL style.)
3309 : *----------
3310 : */
3311 9366 : if (IntervalStyle == INTSTYLE_SQL_STANDARD && nf > 0 && *field[0] == '-')
3312 : {
3313 38 : force_negative = true;
3314 : /* Check for additional explicit signs */
3315 244 : for (i = 1; i < nf; i++)
3316 : {
3317 224 : if (*field[i] == '-' || *field[i] == '+')
3318 : {
3319 18 : force_negative = false;
3320 18 : break;
3321 : }
3322 : }
3323 : }
3324 :
3325 : /* read through list backwards to pick up units before values */
3326 30356 : for (i = nf - 1; i >= 0; i--)
3327 : {
3328 22010 : switch (ftype[i])
3329 : {
3330 1222 : case DTK_TIME:
3331 1222 : dterr = DecodeTimeForInterval(field[i], fmask, range,
3332 : &tmask, itm_in);
3333 1222 : if (dterr)
3334 18 : return dterr;
3335 1204 : if (force_negative &&
3336 2 : itm_in->tm_usec > 0)
3337 2 : itm_in->tm_usec = -itm_in->tm_usec;
3338 1204 : type = DTK_DAY;
3339 1204 : break;
3340 :
3341 2294 : case DTK_TZ:
3342 :
3343 : /*
3344 : * Timezone means a token with a leading sign character and at
3345 : * least one digit; there could be ':', '.', '-' embedded in
3346 : * it as well.
3347 : */
3348 : Assert(*field[i] == '-' || *field[i] == '+');
3349 :
3350 : /*
3351 : * Check for signed hh:mm or hh:mm:ss. If so, process exactly
3352 : * like DTK_TIME case above, plus handling the sign.
3353 : */
3354 3044 : if (strchr(field[i] + 1, ':') != NULL &&
3355 750 : DecodeTimeForInterval(field[i] + 1, fmask, range,
3356 : &tmask, itm_in) == 0)
3357 : {
3358 750 : if (*field[i] == '-')
3359 : {
3360 : /* flip the sign on time field */
3361 690 : if (itm_in->tm_usec == PG_INT64_MIN)
3362 0 : return DTERR_FIELD_OVERFLOW;
3363 690 : itm_in->tm_usec = -itm_in->tm_usec;
3364 : }
3365 :
3366 750 : if (force_negative &&
3367 0 : itm_in->tm_usec > 0)
3368 0 : itm_in->tm_usec = -itm_in->tm_usec;
3369 :
3370 : /*
3371 : * Set the next type to be a day, if units are not
3372 : * specified. This handles the case of '1 +02:03' since we
3373 : * are reading right to left.
3374 : */
3375 750 : type = DTK_DAY;
3376 750 : break;
3377 : }
3378 :
3379 : /*
3380 : * Otherwise, fall through to DTK_NUMBER case, which can
3381 : * handle signed float numbers and signed year-month values.
3382 : */
3383 :
3384 : /* FALLTHROUGH */
3385 :
3386 : case DTK_DATE:
3387 : case DTK_NUMBER:
3388 10416 : if (type == IGNORE_DTF)
3389 : {
3390 : /* use typmod to decide what rightmost field is */
3391 : switch (range)
3392 : {
3393 6 : case INTERVAL_MASK(YEAR):
3394 6 : type = DTK_YEAR;
3395 6 : break;
3396 30 : case INTERVAL_MASK(MONTH):
3397 : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3398 30 : type = DTK_MONTH;
3399 30 : break;
3400 18 : case INTERVAL_MASK(DAY):
3401 18 : type = DTK_DAY;
3402 18 : break;
3403 24 : case INTERVAL_MASK(HOUR):
3404 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3405 24 : type = DTK_HOUR;
3406 24 : break;
3407 24 : case INTERVAL_MASK(MINUTE):
3408 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3409 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3410 24 : type = DTK_MINUTE;
3411 24 : break;
3412 60 : case INTERVAL_MASK(SECOND):
3413 : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3414 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3415 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3416 60 : type = DTK_SECOND;
3417 60 : break;
3418 438 : default:
3419 438 : type = DTK_SECOND;
3420 438 : break;
3421 : }
3422 9816 : }
3423 :
3424 10416 : errno = 0;
3425 10416 : val = strtoi64(field[i], &cp, 10);
3426 10416 : if (errno == ERANGE)
3427 12 : return DTERR_FIELD_OVERFLOW;
3428 :
3429 10404 : if (*cp == '-')
3430 : {
3431 : /* SQL "years-months" syntax */
3432 : int val2;
3433 :
3434 60 : val2 = strtoint(cp + 1, &cp, 10);
3435 60 : if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
3436 0 : return DTERR_FIELD_OVERFLOW;
3437 60 : if (*cp != '\0')
3438 0 : return DTERR_BAD_FORMAT;
3439 60 : type = DTK_MONTH;
3440 60 : if (*field[i] == '-')
3441 6 : val2 = -val2;
3442 60 : if (pg_mul_s64_overflow(val, MONTHS_PER_YEAR, &val))
3443 0 : return DTERR_FIELD_OVERFLOW;
3444 60 : if (pg_add_s64_overflow(val, val2, &val))
3445 0 : return DTERR_FIELD_OVERFLOW;
3446 60 : fval = 0;
3447 : }
3448 10344 : else if (*cp == '.')
3449 : {
3450 486 : dterr = ParseFraction(cp, &fval);
3451 486 : if (dterr)
3452 0 : return dterr;
3453 486 : if (*field[i] == '-')
3454 138 : fval = -fval;
3455 : }
3456 9858 : else if (*cp == '\0')
3457 9474 : fval = 0;
3458 : else
3459 384 : return DTERR_BAD_FORMAT;
3460 :
3461 10020 : tmask = 0; /* DTK_M(type); */
3462 :
3463 10020 : if (force_negative)
3464 : {
3465 : /* val and fval should be of same sign, but test anyway */
3466 80 : if (val > 0)
3467 60 : val = -val;
3468 80 : if (fval > 0)
3469 18 : fval = -fval;
3470 : }
3471 :
3472 : switch (type)
3473 : {
3474 186 : case DTK_MICROSEC:
3475 186 : if (!AdjustMicroseconds(val, fval, 1, itm_in))
3476 36 : return DTERR_FIELD_OVERFLOW;
3477 150 : tmask = DTK_M(MICROSECOND);
3478 150 : break;
3479 :
3480 102 : case DTK_MILLISEC:
3481 102 : if (!AdjustMicroseconds(val, fval, 1000, itm_in))
3482 12 : return DTERR_FIELD_OVERFLOW;
3483 90 : tmask = DTK_M(MILLISECOND);
3484 90 : break;
3485 :
3486 594 : case DTK_SECOND:
3487 594 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3488 12 : return DTERR_FIELD_OVERFLOW;
3489 :
3490 : /*
3491 : * If any subseconds were specified, consider this
3492 : * microsecond and millisecond input as well.
3493 : */
3494 582 : if (fval == 0)
3495 420 : tmask = DTK_M(SECOND);
3496 : else
3497 162 : tmask = DTK_ALL_SECS_M;
3498 582 : break;
3499 :
3500 334 : case DTK_MINUTE:
3501 334 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3502 12 : return DTERR_FIELD_OVERFLOW;
3503 322 : tmask = DTK_M(MINUTE);
3504 322 : break;
3505 :
3506 668 : case DTK_HOUR:
3507 668 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3508 12 : return DTERR_FIELD_OVERFLOW;
3509 656 : tmask = DTK_M(HOUR);
3510 656 : type = DTK_DAY; /* set for next field */
3511 656 : break;
3512 :
3513 6464 : case DTK_DAY:
3514 6464 : if (!AdjustDays(val, 1, itm_in) ||
3515 6392 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3516 90 : return DTERR_FIELD_OVERFLOW;
3517 6374 : tmask = DTK_M(DAY);
3518 6374 : break;
3519 :
3520 96 : case DTK_WEEK:
3521 96 : if (!AdjustDays(val, 7, itm_in) ||
3522 72 : !AdjustFractDays(fval, 7, itm_in))
3523 48 : return DTERR_FIELD_OVERFLOW;
3524 48 : tmask = DTK_M(WEEK);
3525 48 : break;
3526 :
3527 714 : case DTK_MONTH:
3528 714 : if (!AdjustMonths(val, itm_in) ||
3529 654 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3530 84 : return DTERR_FIELD_OVERFLOW;
3531 630 : tmask = DTK_M(MONTH);
3532 630 : break;
3533 :
3534 664 : case DTK_YEAR:
3535 664 : if (!AdjustYears(val, 1, itm_in) ||
3536 616 : !AdjustFractYears(fval, 1, itm_in))
3537 60 : return DTERR_FIELD_OVERFLOW;
3538 604 : tmask = DTK_M(YEAR);
3539 604 : break;
3540 :
3541 66 : case DTK_DECADE:
3542 66 : if (!AdjustYears(val, 10, itm_in) ||
3543 42 : !AdjustFractYears(fval, 10, itm_in))
3544 36 : return DTERR_FIELD_OVERFLOW;
3545 30 : tmask = DTK_M(DECADE);
3546 30 : break;
3547 :
3548 66 : case DTK_CENTURY:
3549 66 : if (!AdjustYears(val, 100, itm_in) ||
3550 42 : !AdjustFractYears(fval, 100, itm_in))
3551 36 : return DTERR_FIELD_OVERFLOW;
3552 30 : tmask = DTK_M(CENTURY);
3553 30 : break;
3554 :
3555 66 : case DTK_MILLENNIUM:
3556 66 : if (!AdjustYears(val, 1000, itm_in) ||
3557 42 : !AdjustFractYears(fval, 1000, itm_in))
3558 36 : return DTERR_FIELD_OVERFLOW;
3559 30 : tmask = DTK_M(MILLENNIUM);
3560 30 : break;
3561 :
3562 0 : default:
3563 0 : return DTERR_BAD_FORMAT;
3564 : }
3565 9546 : break;
3566 :
3567 9622 : case DTK_STRING:
3568 : case DTK_SPECIAL:
3569 9622 : type = DecodeUnits(i, field[i], &uval);
3570 9622 : if (type == IGNORE_DTF)
3571 0 : continue;
3572 :
3573 9622 : tmask = 0; /* DTK_M(type); */
3574 : switch (type)
3575 : {
3576 9508 : case UNITS:
3577 9508 : type = uval;
3578 9508 : break;
3579 :
3580 78 : case AGO:
3581 78 : is_before = true;
3582 78 : type = uval;
3583 78 : break;
3584 :
3585 0 : case RESERV:
3586 0 : tmask = (DTK_DATE_M | DTK_TIME_M);
3587 0 : *dtype = uval;
3588 0 : break;
3589 :
3590 36 : default:
3591 36 : return DTERR_BAD_FORMAT;
3592 : }
3593 9586 : break;
3594 :
3595 0 : default:
3596 0 : return DTERR_BAD_FORMAT;
3597 : }
3598 :
3599 21086 : if (tmask & fmask)
3600 96 : return DTERR_BAD_FORMAT;
3601 20990 : fmask |= tmask;
3602 : }
3603 :
3604 : /* ensure that at least one time field has been found */
3605 8346 : if (fmask == 0)
3606 6 : return DTERR_BAD_FORMAT;
3607 :
3608 : /* finally, AGO negates everything */
3609 8340 : if (is_before)
3610 : {
3611 42 : if (itm_in->tm_usec == PG_INT64_MIN ||
3612 30 : itm_in->tm_mday == INT_MIN ||
3613 24 : itm_in->tm_mon == INT_MIN ||
3614 18 : itm_in->tm_year == INT_MIN)
3615 24 : return DTERR_FIELD_OVERFLOW;
3616 :
3617 18 : itm_in->tm_usec = -itm_in->tm_usec;
3618 18 : itm_in->tm_mday = -itm_in->tm_mday;
3619 18 : itm_in->tm_mon = -itm_in->tm_mon;
3620 18 : itm_in->tm_year = -itm_in->tm_year;
3621 : }
3622 :
3623 8316 : return 0;
3624 : }
3625 :
3626 :
3627 : /*
3628 : * Helper functions to avoid duplicated code in DecodeISO8601Interval.
3629 : *
3630 : * Parse a decimal value and break it into integer and fractional parts.
3631 : * Set *endptr to end+1 of the parsed substring.
3632 : * Returns 0 or DTERR code.
3633 : */
3634 : static int
3635 954 : ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
3636 : {
3637 : double val;
3638 :
3639 : /*
3640 : * Historically this has accepted anything that strtod() would take,
3641 : * notably including "e" notation, so continue doing that. This is
3642 : * slightly annoying because the precision of double is less than that of
3643 : * int64, so we would lose accuracy for inputs larger than 2^53 or so.
3644 : * However, historically we rejected inputs outside the int32 range,
3645 : * making that concern moot. What we do now is reject abs(val) above
3646 : * 1.0e15 (a round number a bit less than 2^50), so that any accepted
3647 : * value will have an exact integer part, and thereby a fraction part with
3648 : * abs(*fpart) less than 1. In the absence of field complaints it doesn't
3649 : * seem worth working harder.
3650 : */
3651 954 : if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
3652 0 : return DTERR_BAD_FORMAT;
3653 954 : errno = 0;
3654 954 : val = strtod(str, endptr);
3655 : /* did we not see anything that looks like a double? */
3656 954 : if (*endptr == str || errno != 0)
3657 6 : return DTERR_BAD_FORMAT;
3658 : /* watch out for overflow, including infinities; reject NaN too */
3659 948 : if (isnan(val) || val < -1.0e15 || val > 1.0e15)
3660 0 : return DTERR_FIELD_OVERFLOW;
3661 : /* be very sure we truncate towards zero (cf dtrunc()) */
3662 948 : if (val >= 0)
3663 732 : *ipart = (int64) floor(val);
3664 : else
3665 216 : *ipart = (int64) -floor(-val);
3666 948 : *fpart = val - *ipart;
3667 : /* Callers expect this to hold */
3668 : Assert(*fpart > -1.0 && *fpart < 1.0);
3669 948 : return 0;
3670 : }
3671 :
3672 : /*
3673 : * Determine number of integral digits in a valid ISO 8601 number field
3674 : * (we should ignore sign and any fraction part)
3675 : */
3676 : static int
3677 66 : ISO8601IntegerWidth(char *fieldstart)
3678 : {
3679 : /* We might have had a leading '-' */
3680 66 : if (*fieldstart == '-')
3681 18 : fieldstart++;
3682 66 : return strspn(fieldstart, "0123456789");
3683 : }
3684 :
3685 :
3686 : /* DecodeISO8601Interval()
3687 : * Decode an ISO 8601 time interval of the "format with designators"
3688 : * (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
3689 : * Examples: P1D for 1 day
3690 : * PT1H for 1 hour
3691 : * P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
3692 : * P0002-06-07T01:30:00 the same value in alternative format
3693 : *
3694 : * Returns 0 if successful, DTERR code if bogus input detected.
3695 : * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
3696 : * ISO8601, otherwise this could cause unexpected error messages.
3697 : * dtype and itm_in are output parameters.
3698 : *
3699 : * A couple exceptions from the spec:
3700 : * - a week field ('W') may coexist with other units
3701 : * - allows decimals in fields other than the least significant unit.
3702 : */
3703 : int
3704 534 : DecodeISO8601Interval(char *str,
3705 : int *dtype, struct pg_itm_in *itm_in)
3706 : {
3707 534 : bool datepart = true;
3708 534 : bool havefield = false;
3709 :
3710 534 : *dtype = DTK_DELTA;
3711 534 : ClearPgItmIn(itm_in);
3712 :
3713 534 : if (strlen(str) < 2 || str[0] != 'P')
3714 150 : return DTERR_BAD_FORMAT;
3715 :
3716 384 : str++;
3717 1122 : while (*str)
3718 : {
3719 : char *fieldstart;
3720 : int64 val;
3721 : double fval;
3722 : char unit;
3723 : int dterr;
3724 :
3725 1014 : if (*str == 'T') /* T indicates the beginning of the time part */
3726 : {
3727 198 : datepart = false;
3728 198 : havefield = false;
3729 198 : str++;
3730 240 : continue;
3731 : }
3732 :
3733 816 : fieldstart = str;
3734 816 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3735 816 : if (dterr)
3736 276 : return dterr;
3737 :
3738 : /*
3739 : * Note: we could step off the end of the string here. Code below
3740 : * *must* exit the loop if unit == '\0'.
3741 : */
3742 810 : unit = *str++;
3743 :
3744 810 : if (datepart)
3745 : {
3746 468 : switch (unit) /* before T: Y M W D */
3747 : {
3748 84 : case 'Y':
3749 84 : if (!AdjustYears(val, 1, itm_in) ||
3750 84 : !AdjustFractYears(fval, 1, itm_in))
3751 12 : return DTERR_FIELD_OVERFLOW;
3752 72 : break;
3753 108 : case 'M':
3754 108 : if (!AdjustMonths(val, itm_in) ||
3755 96 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3756 24 : return DTERR_FIELD_OVERFLOW;
3757 84 : break;
3758 54 : case 'W':
3759 54 : if (!AdjustDays(val, 7, itm_in) ||
3760 42 : !AdjustFractDays(fval, 7, itm_in))
3761 24 : return DTERR_FIELD_OVERFLOW;
3762 30 : break;
3763 132 : case 'D':
3764 132 : if (!AdjustDays(val, 1, itm_in) ||
3765 96 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3766 36 : return DTERR_FIELD_OVERFLOW;
3767 96 : break;
3768 30 : case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
3769 : case '\0':
3770 30 : if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
3771 : {
3772 6 : if (!AdjustYears(val / 10000, 1, itm_in) ||
3773 6 : !AdjustMonths((val / 100) % 100, itm_in) ||
3774 6 : !AdjustDays(val % 100, 1, itm_in) ||
3775 6 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3776 0 : return DTERR_FIELD_OVERFLOW;
3777 6 : if (unit == '\0')
3778 0 : return 0;
3779 6 : datepart = false;
3780 6 : havefield = false;
3781 6 : continue;
3782 : }
3783 : /* Else fall through to extended alternative format */
3784 : /* FALLTHROUGH */
3785 : case '-': /* ISO 8601 4.4.3.3 Alternative Format,
3786 : * Extended */
3787 84 : if (havefield)
3788 0 : return DTERR_BAD_FORMAT;
3789 :
3790 84 : if (!AdjustYears(val, 1, itm_in) ||
3791 72 : !AdjustFractYears(fval, 1, itm_in))
3792 12 : return DTERR_FIELD_OVERFLOW;
3793 72 : if (unit == '\0')
3794 6 : return 0;
3795 66 : if (unit == 'T')
3796 : {
3797 6 : datepart = false;
3798 6 : havefield = false;
3799 6 : continue;
3800 : }
3801 :
3802 60 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3803 60 : if (dterr)
3804 0 : return dterr;
3805 60 : if (!AdjustMonths(val, itm_in) ||
3806 54 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3807 6 : return DTERR_FIELD_OVERFLOW;
3808 54 : if (*str == '\0')
3809 6 : return 0;
3810 48 : if (*str == 'T')
3811 : {
3812 6 : datepart = false;
3813 6 : havefield = false;
3814 6 : continue;
3815 : }
3816 42 : if (*str != '-')
3817 0 : return DTERR_BAD_FORMAT;
3818 42 : str++;
3819 :
3820 42 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3821 42 : if (dterr)
3822 0 : return dterr;
3823 42 : if (!AdjustDays(val, 1, itm_in) ||
3824 36 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3825 6 : return DTERR_FIELD_OVERFLOW;
3826 36 : if (*str == '\0')
3827 12 : return 0;
3828 24 : if (*str == 'T')
3829 : {
3830 24 : datepart = false;
3831 24 : havefield = false;
3832 24 : continue;
3833 : }
3834 0 : return DTERR_BAD_FORMAT;
3835 0 : default:
3836 : /* not a valid date unit suffix */
3837 0 : return DTERR_BAD_FORMAT;
3838 : }
3839 : }
3840 : else
3841 : {
3842 342 : switch (unit) /* after T: H M S */
3843 : {
3844 108 : case 'H':
3845 108 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3846 36 : return DTERR_FIELD_OVERFLOW;
3847 72 : break;
3848 60 : case 'M':
3849 60 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3850 0 : return DTERR_FIELD_OVERFLOW;
3851 60 : break;
3852 96 : case 'S':
3853 96 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3854 12 : return DTERR_FIELD_OVERFLOW;
3855 84 : break;
3856 36 : case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
3857 36 : if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
3858 : {
3859 6 : if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
3860 6 : !AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
3861 6 : !AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
3862 6 : !AdjustFractMicroseconds(fval, 1, itm_in))
3863 0 : return DTERR_FIELD_OVERFLOW;
3864 6 : return 0;
3865 : }
3866 : /* Else fall through to extended alternative format */
3867 : /* FALLTHROUGH */
3868 : case ':': /* ISO 8601 4.4.3.3 Alternative Format,
3869 : * Extended */
3870 72 : if (havefield)
3871 0 : return DTERR_BAD_FORMAT;
3872 :
3873 72 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3874 30 : return DTERR_FIELD_OVERFLOW;
3875 42 : if (unit == '\0')
3876 18 : return 0;
3877 :
3878 24 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3879 24 : if (dterr)
3880 0 : return dterr;
3881 24 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3882 6 : return DTERR_FIELD_OVERFLOW;
3883 18 : if (*str == '\0')
3884 6 : return 0;
3885 12 : if (*str != ':')
3886 0 : return DTERR_BAD_FORMAT;
3887 12 : str++;
3888 :
3889 12 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3890 12 : if (dterr)
3891 0 : return dterr;
3892 12 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3893 0 : return DTERR_FIELD_OVERFLOW;
3894 12 : if (*str == '\0')
3895 12 : return 0;
3896 0 : return DTERR_BAD_FORMAT;
3897 :
3898 0 : default:
3899 : /* not a valid time unit suffix */
3900 0 : return DTERR_BAD_FORMAT;
3901 : }
3902 : }
3903 :
3904 498 : havefield = true;
3905 : }
3906 :
3907 108 : return 0;
3908 : }
3909 :
3910 :
3911 : /* DecodeUnits()
3912 : * Decode text string using lookup table.
3913 : *
3914 : * This routine recognizes keywords associated with time interval units.
3915 : *
3916 : * Given string must be lowercased already.
3917 : *
3918 : * Implement a cache lookup since it is likely that dates
3919 : * will be related in format.
3920 : */
3921 : int
3922 62360 : DecodeUnits(int field, const char *lowtoken, int *val)
3923 : {
3924 : int type;
3925 : const datetkn *tp;
3926 :
3927 62360 : tp = deltacache[field];
3928 : /* use strncmp so that we match truncated tokens */
3929 62360 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3930 : {
3931 51604 : tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3932 : }
3933 62360 : if (tp == NULL)
3934 : {
3935 33898 : type = UNKNOWN_FIELD;
3936 33898 : *val = 0;
3937 : }
3938 : else
3939 : {
3940 28462 : deltacache[field] = tp;
3941 28462 : type = tp->type;
3942 28462 : *val = tp->value;
3943 : }
3944 :
3945 62360 : return type;
3946 : } /* DecodeUnits() */
3947 :
3948 : /*
3949 : * Report an error detected by one of the datetime input processing routines.
3950 : *
3951 : * dterr is the error code, and *extra contains any auxiliary info we need
3952 : * for the error report. extra can be NULL if not needed for the particular
3953 : * dterr value.
3954 : *
3955 : * str is the original input string, and datatype is the name of the datatype
3956 : * we were trying to accept. (For some DTERR codes, these are not used and
3957 : * can be NULL.)
3958 : *
3959 : * If escontext points to an ErrorSaveContext node, that is filled instead
3960 : * of throwing an error.
3961 : *
3962 : * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
3963 : * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
3964 : * separate SQLSTATE codes, so ...
3965 : */
3966 : void
3967 1602 : DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
3968 : const char *str, const char *datatype,
3969 : Node *escontext)
3970 : {
3971 1602 : switch (dterr)
3972 : {
3973 174 : case DTERR_FIELD_OVERFLOW:
3974 174 : errsave(escontext,
3975 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
3976 : errmsg("date/time field value out of range: \"%s\"",
3977 : str)));
3978 24 : break;
3979 180 : case DTERR_MD_FIELD_OVERFLOW:
3980 : /* <nanny>same as above, but add hint about DateStyle</nanny> */
3981 180 : errsave(escontext,
3982 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
3983 : errmsg("date/time field value out of range: \"%s\"",
3984 : str),
3985 : errhint("Perhaps you need a different \"datestyle\" setting.")));
3986 0 : break;
3987 720 : case DTERR_INTERVAL_OVERFLOW:
3988 720 : errsave(escontext,
3989 : (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
3990 : errmsg("interval field value out of range: \"%s\"",
3991 : str)));
3992 0 : break;
3993 12 : case DTERR_TZDISP_OVERFLOW:
3994 12 : errsave(escontext,
3995 : (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
3996 : errmsg("time zone displacement out of range: \"%s\"",
3997 : str)));
3998 0 : break;
3999 36 : case DTERR_BAD_TIMEZONE:
4000 36 : errsave(escontext,
4001 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4002 : errmsg("time zone \"%s\" not recognized",
4003 : extra->dtee_timezone)));
4004 24 : break;
4005 0 : case DTERR_BAD_ZONE_ABBREV:
4006 0 : errsave(escontext,
4007 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
4008 : errmsg("time zone \"%s\" not recognized",
4009 : extra->dtee_timezone),
4010 : errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4011 : extra->dtee_abbrev)));
4012 0 : break;
4013 480 : case DTERR_BAD_FORMAT:
4014 : default:
4015 480 : errsave(escontext,
4016 : (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
4017 : errmsg("invalid input syntax for type %s: \"%s\"",
4018 : datatype, str)));
4019 84 : break;
4020 : }
4021 132 : }
4022 :
4023 : /* datebsearch()
4024 : * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
4025 : * is WAY faster than the generic bsearch().
4026 : */
4027 : static const datetkn *
4028 69066 : datebsearch(const char *key, const datetkn *base, int nel)
4029 : {
4030 69066 : if (nel > 0)
4031 : {
4032 69066 : const datetkn *last = base + nel - 1,
4033 : *position;
4034 : int result;
4035 :
4036 439118 : while (last >= base)
4037 : {
4038 397124 : position = base + ((last - base) >> 1);
4039 : /* precheck the first character for a bit of extra speed */
4040 397124 : result = (int) key[0] - (int) position->token[0];
4041 397124 : if (result == 0)
4042 : {
4043 : /* use strncmp so that we match truncated tokens */
4044 103920 : result = strncmp(key, position->token, TOKMAXLEN);
4045 103920 : if (result == 0)
4046 27072 : return position;
4047 : }
4048 370052 : if (result < 0)
4049 184476 : last = position - 1;
4050 : else
4051 185576 : base = position + 1;
4052 : }
4053 : }
4054 41994 : return NULL;
4055 : }
4056 :
4057 : /* EncodeTimezone()
4058 : * Copies representation of a numeric timezone offset to str.
4059 : *
4060 : * Returns a pointer to the new end of string. No NUL terminator is put
4061 : * there; callers are responsible for NUL terminating str themselves.
4062 : */
4063 : static char *
4064 59762 : EncodeTimezone(char *str, int tz, int style)
4065 : {
4066 : int hour,
4067 : min,
4068 : sec;
4069 :
4070 59762 : sec = abs(tz);
4071 59762 : min = sec / SECS_PER_MINUTE;
4072 59762 : sec -= min * SECS_PER_MINUTE;
4073 59762 : hour = min / MINS_PER_HOUR;
4074 59762 : min -= hour * MINS_PER_HOUR;
4075 :
4076 : /* TZ is negated compared to sign we wish to display ... */
4077 59762 : *str++ = (tz <= 0 ? '+' : '-');
4078 :
4079 59762 : if (sec != 0)
4080 : {
4081 0 : str = pg_ultostr_zeropad(str, hour, 2);
4082 0 : *str++ = ':';
4083 0 : str = pg_ultostr_zeropad(str, min, 2);
4084 0 : *str++ = ':';
4085 0 : str = pg_ultostr_zeropad(str, sec, 2);
4086 : }
4087 59762 : else if (min != 0 || style == USE_XSD_DATES)
4088 : {
4089 312 : str = pg_ultostr_zeropad(str, hour, 2);
4090 312 : *str++ = ':';
4091 312 : str = pg_ultostr_zeropad(str, min, 2);
4092 : }
4093 : else
4094 59450 : str = pg_ultostr_zeropad(str, hour, 2);
4095 59762 : return str;
4096 : }
4097 :
4098 : /* EncodeDateOnly()
4099 : * Encode date as local time.
4100 : */
4101 : void
4102 8702 : EncodeDateOnly(struct pg_tm *tm, int style, char *str)
4103 : {
4104 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4105 :
4106 8702 : switch (style)
4107 : {
4108 4494 : case USE_ISO_DATES:
4109 : case USE_XSD_DATES:
4110 : /* compatible with ISO date formats */
4111 4494 : str = pg_ultostr_zeropad(str,
4112 4494 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4113 4494 : *str++ = '-';
4114 4494 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4115 4494 : *str++ = '-';
4116 4494 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4117 4494 : break;
4118 :
4119 0 : case USE_SQL_DATES:
4120 : /* compatible with Oracle/Ingres date formats */
4121 0 : if (DateOrder == DATEORDER_DMY)
4122 : {
4123 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4124 0 : *str++ = '/';
4125 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4126 : }
4127 : else
4128 : {
4129 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4130 0 : *str++ = '/';
4131 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4132 : }
4133 0 : *str++ = '/';
4134 0 : str = pg_ultostr_zeropad(str,
4135 0 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4136 0 : break;
4137 :
4138 8 : case USE_GERMAN_DATES:
4139 : /* German-style date format */
4140 8 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4141 8 : *str++ = '.';
4142 8 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4143 8 : *str++ = '.';
4144 8 : str = pg_ultostr_zeropad(str,
4145 8 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4146 8 : break;
4147 :
4148 4200 : case USE_POSTGRES_DATES:
4149 : default:
4150 : /* traditional date-only style for Postgres */
4151 4200 : if (DateOrder == DATEORDER_DMY)
4152 : {
4153 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4154 0 : *str++ = '-';
4155 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4156 : }
4157 : else
4158 : {
4159 4200 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4160 4200 : *str++ = '-';
4161 4200 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4162 : }
4163 4200 : *str++ = '-';
4164 4200 : str = pg_ultostr_zeropad(str,
4165 4200 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4166 4200 : break;
4167 : }
4168 :
4169 8702 : if (tm->tm_year <= 0)
4170 : {
4171 80 : memcpy(str, " BC", 3); /* Don't copy NUL */
4172 80 : str += 3;
4173 : }
4174 8702 : *str = '\0';
4175 8702 : }
4176 :
4177 :
4178 : /* EncodeTimeOnly()
4179 : * Encode time fields only.
4180 : *
4181 : * tm and fsec are the value to encode, print_tz determines whether to include
4182 : * a time zone (the difference between time and timetz types), tz is the
4183 : * numeric time zone offset, style is the date style, str is where to write the
4184 : * output.
4185 : */
4186 : void
4187 10932 : EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
4188 : {
4189 10932 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4190 10932 : *str++ = ':';
4191 10932 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4192 10932 : *str++ = ':';
4193 10932 : str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
4194 10932 : if (print_tz)
4195 5612 : str = EncodeTimezone(str, tz, style);
4196 10932 : *str = '\0';
4197 10932 : }
4198 :
4199 :
4200 : /* EncodeDateTime()
4201 : * Encode date and time interpreted as local time.
4202 : *
4203 : * tm and fsec are the value to encode, print_tz determines whether to include
4204 : * a time zone (the difference between timestamp and timestamptz types), tz is
4205 : * the numeric time zone offset, tzn is the textual time zone, which if
4206 : * specified will be used instead of tz by some styles, style is the date
4207 : * style, str is where to write the output.
4208 : *
4209 : * Supported date styles:
4210 : * Postgres - day mon hh:mm:ss yyyy tz
4211 : * SQL - mm/dd/yyyy hh:mm:ss.ss tz
4212 : * ISO - yyyy-mm-dd hh:mm:ss+/-tz
4213 : * German - dd.mm.yyyy hh:mm:ss tz
4214 : * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
4215 : */
4216 : void
4217 114992 : EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
4218 : {
4219 : int day;
4220 :
4221 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4222 :
4223 : /*
4224 : * Negative tm_isdst means we have no valid time zone translation.
4225 : */
4226 114992 : if (tm->tm_isdst < 0)
4227 43082 : print_tz = false;
4228 :
4229 114992 : switch (style)
4230 : {
4231 86536 : case USE_ISO_DATES:
4232 : case USE_XSD_DATES:
4233 : /* Compatible with ISO-8601 date formats */
4234 86536 : str = pg_ultostr_zeropad(str,
4235 86536 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4236 86536 : *str++ = '-';
4237 86536 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4238 86536 : *str++ = '-';
4239 86536 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4240 86536 : *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
4241 86536 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4242 86536 : *str++ = ':';
4243 86536 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4244 86536 : *str++ = ':';
4245 86536 : str = AppendTimestampSeconds(str, tm, fsec);
4246 86536 : if (print_tz)
4247 54150 : str = EncodeTimezone(str, tz, style);
4248 86536 : break;
4249 :
4250 780 : case USE_SQL_DATES:
4251 : /* Compatible with Oracle/Ingres date formats */
4252 780 : if (DateOrder == DATEORDER_DMY)
4253 : {
4254 384 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4255 384 : *str++ = '/';
4256 384 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4257 : }
4258 : else
4259 : {
4260 396 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4261 396 : *str++ = '/';
4262 396 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4263 : }
4264 780 : *str++ = '/';
4265 780 : str = pg_ultostr_zeropad(str,
4266 780 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4267 780 : *str++ = ' ';
4268 780 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4269 780 : *str++ = ':';
4270 780 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4271 780 : *str++ = ':';
4272 780 : str = AppendTimestampSeconds(str, tm, fsec);
4273 :
4274 : /*
4275 : * Note: the uses of %.*s in this function would be risky if the
4276 : * timezone names ever contain non-ASCII characters, since we are
4277 : * not being careful to do encoding-aware clipping. However, all
4278 : * TZ abbreviations in the IANA database are plain ASCII.
4279 : */
4280 780 : if (print_tz)
4281 : {
4282 18 : if (tzn)
4283 : {
4284 18 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4285 18 : str += strlen(str);
4286 : }
4287 : else
4288 0 : str = EncodeTimezone(str, tz, style);
4289 : }
4290 780 : break;
4291 :
4292 24 : case USE_GERMAN_DATES:
4293 : /* German variant on European style */
4294 24 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4295 24 : *str++ = '.';
4296 24 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4297 24 : *str++ = '.';
4298 24 : str = pg_ultostr_zeropad(str,
4299 24 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4300 24 : *str++ = ' ';
4301 24 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4302 24 : *str++ = ':';
4303 24 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4304 24 : *str++ = ':';
4305 24 : str = AppendTimestampSeconds(str, tm, fsec);
4306 :
4307 24 : if (print_tz)
4308 : {
4309 24 : if (tzn)
4310 : {
4311 24 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4312 24 : str += strlen(str);
4313 : }
4314 : else
4315 0 : str = EncodeTimezone(str, tz, style);
4316 : }
4317 24 : break;
4318 :
4319 27652 : case USE_POSTGRES_DATES:
4320 : default:
4321 : /* Backward-compatible with traditional Postgres abstime dates */
4322 27652 : day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4323 27652 : tm->tm_wday = j2day(day);
4324 27652 : memcpy(str, days[tm->tm_wday], 3);
4325 27652 : str += 3;
4326 27652 : *str++ = ' ';
4327 27652 : if (DateOrder == DATEORDER_DMY)
4328 : {
4329 398 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4330 398 : *str++ = ' ';
4331 398 : memcpy(str, months[tm->tm_mon - 1], 3);
4332 398 : str += 3;
4333 : }
4334 : else
4335 : {
4336 27254 : memcpy(str, months[tm->tm_mon - 1], 3);
4337 27254 : str += 3;
4338 27254 : *str++ = ' ';
4339 27254 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4340 : }
4341 27652 : *str++ = ' ';
4342 27652 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4343 27652 : *str++ = ':';
4344 27652 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4345 27652 : *str++ = ':';
4346 27652 : str = AppendTimestampSeconds(str, tm, fsec);
4347 27652 : *str++ = ' ';
4348 27652 : str = pg_ultostr_zeropad(str,
4349 27652 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4350 :
4351 27652 : if (print_tz)
4352 : {
4353 17718 : if (tzn)
4354 : {
4355 17718 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4356 17718 : str += strlen(str);
4357 : }
4358 : else
4359 : {
4360 : /*
4361 : * We have a time zone, but no string version. Use the
4362 : * numeric form, but be sure to include a leading space to
4363 : * avoid formatting something which would be rejected by
4364 : * the date/time parser later. - thomas 2001-10-19
4365 : */
4366 0 : *str++ = ' ';
4367 0 : str = EncodeTimezone(str, tz, style);
4368 : }
4369 : }
4370 27652 : break;
4371 : }
4372 :
4373 114992 : if (tm->tm_year <= 0)
4374 : {
4375 274 : memcpy(str, " BC", 3); /* Don't copy NUL */
4376 274 : str += 3;
4377 : }
4378 114992 : *str = '\0';
4379 114992 : }
4380 :
4381 :
4382 : /*
4383 : * Helper functions to avoid duplicated code in EncodeInterval.
4384 : */
4385 :
4386 : /* Append an ISO-8601-style interval field, but only if value isn't zero */
4387 : static char *
4388 210 : AddISO8601IntPart(char *cp, int64 value, char units)
4389 : {
4390 210 : if (value == 0)
4391 54 : return cp;
4392 156 : sprintf(cp, "%lld%c", (long long) value, units);
4393 156 : return cp + strlen(cp);
4394 : }
4395 :
4396 : /* Append a postgres-style interval field, but only if value isn't zero */
4397 : static char *
4398 13548 : AddPostgresIntPart(char *cp, int64 value, const char *units,
4399 : bool *is_zero, bool *is_before)
4400 : {
4401 13548 : if (value == 0)
4402 7312 : return cp;
4403 18708 : sprintf(cp, "%s%s%lld %s%s",
4404 6236 : (!*is_zero) ? " " : "",
4405 6236 : (*is_before && value > 0) ? "+" : "",
4406 : (long long) value,
4407 : units,
4408 : (value != 1) ? "s" : "");
4409 :
4410 : /*
4411 : * Each nonzero field sets is_before for (only) the next one. This is a
4412 : * tad bizarre but it's how it worked before...
4413 : */
4414 6236 : *is_before = (value < 0);
4415 6236 : *is_zero = false;
4416 6236 : return cp + strlen(cp);
4417 : }
4418 :
4419 : /* Append a verbose-style interval field, but only if value isn't zero */
4420 : static char *
4421 39810 : AddVerboseIntPart(char *cp, int64 value, const char *units,
4422 : bool *is_zero, bool *is_before)
4423 : {
4424 39810 : if (value == 0)
4425 26202 : return cp;
4426 : /* first nonzero value sets is_before */
4427 13608 : if (*is_zero)
4428 : {
4429 7350 : *is_before = (value < 0);
4430 7350 : value = i64abs(value);
4431 : }
4432 6258 : else if (*is_before)
4433 1296 : value = -value;
4434 13608 : sprintf(cp, " %lld %s%s", (long long) value, units, (value == 1) ? "" : "s");
4435 13608 : *is_zero = false;
4436 13608 : return cp + strlen(cp);
4437 : }
4438 :
4439 :
4440 : /* EncodeInterval()
4441 : * Interpret time structure as a delta time and convert to string.
4442 : *
4443 : * Support "traditional Postgres" and ISO-8601 styles.
4444 : * Actually, afaik ISO does not address time interval formatting,
4445 : * but this looks similar to the spec for absolute date/time.
4446 : * - thomas 1998-04-30
4447 : *
4448 : * Actually, afaik, ISO 8601 does specify formats for "time
4449 : * intervals...[of the]...format with time-unit designators", which
4450 : * are pretty ugly. The format looks something like
4451 : * P1Y1M1DT1H1M1.12345S
4452 : * but useful for exchanging data with computers instead of humans.
4453 : * - ron 2003-07-14
4454 : *
4455 : * And ISO's SQL 2008 standard specifies standards for
4456 : * "year-month literal"s (that look like '2-3') and
4457 : * "day-time literal"s (that look like ('4 5:6:7')
4458 : */
4459 : void
4460 12652 : EncodeInterval(struct pg_itm *itm, int style, char *str)
4461 : {
4462 12652 : char *cp = str;
4463 12652 : int year = itm->tm_year;
4464 12652 : int mon = itm->tm_mon;
4465 12652 : int64 mday = itm->tm_mday; /* tm_mday could be INT_MIN */
4466 12652 : int64 hour = itm->tm_hour;
4467 12652 : int min = itm->tm_min;
4468 12652 : int sec = itm->tm_sec;
4469 12652 : int fsec = itm->tm_usec;
4470 12652 : bool is_before = false;
4471 12652 : bool is_zero = true;
4472 :
4473 : /*
4474 : * The sign of year and month are guaranteed to match, since they are
4475 : * stored internally as "month". But we'll need to check for is_before and
4476 : * is_zero when determining the signs of day and hour/minute/seconds
4477 : * fields.
4478 : */
4479 12652 : switch (style)
4480 : {
4481 : /* SQL Standard interval format */
4482 126 : case INTSTYLE_SQL_STANDARD:
4483 : {
4484 96 : bool has_negative = year < 0 || mon < 0 ||
4485 66 : mday < 0 || hour < 0 ||
4486 222 : min < 0 || sec < 0 || fsec < 0;
4487 102 : bool has_positive = year > 0 || mon > 0 ||
4488 66 : mday > 0 || hour > 0 ||
4489 228 : min > 0 || sec > 0 || fsec > 0;
4490 126 : bool has_year_month = year != 0 || mon != 0;
4491 42 : bool has_day_time = mday != 0 || hour != 0 ||
4492 168 : min != 0 || sec != 0 || fsec != 0;
4493 126 : bool has_day = mday != 0;
4494 222 : bool sql_standard_value = !(has_negative && has_positive) &&
4495 96 : !(has_year_month && has_day_time);
4496 :
4497 : /*
4498 : * SQL Standard wants only 1 "<sign>" preceding the whole
4499 : * interval ... but can't do that if mixed signs.
4500 : */
4501 126 : if (has_negative && sql_standard_value)
4502 : {
4503 30 : *cp++ = '-';
4504 30 : year = -year;
4505 30 : mon = -mon;
4506 30 : mday = -mday;
4507 30 : hour = -hour;
4508 30 : min = -min;
4509 30 : sec = -sec;
4510 30 : fsec = -fsec;
4511 : }
4512 :
4513 126 : if (!has_negative && !has_positive)
4514 : {
4515 12 : sprintf(cp, "0");
4516 : }
4517 114 : else if (!sql_standard_value)
4518 : {
4519 : /*
4520 : * For non sql-standard interval values, force outputting
4521 : * the signs to avoid ambiguities with intervals with
4522 : * mixed sign components.
4523 : */
4524 54 : char year_sign = (year < 0 || mon < 0) ? '-' : '+';
4525 54 : char day_sign = (mday < 0) ? '-' : '+';
4526 78 : char sec_sign = (hour < 0 || min < 0 ||
4527 24 : sec < 0 || fsec < 0) ? '-' : '+';
4528 :
4529 54 : sprintf(cp, "%c%d-%d %c%lld %c%lld:%02d:",
4530 : year_sign, abs(year), abs(mon),
4531 54 : day_sign, (long long) i64abs(mday),
4532 54 : sec_sign, (long long) i64abs(hour), abs(min));
4533 54 : cp += strlen(cp);
4534 54 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4535 54 : *cp = '\0';
4536 : }
4537 60 : else if (has_year_month)
4538 : {
4539 18 : sprintf(cp, "%d-%d", year, mon);
4540 : }
4541 42 : else if (has_day)
4542 : {
4543 30 : sprintf(cp, "%lld %lld:%02d:",
4544 : (long long) mday, (long long) hour, min);
4545 30 : cp += strlen(cp);
4546 30 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4547 30 : *cp = '\0';
4548 : }
4549 : else
4550 : {
4551 12 : sprintf(cp, "%lld:%02d:", (long long) hour, min);
4552 12 : cp += strlen(cp);
4553 12 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4554 12 : *cp = '\0';
4555 : }
4556 : }
4557 126 : break;
4558 :
4559 : /* ISO 8601 "time-intervals by duration only" */
4560 48 : case INTSTYLE_ISO_8601:
4561 : /* special-case zero to avoid printing nothing */
4562 48 : if (year == 0 && mon == 0 && mday == 0 &&
4563 6 : hour == 0 && min == 0 && sec == 0 && fsec == 0)
4564 : {
4565 6 : sprintf(cp, "PT0S");
4566 6 : break;
4567 : }
4568 42 : *cp++ = 'P';
4569 42 : cp = AddISO8601IntPart(cp, year, 'Y');
4570 42 : cp = AddISO8601IntPart(cp, mon, 'M');
4571 42 : cp = AddISO8601IntPart(cp, mday, 'D');
4572 42 : if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
4573 36 : *cp++ = 'T';
4574 42 : cp = AddISO8601IntPart(cp, hour, 'H');
4575 42 : cp = AddISO8601IntPart(cp, min, 'M');
4576 42 : if (sec != 0 || fsec != 0)
4577 : {
4578 36 : if (sec < 0 || fsec < 0)
4579 12 : *cp++ = '-';
4580 36 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4581 36 : *cp++ = 'S';
4582 36 : *cp++ = '\0';
4583 : }
4584 42 : break;
4585 :
4586 : /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
4587 4516 : case INTSTYLE_POSTGRES:
4588 4516 : cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
4589 :
4590 : /*
4591 : * Ideally we should spell out "month" like we do for "year" and
4592 : * "day". However, for backward compatibility, we can't easily
4593 : * fix this. bjm 2011-05-24
4594 : */
4595 4516 : cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
4596 4516 : cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
4597 4516 : if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
4598 : {
4599 3704 : bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
4600 :
4601 7408 : sprintf(cp, "%s%s%02lld:%02d:",
4602 3704 : is_zero ? "" : " ",
4603 3532 : (minus ? "-" : (is_before ? "+" : "")),
4604 3704 : (long long) i64abs(hour), abs(min));
4605 3704 : cp += strlen(cp);
4606 3704 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4607 3704 : *cp = '\0';
4608 : }
4609 4516 : break;
4610 :
4611 : /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
4612 7962 : case INTSTYLE_POSTGRES_VERBOSE:
4613 : default:
4614 7962 : strcpy(cp, "@");
4615 7962 : cp++;
4616 7962 : cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
4617 7962 : cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
4618 7962 : cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
4619 7962 : cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
4620 7962 : cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
4621 7962 : if (sec != 0 || fsec != 0)
4622 : {
4623 3240 : *cp++ = ' ';
4624 3240 : if (sec < 0 || (sec == 0 && fsec < 0))
4625 : {
4626 996 : if (is_zero)
4627 342 : is_before = true;
4628 654 : else if (!is_before)
4629 6 : *cp++ = '-';
4630 : }
4631 2244 : else if (is_before)
4632 12 : *cp++ = '-';
4633 3240 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4634 : /* We output "ago", not negatives, so use abs(). */
4635 3240 : sprintf(cp, " sec%s",
4636 3240 : (abs(sec) != 1 || fsec != 0) ? "s" : "");
4637 3240 : is_zero = false;
4638 : }
4639 : /* identically zero? then put in a unitless zero... */
4640 7962 : if (is_zero)
4641 224 : strcat(cp, " 0");
4642 7962 : if (is_before)
4643 1314 : strcat(cp, " ago");
4644 7962 : break;
4645 : }
4646 12652 : }
4647 :
4648 :
4649 : /*
4650 : * We've been burnt by stupid errors in the ordering of the datetkn tables
4651 : * once too often. Arrange to check them during postmaster start.
4652 : */
4653 : static bool
4654 2396 : CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
4655 : {
4656 2396 : bool ok = true;
4657 : int i;
4658 :
4659 161730 : for (i = 0; i < nel; i++)
4660 : {
4661 : /* check for token strings that don't fit */
4662 159334 : if (strlen(base[i].token) > TOKMAXLEN)
4663 : {
4664 : /* %.*s is safe since all our tokens are ASCII */
4665 0 : elog(LOG, "token too long in %s table: \"%.*s\"",
4666 : tablename,
4667 : TOKMAXLEN + 1, base[i].token);
4668 0 : ok = false;
4669 0 : break; /* don't risk applying strcmp */
4670 : }
4671 : /* check for out of order */
4672 159334 : if (i > 0 &&
4673 156938 : strcmp(base[i - 1].token, base[i].token) >= 0)
4674 : {
4675 0 : elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4676 : tablename,
4677 : base[i - 1].token,
4678 : base[i].token);
4679 0 : ok = false;
4680 : }
4681 : }
4682 2396 : return ok;
4683 : }
4684 :
4685 : bool
4686 1198 : CheckDateTokenTables(void)
4687 : {
4688 1198 : bool ok = true;
4689 :
4690 : Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
4691 : Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
4692 :
4693 1198 : ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
4694 1198 : ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4695 1198 : return ok;
4696 : }
4697 :
4698 : /*
4699 : * Common code for temporal prosupport functions: simplify, if possible,
4700 : * a call to a temporal type's length-coercion function.
4701 : *
4702 : * Types time, timetz, timestamp and timestamptz each have a range of allowed
4703 : * precisions. An unspecified precision is rigorously equivalent to the
4704 : * highest specifiable precision. We can replace the function call with a
4705 : * no-op RelabelType if it is coercing to the same or higher precision as the
4706 : * input is known to have.
4707 : *
4708 : * The input Node is always a FuncExpr, but to reduce the #include footprint
4709 : * of datetime.h, we declare it as Node *.
4710 : *
4711 : * Note: timestamp_scale throws an error when the typmod is out of range, but
4712 : * we can't get there from a cast: our typmodin will have caught it already.
4713 : */
4714 : Node *
4715 24 : TemporalSimplify(int32 max_precis, Node *node)
4716 : {
4717 24 : FuncExpr *expr = castNode(FuncExpr, node);
4718 24 : Node *ret = NULL;
4719 : Node *typmod;
4720 :
4721 : Assert(list_length(expr->args) >= 2);
4722 :
4723 24 : typmod = (Node *) lsecond(expr->args);
4724 :
4725 24 : if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
4726 : {
4727 24 : Node *source = (Node *) linitial(expr->args);
4728 24 : int32 old_precis = exprTypmod(source);
4729 24 : int32 new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
4730 :
4731 24 : if (new_precis < 0 || new_precis == max_precis ||
4732 0 : (old_precis >= 0 && new_precis >= old_precis))
4733 0 : ret = relabel_to_typmod(source, new_precis);
4734 : }
4735 :
4736 24 : return ret;
4737 : }
4738 :
4739 : /*
4740 : * This function gets called during timezone config file load or reload
4741 : * to create the final array of timezone tokens. The argument array
4742 : * is already sorted in name order.
4743 : *
4744 : * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
4745 : * chunk) or NULL on alloc failure. No other error conditions are defined.
4746 : */
4747 : TimeZoneAbbrevTable *
4748 13382 : ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
4749 : {
4750 : TimeZoneAbbrevTable *tbl;
4751 : Size tbl_size;
4752 : int i;
4753 :
4754 : /* Space for fixed fields and datetkn array */
4755 13382 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4756 13382 : n * sizeof(datetkn);
4757 13382 : tbl_size = MAXALIGN(tbl_size);
4758 : /* Count up space for dynamic abbreviations */
4759 2636266 : for (i = 0; i < n; i++)
4760 : {
4761 2622884 : struct tzEntry *abbr = abbrevs + i;
4762 :
4763 2622884 : if (abbr->zone != NULL)
4764 : {
4765 : Size dsize;
4766 :
4767 682476 : dsize = offsetof(DynamicZoneAbbrev, zone) +
4768 682476 : strlen(abbr->zone) + 1;
4769 682476 : tbl_size += MAXALIGN(dsize);
4770 : }
4771 : }
4772 :
4773 : /* Alloc the result ... */
4774 13382 : tbl = guc_malloc(LOG, tbl_size);
4775 13382 : if (!tbl)
4776 0 : return NULL;
4777 :
4778 : /* ... and fill it in */
4779 13382 : tbl->tblsize = tbl_size;
4780 13382 : tbl->numabbrevs = n;
4781 : /* in this loop, tbl_size reprises the space calculation above */
4782 13382 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4783 13382 : n * sizeof(datetkn);
4784 13382 : tbl_size = MAXALIGN(tbl_size);
4785 2636266 : for (i = 0; i < n; i++)
4786 : {
4787 2622884 : struct tzEntry *abbr = abbrevs + i;
4788 2622884 : datetkn *dtoken = tbl->abbrevs + i;
4789 :
4790 : /* use strlcpy to truncate name if necessary */
4791 2622884 : strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
4792 2622884 : if (abbr->zone != NULL)
4793 : {
4794 : /* Allocate a DynamicZoneAbbrev for this abbreviation */
4795 : DynamicZoneAbbrev *dtza;
4796 : Size dsize;
4797 :
4798 682476 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
4799 682476 : dtza->tz = NULL;
4800 682476 : strcpy(dtza->zone, abbr->zone);
4801 :
4802 682476 : dtoken->type = DYNTZ;
4803 : /* value is offset from table start to DynamicZoneAbbrev */
4804 682476 : dtoken->value = (int32) tbl_size;
4805 :
4806 682476 : dsize = offsetof(DynamicZoneAbbrev, zone) +
4807 682476 : strlen(abbr->zone) + 1;
4808 682476 : tbl_size += MAXALIGN(dsize);
4809 : }
4810 : else
4811 : {
4812 1940408 : dtoken->type = abbr->is_dst ? DTZ : TZ;
4813 1940408 : dtoken->value = abbr->offset;
4814 : }
4815 : }
4816 :
4817 : /* Assert the two loops above agreed on size calculations */
4818 : Assert(tbl->tblsize == tbl_size);
4819 :
4820 : /* Check the ordering, if testing */
4821 : Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
4822 :
4823 13382 : return tbl;
4824 : }
4825 :
4826 : /*
4827 : * Install a TimeZoneAbbrevTable as the active table.
4828 : *
4829 : * Caller is responsible that the passed table doesn't go away while in use.
4830 : */
4831 : void
4832 13206 : InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
4833 : {
4834 13206 : zoneabbrevtbl = tbl;
4835 : /* reset abbrevcache, which may contain pointers into old table */
4836 13206 : memset(abbrevcache, 0, sizeof(abbrevcache));
4837 13206 : }
4838 :
4839 : /*
4840 : * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
4841 : *
4842 : * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
4843 : */
4844 : static pg_tz *
4845 1176 : FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
4846 : DateTimeErrorExtra *extra)
4847 : {
4848 : DynamicZoneAbbrev *dtza;
4849 :
4850 : /* Just some sanity checks to prevent indexing off into nowhere */
4851 : Assert(tp->type == DYNTZ);
4852 : Assert(tp->value > 0 && tp->value < tbl->tblsize);
4853 :
4854 1176 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
4855 :
4856 : /* Look up the underlying zone if we haven't already */
4857 1176 : if (dtza->tz == NULL)
4858 : {
4859 930 : dtza->tz = pg_tzset(dtza->zone);
4860 930 : if (dtza->tz == NULL)
4861 : {
4862 : /* Ooops, bogus zone name in config file entry */
4863 0 : extra->dtee_timezone = dtza->zone;
4864 0 : extra->dtee_abbrev = tp->token;
4865 : }
4866 : }
4867 1176 : return dtza->tz;
4868 : }
4869 :
4870 :
4871 : /*
4872 : * This set-returning function reads all the available time zone abbreviations
4873 : * and returns a set of (abbrev, utc_offset, is_dst).
4874 : */
4875 : Datum
4876 3558 : pg_timezone_abbrevs(PG_FUNCTION_ARGS)
4877 : {
4878 : FuncCallContext *funcctx;
4879 : int *pindex;
4880 : Datum result;
4881 : HeapTuple tuple;
4882 : Datum values[3];
4883 3558 : bool nulls[3] = {0};
4884 : const datetkn *tp;
4885 : char buffer[TOKMAXLEN + 1];
4886 : int gmtoffset;
4887 : bool is_dst;
4888 : unsigned char *p;
4889 : struct pg_itm_in itm_in;
4890 : Interval *resInterval;
4891 :
4892 : /* stuff done only on the first call of the function */
4893 3558 : if (SRF_IS_FIRSTCALL())
4894 : {
4895 : TupleDesc tupdesc;
4896 : MemoryContext oldcontext;
4897 :
4898 : /* create a function context for cross-call persistence */
4899 18 : funcctx = SRF_FIRSTCALL_INIT();
4900 :
4901 : /*
4902 : * switch to memory context appropriate for multiple function calls
4903 : */
4904 18 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4905 :
4906 : /* allocate memory for user context */
4907 18 : pindex = (int *) palloc(sizeof(int));
4908 18 : *pindex = 0;
4909 18 : funcctx->user_fctx = (void *) pindex;
4910 :
4911 18 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
4912 0 : elog(ERROR, "return type must be a row type");
4913 18 : funcctx->tuple_desc = tupdesc;
4914 :
4915 18 : MemoryContextSwitchTo(oldcontext);
4916 : }
4917 :
4918 : /* stuff done on every call of the function */
4919 3558 : funcctx = SRF_PERCALL_SETUP();
4920 3558 : pindex = (int *) funcctx->user_fctx;
4921 :
4922 3558 : if (zoneabbrevtbl == NULL ||
4923 3558 : *pindex >= zoneabbrevtbl->numabbrevs)
4924 18 : SRF_RETURN_DONE(funcctx);
4925 :
4926 3540 : tp = zoneabbrevtbl->abbrevs + *pindex;
4927 :
4928 3540 : switch (tp->type)
4929 : {
4930 1764 : case TZ:
4931 1764 : gmtoffset = tp->value;
4932 1764 : is_dst = false;
4933 1764 : break;
4934 864 : case DTZ:
4935 864 : gmtoffset = tp->value;
4936 864 : is_dst = true;
4937 864 : break;
4938 912 : case DYNTZ:
4939 : {
4940 : /* Determine the current meaning of the abbrev */
4941 : pg_tz *tzp;
4942 : DateTimeErrorExtra extra;
4943 : TimestampTz now;
4944 : int isdst;
4945 :
4946 912 : tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
4947 912 : if (tzp == NULL)
4948 0 : DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
4949 : NULL, NULL, NULL);
4950 912 : now = GetCurrentTransactionStartTimestamp();
4951 1824 : gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
4952 912 : tp->token,
4953 : tzp,
4954 : &isdst);
4955 912 : is_dst = (bool) isdst;
4956 912 : break;
4957 : }
4958 0 : default:
4959 0 : elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
4960 : gmtoffset = 0; /* keep compiler quiet */
4961 : is_dst = false;
4962 : break;
4963 : }
4964 :
4965 : /*
4966 : * Convert name to text, using upcasing conversion that is the inverse of
4967 : * what ParseDateTime() uses.
4968 : */
4969 3540 : strlcpy(buffer, tp->token, sizeof(buffer));
4970 16410 : for (p = (unsigned char *) buffer; *p; p++)
4971 12870 : *p = pg_toupper(*p);
4972 :
4973 3540 : values[0] = CStringGetTextDatum(buffer);
4974 :
4975 : /* Convert offset (in seconds) to an interval; can't overflow */
4976 14160 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
4977 3540 : itm_in.tm_usec = (int64) gmtoffset * USECS_PER_SEC;
4978 3540 : resInterval = (Interval *) palloc(sizeof(Interval));
4979 3540 : (void) itmin2interval(&itm_in, resInterval);
4980 3540 : values[1] = IntervalPGetDatum(resInterval);
4981 :
4982 3540 : values[2] = BoolGetDatum(is_dst);
4983 :
4984 3540 : (*pindex)++;
4985 :
4986 3540 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
4987 3540 : result = HeapTupleGetDatum(tuple);
4988 :
4989 3540 : SRF_RETURN_NEXT(funcctx, result);
4990 : }
4991 :
4992 : /*
4993 : * This set-returning function reads all the available full time zones
4994 : * and returns a set of (name, abbrev, utc_offset, is_dst).
4995 : */
4996 : Datum
4997 16 : pg_timezone_names(PG_FUNCTION_ARGS)
4998 : {
4999 16 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
5000 : pg_tzenum *tzenum;
5001 : pg_tz *tz;
5002 : Datum values[4];
5003 16 : bool nulls[4] = {0};
5004 : int tzoff;
5005 : struct pg_tm tm;
5006 : fsec_t fsec;
5007 : const char *tzn;
5008 : Interval *resInterval;
5009 : struct pg_itm_in itm_in;
5010 :
5011 16 : InitMaterializedSRF(fcinfo, 0);
5012 :
5013 : /* initialize timezone scanning code */
5014 16 : tzenum = pg_tzenumerate_start();
5015 :
5016 : /* search for another zone to display */
5017 : for (;;)
5018 : {
5019 9568 : tz = pg_tzenumerate_next(tzenum);
5020 9568 : if (!tz)
5021 16 : break;
5022 :
5023 : /* Convert now() to local time in this zone */
5024 9552 : if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
5025 : &tzoff, &tm, &fsec, &tzn, tz) != 0)
5026 0 : continue; /* ignore if conversion fails */
5027 :
5028 : /*
5029 : * IANA's rather silly "Factory" time zone used to emit ridiculously
5030 : * long "abbreviations" such as "Local time zone must be set--see zic
5031 : * manual page" or "Local time zone must be set--use tzsetup". While
5032 : * modern versions of tzdb emit the much saner "-00", it seems some
5033 : * benighted packagers are hacking the IANA data so that it continues
5034 : * to produce these strings. To prevent producing a weirdly wide
5035 : * abbrev column, reject ridiculously long abbreviations.
5036 : */
5037 9552 : if (tzn && strlen(tzn) > 31)
5038 0 : continue;
5039 :
5040 9552 : values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
5041 9552 : values[1] = CStringGetTextDatum(tzn ? tzn : "");
5042 :
5043 : /* Convert tzoff to an interval; can't overflow */
5044 38208 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5045 9552 : itm_in.tm_usec = (int64) -tzoff * USECS_PER_SEC;
5046 9552 : resInterval = (Interval *) palloc(sizeof(Interval));
5047 9552 : (void) itmin2interval(&itm_in, resInterval);
5048 9552 : values[2] = IntervalPGetDatum(resInterval);
5049 :
5050 9552 : values[3] = BoolGetDatum(tm.tm_isdst > 0);
5051 :
5052 9552 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
5053 : }
5054 :
5055 16 : pg_tzenumerate_end(tzenum);
5056 16 : return (Datum) 0;
5057 : }
|