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