LCOV - code coverage report
Current view: top level - src/backend/utils/adt - datetime.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 1717 2088 82.2 %
Date: 2020-05-29 00:07:09 Functions: 50 50 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13