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