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