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