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