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