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