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