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