Header And Logo

PostgreSQL
| The world's most advanced open source database.

datetime.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * datetime.c
00004  *    Support functions for date/time types.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/adt/datetime.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <ctype.h>
00018 #include <float.h>
00019 #include <limits.h>
00020 #include <math.h>
00021 
00022 #include "access/htup_details.h"
00023 #include "access/xact.h"
00024 #include "catalog/pg_type.h"
00025 #include "funcapi.h"
00026 #include "miscadmin.h"
00027 #include "nodes/nodeFuncs.h"
00028 #include "utils/builtins.h"
00029 #include "utils/date.h"
00030 #include "utils/datetime.h"
00031 #include "utils/memutils.h"
00032 #include "utils/tzparser.h"
00033 
00034 
00035 static int DecodeNumber(int flen, char *field, bool haveTextMonth,
00036              int fmask, int *tmask,
00037              struct pg_tm * tm, fsec_t *fsec, bool *is2digits);
00038 static int DecodeNumberField(int len, char *str,
00039                   int fmask, int *tmask,
00040                   struct pg_tm * tm, fsec_t *fsec, bool *is2digits);
00041 static int DecodeTime(char *str, int fmask, int range,
00042            int *tmask, struct pg_tm * tm, fsec_t *fsec);
00043 static int  DecodeTimezone(char *str, int *tzp);
00044 static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
00045 static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
00046            struct pg_tm * tm);
00047 static int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
00048              struct pg_tm * tm);
00049 static void TrimTrailingZeros(char *str);
00050 static void AppendSeconds(char *cp, int sec, fsec_t fsec,
00051               int precision, bool fillzeros);
00052 static void AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec,
00053                    int scale);
00054 static void AdjustFractDays(double frac, struct pg_tm * tm, fsec_t *fsec,
00055                 int scale);
00056 
00057 
00058 const int   day_tab[2][13] =
00059 {
00060     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
00061     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
00062 };
00063 
00064 char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
00065 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
00066 
00067 char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
00068 "Thursday", "Friday", "Saturday", NULL};
00069 
00070 
00071 /*****************************************************************************
00072  *   PRIVATE ROUTINES                                                        *
00073  *****************************************************************************/
00074 
00075 /*
00076  * Definitions for squeezing values into "value"
00077  * We set aside a high bit for a sign, and scale the timezone offsets
00078  * in minutes by a factor of 15 (so can represent quarter-hour increments).
00079  */
00080 #define ABS_SIGNBIT     ((char) 0200)
00081 #define VALMASK         ((char) 0177)
00082 #define POS(n)          (n)
00083 #define NEG(n)          ((n)|ABS_SIGNBIT)
00084 #define SIGNEDCHAR(c)   ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
00085 #define FROMVAL(tp)     (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
00086 #define TOVAL(tp, v)    ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
00087 
00088 /*
00089  * datetktbl holds date/time keywords.
00090  *
00091  * Note that this table must be strictly alphabetically ordered to allow an
00092  * O(ln(N)) search algorithm to be used.
00093  *
00094  * The text field is NOT guaranteed to be NULL-terminated.
00095  *
00096  * To keep this table reasonably small, we divide the lexval for TZ and DTZ
00097  * entries by 15 (so they are on 15 minute boundaries) and truncate the text
00098  * field at TOKMAXLEN characters.
00099  * Formerly, we divided by 10 rather than 15 but there are a few time zones
00100  * which are 30 or 45 minutes away from an even hour, most are on an hour
00101  * boundary, and none on other boundaries.
00102  *
00103  * The static table contains no TZ or DTZ entries, rather those are loaded
00104  * from configuration files and stored in timezonetktbl, which has the same
00105  * format as the static datetktbl.
00106  */
00107 static datetkn *timezonetktbl = NULL;
00108 
00109 static int  sztimezonetktbl = 0;
00110 
00111 static const datetkn datetktbl[] = {
00112 /*  text, token, lexval */
00113     {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
00114     {DA_D, ADBC, AD},           /* "ad" for years > 0 */
00115     {"allballs", RESERV, DTK_ZULU},     /* 00:00:00 */
00116     {"am", AMPM, AM},
00117     {"apr", MONTH, 4},
00118     {"april", MONTH, 4},
00119     {"at", IGNORE_DTF, 0},      /* "at" (throwaway) */
00120     {"aug", MONTH, 8},
00121     {"august", MONTH, 8},
00122     {DB_C, ADBC, BC},           /* "bc" for years <= 0 */
00123     {DCURRENT, RESERV, DTK_CURRENT},    /* "current" is always now */
00124     {"d", UNITS, DTK_DAY},      /* "day of month" for ISO input */
00125     {"dec", MONTH, 12},
00126     {"december", MONTH, 12},
00127     {"dow", RESERV, DTK_DOW},   /* day of week */
00128     {"doy", RESERV, DTK_DOY},   /* day of year */
00129     {"dst", DTZMOD, 6},
00130     {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
00131     {"feb", MONTH, 2},
00132     {"february", MONTH, 2},
00133     {"fri", DOW, 5},
00134     {"friday", DOW, 5},
00135     {"h", UNITS, DTK_HOUR},     /* "hour" */
00136     {LATE, RESERV, DTK_LATE},   /* "infinity" reserved for "late time" */
00137     {INVALID, RESERV, DTK_INVALID},     /* "invalid" reserved for bad time */
00138     {"isodow", RESERV, DTK_ISODOW},     /* ISO day of week, Sunday == 7 */
00139     {"isoyear", UNITS, DTK_ISOYEAR},    /* year in terms of the ISO week date */
00140     {"j", UNITS, DTK_JULIAN},
00141     {"jan", MONTH, 1},
00142     {"january", MONTH, 1},
00143     {"jd", UNITS, DTK_JULIAN},
00144     {"jul", MONTH, 7},
00145     {"julian", UNITS, DTK_JULIAN},
00146     {"july", MONTH, 7},
00147     {"jun", MONTH, 6},
00148     {"june", MONTH, 6},
00149     {"m", UNITS, DTK_MONTH},    /* "month" for ISO input */
00150     {"mar", MONTH, 3},
00151     {"march", MONTH, 3},
00152     {"may", MONTH, 5},
00153     {"mm", UNITS, DTK_MINUTE},  /* "minute" for ISO input */
00154     {"mon", DOW, 1},
00155     {"monday", DOW, 1},
00156     {"nov", MONTH, 11},
00157     {"november", MONTH, 11},
00158     {NOW, RESERV, DTK_NOW},     /* current transaction time */
00159     {"oct", MONTH, 10},
00160     {"october", MONTH, 10},
00161     {"on", IGNORE_DTF, 0},      /* "on" (throwaway) */
00162     {"pm", AMPM, PM},
00163     {"s", UNITS, DTK_SECOND},   /* "seconds" for ISO input */
00164     {"sat", DOW, 6},
00165     {"saturday", DOW, 6},
00166     {"sep", MONTH, 9},
00167     {"sept", MONTH, 9},
00168     {"september", MONTH, 9},
00169     {"sun", DOW, 0},
00170     {"sunday", DOW, 0},
00171     {"t", ISOTIME, DTK_TIME},   /* Filler for ISO time fields */
00172     {"thu", DOW, 4},
00173     {"thur", DOW, 4},
00174     {"thurs", DOW, 4},
00175     {"thursday", DOW, 4},
00176     {TODAY, RESERV, DTK_TODAY}, /* midnight */
00177     {TOMORROW, RESERV, DTK_TOMORROW},   /* tomorrow midnight */
00178     {"tue", DOW, 2},
00179     {"tues", DOW, 2},
00180     {"tuesday", DOW, 2},
00181     {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
00182     {"wed", DOW, 3},
00183     {"wednesday", DOW, 3},
00184     {"weds", DOW, 3},
00185     {"y", UNITS, DTK_YEAR},     /* "year" for ISO input */
00186     {YESTERDAY, RESERV, DTK_YESTERDAY}  /* yesterday midnight */
00187 };
00188 
00189 static int  szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
00190 
00191 static datetkn deltatktbl[] = {
00192     /* text, token, lexval */
00193     {"@", IGNORE_DTF, 0},       /* postgres relative prefix */
00194     {DAGO, AGO, 0},             /* "ago" indicates negative time offset */
00195     {"c", UNITS, DTK_CENTURY},  /* "century" relative */
00196     {"cent", UNITS, DTK_CENTURY},       /* "century" relative */
00197     {"centuries", UNITS, DTK_CENTURY},  /* "centuries" relative */
00198     {DCENTURY, UNITS, DTK_CENTURY},     /* "century" relative */
00199     {"d", UNITS, DTK_DAY},      /* "day" relative */
00200     {DDAY, UNITS, DTK_DAY},     /* "day" relative */
00201     {"days", UNITS, DTK_DAY},   /* "days" relative */
00202     {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
00203     {DDECADE, UNITS, DTK_DECADE},       /* "decade" relative */
00204     {"decades", UNITS, DTK_DECADE},     /* "decades" relative */
00205     {"decs", UNITS, DTK_DECADE},    /* "decades" relative */
00206     {"h", UNITS, DTK_HOUR},     /* "hour" relative */
00207     {DHOUR, UNITS, DTK_HOUR},   /* "hour" relative */
00208     {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
00209     {"hr", UNITS, DTK_HOUR},    /* "hour" relative */
00210     {"hrs", UNITS, DTK_HOUR},   /* "hours" relative */
00211     {INVALID, RESERV, DTK_INVALID},     /* reserved for invalid time */
00212     {"m", UNITS, DTK_MINUTE},   /* "minute" relative */
00213     {"microsecon", UNITS, DTK_MICROSEC},        /* "microsecond" relative */
00214     {"mil", UNITS, DTK_MILLENNIUM},     /* "millennium" relative */
00215     {"millennia", UNITS, DTK_MILLENNIUM},       /* "millennia" relative */
00216     {DMILLENNIUM, UNITS, DTK_MILLENNIUM},       /* "millennium" relative */
00217     {"millisecon", UNITS, DTK_MILLISEC},        /* relative */
00218     {"mils", UNITS, DTK_MILLENNIUM},    /* "millennia" relative */
00219     {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
00220     {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
00221     {DMINUTE, UNITS, DTK_MINUTE},       /* "minute" relative */
00222     {"minutes", UNITS, DTK_MINUTE},     /* "minutes" relative */
00223     {"mon", UNITS, DTK_MONTH},  /* "months" relative */
00224     {"mons", UNITS, DTK_MONTH}, /* "months" relative */
00225     {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
00226     {"months", UNITS, DTK_MONTH},
00227     {"ms", UNITS, DTK_MILLISEC},
00228     {"msec", UNITS, DTK_MILLISEC},
00229     {DMILLISEC, UNITS, DTK_MILLISEC},
00230     {"mseconds", UNITS, DTK_MILLISEC},
00231     {"msecs", UNITS, DTK_MILLISEC},
00232     {"qtr", UNITS, DTK_QUARTER},    /* "quarter" relative */
00233     {DQUARTER, UNITS, DTK_QUARTER},     /* "quarter" relative */
00234     {"s", UNITS, DTK_SECOND},
00235     {"sec", UNITS, DTK_SECOND},
00236     {DSECOND, UNITS, DTK_SECOND},
00237     {"seconds", UNITS, DTK_SECOND},
00238     {"secs", UNITS, DTK_SECOND},
00239     {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
00240     {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
00241     {"timezone_m", UNITS, DTK_TZ_MINUTE},       /* timezone minutes units */
00242     {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
00243     {"us", UNITS, DTK_MICROSEC},    /* "microsecond" relative */
00244     {"usec", UNITS, DTK_MICROSEC},      /* "microsecond" relative */
00245     {DMICROSEC, UNITS, DTK_MICROSEC},   /* "microsecond" relative */
00246     {"useconds", UNITS, DTK_MICROSEC},  /* "microseconds" relative */
00247     {"usecs", UNITS, DTK_MICROSEC},     /* "microseconds" relative */
00248     {"w", UNITS, DTK_WEEK},     /* "week" relative */
00249     {DWEEK, UNITS, DTK_WEEK},   /* "week" relative */
00250     {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
00251     {"y", UNITS, DTK_YEAR},     /* "year" relative */
00252     {DYEAR, UNITS, DTK_YEAR},   /* "year" relative */
00253     {"years", UNITS, DTK_YEAR}, /* "years" relative */
00254     {"yr", UNITS, DTK_YEAR},    /* "year" relative */
00255     {"yrs", UNITS, DTK_YEAR}    /* "years" relative */
00256 };
00257 
00258 static int  szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
00259 
00260 static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
00261 
00262 static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
00263 
00264 
00265 /*
00266  * strtoi --- just like strtol, but returns int not long
00267  */
00268 static int
00269 strtoi(const char *nptr, char **endptr, int base)
00270 {
00271     long        val;
00272 
00273     val = strtol(nptr, endptr, base);
00274 #ifdef HAVE_LONG_INT_64
00275     if (val != (long) ((int32) val))
00276         errno = ERANGE;
00277 #endif
00278     return (int) val;
00279 }
00280 
00281 
00282 /*
00283  * Calendar time to Julian date conversions.
00284  * Julian date is commonly used in astronomical applications,
00285  *  since it is numerically accurate and computationally simple.
00286  * The algorithms here will accurately convert between Julian day
00287  *  and calendar date for all non-negative Julian days
00288  *  (i.e. from Nov 24, -4713 on).
00289  *
00290  * These routines will be used by other date/time packages
00291  * - thomas 97/02/25
00292  *
00293  * Rewritten to eliminate overflow problems. This now allows the
00294  * routines to work correctly for all Julian day counts from
00295  * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
00296  * a 32-bit integer. Longer types should also work to the limits
00297  * of their precision.
00298  */
00299 
00300 int
00301 date2j(int y, int m, int d)
00302 {
00303     int         julian;
00304     int         century;
00305 
00306     if (m > 2)
00307     {
00308         m += 1;
00309         y += 4800;
00310     }
00311     else
00312     {
00313         m += 13;
00314         y += 4799;
00315     }
00316 
00317     century = y / 100;
00318     julian = y * 365 - 32167;
00319     julian += y / 4 - century + century / 4;
00320     julian += 7834 * m / 256 + d;
00321 
00322     return julian;
00323 }   /* date2j() */
00324 
00325 void
00326 j2date(int jd, int *year, int *month, int *day)
00327 {
00328     unsigned int julian;
00329     unsigned int quad;
00330     unsigned int extra;
00331     int         y;
00332 
00333     julian = jd;
00334     julian += 32044;
00335     quad = julian / 146097;
00336     extra = (julian - quad * 146097) * 4 + 3;
00337     julian += 60 + quad * 3 + extra / 146097;
00338     quad = julian / 1461;
00339     julian -= quad * 1461;
00340     y = julian * 4 / 1461;
00341     julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
00342         + 123;
00343     y += quad * 4;
00344     *year = y - 4800;
00345     quad = julian * 2141 / 65536;
00346     *day = julian - 7834 * quad / 256;
00347     *month = (quad + 10) % MONTHS_PER_YEAR + 1;
00348 
00349     return;
00350 }   /* j2date() */
00351 
00352 
00353 /*
00354  * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
00355  *
00356  * Note: various places use the locution j2day(date - 1) to produce a
00357  * result according to the convention 0..6 = Mon..Sun.  This is a bit of
00358  * a crock, but will work as long as the computation here is just a modulo.
00359  */
00360 int
00361 j2day(int date)
00362 {
00363     unsigned int day;
00364 
00365     day = date;
00366 
00367     day += 1;
00368     day %= 7;
00369 
00370     return (int) day;
00371 }   /* j2day() */
00372 
00373 
00374 /*
00375  * GetCurrentDateTime()
00376  *
00377  * Get the transaction start time ("now()") broken down as a struct pg_tm.
00378  */
00379 void
00380 GetCurrentDateTime(struct pg_tm * tm)
00381 {
00382     int         tz;
00383     fsec_t      fsec;
00384 
00385     timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, &fsec,
00386                  NULL, NULL);
00387     /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
00388 }
00389 
00390 /*
00391  * GetCurrentTimeUsec()
00392  *
00393  * Get the transaction start time ("now()") broken down as a struct pg_tm,
00394  * including fractional seconds and timezone offset.
00395  */
00396 void
00397 GetCurrentTimeUsec(struct pg_tm * tm, fsec_t *fsec, int *tzp)
00398 {
00399     int         tz;
00400 
00401     timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec,
00402                  NULL, NULL);
00403     /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
00404     if (tzp != NULL)
00405         *tzp = tz;
00406 }
00407 
00408 
00409 /* TrimTrailingZeros()
00410  * ... resulting from printing numbers with full precision.
00411  *
00412  * Before Postgres 8.4, this always left at least 2 fractional digits,
00413  * but conversations on the lists suggest this isn't desired
00414  * since showing '0.10' is misleading with values of precision(1).
00415  */
00416 static void
00417 TrimTrailingZeros(char *str)
00418 {
00419     int         len = strlen(str);
00420 
00421     while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.')
00422     {
00423         len--;
00424         *(str + len) = '\0';
00425     }
00426 }
00427 
00428 /*
00429  * Append sections and fractional seconds (if any) at *cp.
00430  * precision is the max number of fraction digits, fillzeros says to
00431  * pad to two integral-seconds digits.
00432  * Note that any sign is stripped from the input seconds values.
00433  */
00434 static void
00435 AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
00436 {
00437     if (fsec == 0)
00438     {
00439         if (fillzeros)
00440             sprintf(cp, "%02d", abs(sec));
00441         else
00442             sprintf(cp, "%d", abs(sec));
00443     }
00444     else
00445     {
00446 #ifdef HAVE_INT64_TIMESTAMP
00447         if (fillzeros)
00448             sprintf(cp, "%02d.%0*d", abs(sec), precision, (int) Abs(fsec));
00449         else
00450             sprintf(cp, "%d.%0*d", abs(sec), precision, (int) Abs(fsec));
00451 #else
00452         if (fillzeros)
00453             sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
00454         else
00455             sprintf(cp, "%.*f", precision, fabs(sec + fsec));
00456 #endif
00457         TrimTrailingZeros(cp);
00458     }
00459 }
00460 
00461 /* Variant of above that's specialized to timestamp case */
00462 static void
00463 AppendTimestampSeconds(char *cp, struct pg_tm * tm, fsec_t fsec)
00464 {
00465     /*
00466      * In float mode, don't print fractional seconds before 1 AD, since it's
00467      * unlikely there's any precision left ...
00468      */
00469 #ifndef HAVE_INT64_TIMESTAMP
00470     if (tm->tm_year <= 0)
00471         fsec = 0;
00472 #endif
00473     AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
00474 }
00475 
00476 /*
00477  * Multiply frac by scale (to produce seconds) and add to *tm & *fsec.
00478  * We assume the input frac is less than 1 so overflow is not an issue.
00479  */
00480 static void
00481 AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec, int scale)
00482 {
00483     int         sec;
00484 
00485     if (frac == 0)
00486         return;
00487     frac *= scale;
00488     sec = (int) frac;
00489     tm->tm_sec += sec;
00490     frac -= sec;
00491 #ifdef HAVE_INT64_TIMESTAMP
00492     *fsec += rint(frac * 1000000);
00493 #else
00494     *fsec += frac;
00495 #endif
00496 }
00497 
00498 /* As above, but initial scale produces days */
00499 static void
00500 AdjustFractDays(double frac, struct pg_tm * tm, fsec_t *fsec, int scale)
00501 {
00502     int         extra_days;
00503 
00504     if (frac == 0)
00505         return;
00506     frac *= scale;
00507     extra_days = (int) frac;
00508     tm->tm_mday += extra_days;
00509     frac -= extra_days;
00510     AdjustFractSeconds(frac, tm, fsec, SECS_PER_DAY);
00511 }
00512 
00513 /* Fetch a fractional-second value with suitable error checking */
00514 static int
00515 ParseFractionalSecond(char *cp, fsec_t *fsec)
00516 {
00517     double      frac;
00518 
00519     /* Caller should always pass the start of the fraction part */
00520     Assert(*cp == '.');
00521     errno = 0;
00522     frac = strtod(cp, &cp);
00523     /* check for parse failure */
00524     if (*cp != '\0' || errno != 0)
00525         return DTERR_BAD_FORMAT;
00526 #ifdef HAVE_INT64_TIMESTAMP
00527     *fsec = rint(frac * 1000000);
00528 #else
00529     *fsec = frac;
00530 #endif
00531     return 0;
00532 }
00533 
00534 
00535 /* ParseDateTime()
00536  *  Break string into tokens based on a date/time context.
00537  *  Returns 0 if successful, DTERR code if bogus input detected.
00538  *
00539  * timestr - the input string
00540  * workbuf - workspace for field string storage. This must be
00541  *   larger than the largest legal input for this datetime type --
00542  *   some additional space will be needed to NUL terminate fields.
00543  * buflen - the size of workbuf
00544  * field[] - pointers to field strings are returned in this array
00545  * ftype[] - field type indicators are returned in this array
00546  * maxfields - dimensions of the above two arrays
00547  * *numfields - set to the actual number of fields detected
00548  *
00549  * The fields extracted from the input are stored as separate,
00550  * null-terminated strings in the workspace at workbuf. Any text is
00551  * converted to lower case.
00552  *
00553  * Several field types are assigned:
00554  *  DTK_NUMBER - digits and (possibly) a decimal point
00555  *  DTK_DATE - digits and two delimiters, or digits and text
00556  *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
00557  *  DTK_STRING - text (no digits or punctuation)
00558  *  DTK_SPECIAL - leading "+" or "-" followed by text
00559  *  DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
00560  *
00561  * Note that some field types can hold unexpected items:
00562  *  DTK_NUMBER can hold date fields (yy.ddd)
00563  *  DTK_STRING can hold months (January) and time zones (PST)
00564  *  DTK_DATE can hold time zone names (America/New_York, GMT-8)
00565  */
00566 int
00567 ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
00568               char **field, int *ftype, int maxfields, int *numfields)
00569 {
00570     int         nf = 0;
00571     const char *cp = timestr;
00572     char       *bufp = workbuf;
00573     const char *bufend = workbuf + buflen;
00574 
00575     /*
00576      * Set the character pointed-to by "bufptr" to "newchar", and increment
00577      * "bufptr". "end" gives the end of the buffer -- we return an error if
00578      * there is no space left to append a character to the buffer. Note that
00579      * "bufptr" is evaluated twice.
00580      */
00581 #define APPEND_CHAR(bufptr, end, newchar)       \
00582     do                                          \
00583     {                                           \
00584         if (((bufptr) + 1) >= (end))            \
00585             return DTERR_BAD_FORMAT;            \
00586         *(bufptr)++ = newchar;                  \
00587     } while (0)
00588 
00589     /* outer loop through fields */
00590     while (*cp != '\0')
00591     {
00592         /* Ignore spaces between fields */
00593         if (isspace((unsigned char) *cp))
00594         {
00595             cp++;
00596             continue;
00597         }
00598 
00599         /* Record start of current field */
00600         if (nf >= maxfields)
00601             return DTERR_BAD_FORMAT;
00602         field[nf] = bufp;
00603 
00604         /* leading digit? then date or time */
00605         if (isdigit((unsigned char) *cp))
00606         {
00607             APPEND_CHAR(bufp, bufend, *cp++);
00608             while (isdigit((unsigned char) *cp))
00609                 APPEND_CHAR(bufp, bufend, *cp++);
00610 
00611             /* time field? */
00612             if (*cp == ':')
00613             {
00614                 ftype[nf] = DTK_TIME;
00615                 APPEND_CHAR(bufp, bufend, *cp++);
00616                 while (isdigit((unsigned char) *cp) ||
00617                        (*cp == ':') || (*cp == '.'))
00618                     APPEND_CHAR(bufp, bufend, *cp++);
00619             }
00620             /* date field? allow embedded text month */
00621             else if (*cp == '-' || *cp == '/' || *cp == '.')
00622             {
00623                 /* save delimiting character to use later */
00624                 char        delim = *cp;
00625 
00626                 APPEND_CHAR(bufp, bufend, *cp++);
00627                 /* second field is all digits? then no embedded text month */
00628                 if (isdigit((unsigned char) *cp))
00629                 {
00630                     ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
00631                     while (isdigit((unsigned char) *cp))
00632                         APPEND_CHAR(bufp, bufend, *cp++);
00633 
00634                     /*
00635                      * insist that the delimiters match to get a three-field
00636                      * date.
00637                      */
00638                     if (*cp == delim)
00639                     {
00640                         ftype[nf] = DTK_DATE;
00641                         APPEND_CHAR(bufp, bufend, *cp++);
00642                         while (isdigit((unsigned char) *cp) || *cp == delim)
00643                             APPEND_CHAR(bufp, bufend, *cp++);
00644                     }
00645                 }
00646                 else
00647                 {
00648                     ftype[nf] = DTK_DATE;
00649                     while (isalnum((unsigned char) *cp) || *cp == delim)
00650                         APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00651                 }
00652             }
00653 
00654             /*
00655              * otherwise, number only and will determine year, month, day, or
00656              * concatenated fields later...
00657              */
00658             else
00659                 ftype[nf] = DTK_NUMBER;
00660         }
00661         /* Leading decimal point? Then fractional seconds... */
00662         else if (*cp == '.')
00663         {
00664             APPEND_CHAR(bufp, bufend, *cp++);
00665             while (isdigit((unsigned char) *cp))
00666                 APPEND_CHAR(bufp, bufend, *cp++);
00667 
00668             ftype[nf] = DTK_NUMBER;
00669         }
00670 
00671         /*
00672          * text? then date string, month, day of week, special, or timezone
00673          */
00674         else if (isalpha((unsigned char) *cp))
00675         {
00676             bool        is_date;
00677 
00678             ftype[nf] = DTK_STRING;
00679             APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00680             while (isalpha((unsigned char) *cp))
00681                 APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00682 
00683             /*
00684              * Dates can have embedded '-', '/', or '.' separators.  It could
00685              * also be a timezone name containing embedded '/', '+', '-', '_',
00686              * or ':' (but '_' or ':' can't be the first punctuation). If the
00687              * next character is a digit or '+', we need to check whether what
00688              * we have so far is a recognized non-timezone keyword --- if so,
00689              * don't believe that this is the start of a timezone.
00690              */
00691             is_date = false;
00692             if (*cp == '-' || *cp == '/' || *cp == '.')
00693                 is_date = true;
00694             else if (*cp == '+' || isdigit((unsigned char) *cp))
00695             {
00696                 *bufp = '\0';   /* null-terminate current field value */
00697                 /* we need search only the core token table, not TZ names */
00698                 if (datebsearch(field[nf], datetktbl, szdatetktbl) == NULL)
00699                     is_date = true;
00700             }
00701             if (is_date)
00702             {
00703                 ftype[nf] = DTK_DATE;
00704                 do
00705                 {
00706                     APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00707                 } while (*cp == '+' || *cp == '-' ||
00708                          *cp == '/' || *cp == '_' ||
00709                          *cp == '.' || *cp == ':' ||
00710                          isalnum((unsigned char) *cp));
00711             }
00712         }
00713         /* sign? then special or numeric timezone */
00714         else if (*cp == '+' || *cp == '-')
00715         {
00716             APPEND_CHAR(bufp, bufend, *cp++);
00717             /* soak up leading whitespace */
00718             while (isspace((unsigned char) *cp))
00719                 cp++;
00720             /* numeric timezone? */
00721             /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
00722             if (isdigit((unsigned char) *cp))
00723             {
00724                 ftype[nf] = DTK_TZ;
00725                 APPEND_CHAR(bufp, bufend, *cp++);
00726                 while (isdigit((unsigned char) *cp) ||
00727                        *cp == ':' || *cp == '.' || *cp == '-')
00728                     APPEND_CHAR(bufp, bufend, *cp++);
00729             }
00730             /* special? */
00731             else if (isalpha((unsigned char) *cp))
00732             {
00733                 ftype[nf] = DTK_SPECIAL;
00734                 APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00735                 while (isalpha((unsigned char) *cp))
00736                     APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
00737             }
00738             /* otherwise something wrong... */
00739             else
00740                 return DTERR_BAD_FORMAT;
00741         }
00742         /* ignore other punctuation but use as delimiter */
00743         else if (ispunct((unsigned char) *cp))
00744         {
00745             cp++;
00746             continue;
00747         }
00748         /* otherwise, something is not right... */
00749         else
00750             return DTERR_BAD_FORMAT;
00751 
00752         /* force in a delimiter after each field */
00753         *bufp++ = '\0';
00754         nf++;
00755     }
00756 
00757     *numfields = nf;
00758 
00759     return 0;
00760 }
00761 
00762 
00763 /* DecodeDateTime()
00764  * Interpret previously parsed fields for general date and time.
00765  * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
00766  * (Currently, all callers treat 1 as an error return too.)
00767  *
00768  *      External format(s):
00769  *              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
00770  *              "Fri Feb-7-1997 15:23:27"
00771  *              "Feb-7-1997 15:23:27"
00772  *              "2-7-1997 15:23:27"
00773  *              "1997-2-7 15:23:27"
00774  *              "1997.038 15:23:27"     (day of year 1-366)
00775  *      Also supports input in compact time:
00776  *              "970207 152327"
00777  *              "97038 152327"
00778  *              "20011225T040506.789-07"
00779  *
00780  * Use the system-provided functions to get the current time zone
00781  * if not specified in the input string.
00782  *
00783  * If the date is outside the range of pg_time_t (in practice that could only
00784  * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
00785  * 1997-05-27
00786  */
00787 int
00788 DecodeDateTime(char **field, int *ftype, int nf,
00789                int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
00790 {
00791     int         fmask = 0,
00792                 tmask,
00793                 type;
00794     int         ptype = 0;      /* "prefix type" for ISO y2001m02d04 format */
00795     int         i;
00796     int         val;
00797     int         dterr;
00798     int         mer = HR24;
00799     bool        haveTextMonth = FALSE;
00800     bool        isjulian = FALSE;
00801     bool        is2digits = FALSE;
00802     bool        bc = FALSE;
00803     pg_tz      *namedTz = NULL;
00804     struct pg_tm cur_tm;
00805 
00806     /*
00807      * We'll insist on at least all of the date fields, but initialize the
00808      * remaining fields in case they are not set later...
00809      */
00810     *dtype = DTK_DATE;
00811     tm->tm_hour = 0;
00812     tm->tm_min = 0;
00813     tm->tm_sec = 0;
00814     *fsec = 0;
00815     /* don't know daylight savings time status apriori */
00816     tm->tm_isdst = -1;
00817     if (tzp != NULL)
00818         *tzp = 0;
00819 
00820     for (i = 0; i < nf; i++)
00821     {
00822         switch (ftype[i])
00823         {
00824             case DTK_DATE:
00825                 /***
00826                  * Integral julian day with attached time zone?
00827                  * All other forms with JD will be separated into
00828                  * distinct fields, so we handle just this case here.
00829                  ***/
00830                 if (ptype == DTK_JULIAN)
00831                 {
00832                     char       *cp;
00833                     int         val;
00834 
00835                     if (tzp == NULL)
00836                         return DTERR_BAD_FORMAT;
00837 
00838                     errno = 0;
00839                     val = strtoi(field[i], &cp, 10);
00840                     if (errno == ERANGE || val < 0)
00841                         return DTERR_FIELD_OVERFLOW;
00842 
00843                     j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
00844                     isjulian = TRUE;
00845 
00846                     /* Get the time zone from the end of the string */
00847                     dterr = DecodeTimezone(cp, tzp);
00848                     if (dterr)
00849                         return dterr;
00850 
00851                     tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
00852                     ptype = 0;
00853                     break;
00854                 }
00855                 /***
00856                  * Already have a date? Then this might be a time zone name
00857                  * with embedded punctuation (e.g. "America/New_York") or a
00858                  * run-together time with trailing time zone (e.g. hhmmss-zz).
00859                  * - thomas 2001-12-25
00860                  *
00861                  * We consider it a time zone if we already have month & day.
00862                  * This is to allow the form "mmm dd hhmmss tz year", which
00863                  * we've historically accepted.
00864                  ***/
00865                 else if (ptype != 0 ||
00866                          ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
00867                           (DTK_M(MONTH) | DTK_M(DAY))))
00868                 {
00869                     /* No time zone accepted? Then quit... */
00870                     if (tzp == NULL)
00871                         return DTERR_BAD_FORMAT;
00872 
00873                     if (isdigit((unsigned char) *field[i]) || ptype != 0)
00874                     {
00875                         char       *cp;
00876 
00877                         if (ptype != 0)
00878                         {
00879                             /* Sanity check; should not fail this test */
00880                             if (ptype != DTK_TIME)
00881                                 return DTERR_BAD_FORMAT;
00882                             ptype = 0;
00883                         }
00884 
00885                         /*
00886                          * Starts with a digit but we already have a time
00887                          * field? Then we are in trouble with a date and time
00888                          * already...
00889                          */
00890                         if ((fmask & DTK_TIME_M) == DTK_TIME_M)
00891                             return DTERR_BAD_FORMAT;
00892 
00893                         if ((cp = strchr(field[i], '-')) == NULL)
00894                             return DTERR_BAD_FORMAT;
00895 
00896                         /* Get the time zone from the end of the string */
00897                         dterr = DecodeTimezone(cp, tzp);
00898                         if (dterr)
00899                             return dterr;
00900                         *cp = '\0';
00901 
00902                         /*
00903                          * Then read the rest of the field as a concatenated
00904                          * time
00905                          */
00906                         dterr = DecodeNumberField(strlen(field[i]), field[i],
00907                                                   fmask,
00908                                                   &tmask, tm,
00909                                                   fsec, &is2digits);
00910                         if (dterr < 0)
00911                             return dterr;
00912 
00913                         /*
00914                          * modify tmask after returning from
00915                          * DecodeNumberField()
00916                          */
00917                         tmask |= DTK_M(TZ);
00918                     }
00919                     else
00920                     {
00921                         namedTz = pg_tzset(field[i]);
00922                         if (!namedTz)
00923                         {
00924                             /*
00925                              * We should return an error code instead of
00926                              * ereport'ing directly, but then there is no way
00927                              * to report the bad time zone name.
00928                              */
00929                             ereport(ERROR,
00930                                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00931                                      errmsg("time zone \"%s\" not recognized",
00932                                             field[i])));
00933                         }
00934                         /* we'll apply the zone setting below */
00935                         tmask = DTK_M(TZ);
00936                     }
00937                 }
00938                 else
00939                 {
00940                     dterr = DecodeDate(field[i], fmask,
00941                                        &tmask, &is2digits, tm);
00942                     if (dterr)
00943                         return dterr;
00944                 }
00945                 break;
00946 
00947             case DTK_TIME:
00948                 /*
00949                  * This might be an ISO time following a "t" field.
00950                  */
00951                 if (ptype != 0)
00952                 {
00953                     /* Sanity check; should not fail this test */
00954                     if (ptype != DTK_TIME)
00955                         return DTERR_BAD_FORMAT;
00956                     ptype = 0;
00957                 }
00958                 dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
00959                                    &tmask, tm, fsec);
00960                 if (dterr)
00961                     return dterr;
00962 
00963                 /*
00964                  * Check upper limit on hours; other limits checked in
00965                  * DecodeTime()
00966                  */
00967                 /* test for > 24:00:00 */
00968                 if (tm->tm_hour > HOURS_PER_DAY ||
00969                     (tm->tm_hour == HOURS_PER_DAY &&
00970                      (tm->tm_min > 0 || tm->tm_sec > 0 || *fsec > 0)))
00971                     return DTERR_FIELD_OVERFLOW;
00972                 break;
00973 
00974             case DTK_TZ:
00975                 {
00976                     int         tz;
00977 
00978                     if (tzp == NULL)
00979                         return DTERR_BAD_FORMAT;
00980 
00981                     dterr = DecodeTimezone(field[i], &tz);
00982                     if (dterr)
00983                         return dterr;
00984                     *tzp = tz;
00985                     tmask = DTK_M(TZ);
00986                 }
00987                 break;
00988 
00989             case DTK_NUMBER:
00990 
00991                 /*
00992                  * Was this an "ISO date" with embedded field labels? An
00993                  * example is "y2001m02d04" - thomas 2001-02-04
00994                  */
00995                 if (ptype != 0)
00996                 {
00997                     char       *cp;
00998                     int         val;
00999 
01000                     errno = 0;
01001                     val = strtoi(field[i], &cp, 10);
01002                     if (errno == ERANGE)
01003                         return DTERR_FIELD_OVERFLOW;
01004 
01005                     /*
01006                      * only a few kinds are allowed to have an embedded
01007                      * decimal
01008                      */
01009                     if (*cp == '.')
01010                         switch (ptype)
01011                         {
01012                             case DTK_JULIAN:
01013                             case DTK_TIME:
01014                             case DTK_SECOND:
01015                                 break;
01016                             default:
01017                                 return DTERR_BAD_FORMAT;
01018                                 break;
01019                         }
01020                     else if (*cp != '\0')
01021                         return DTERR_BAD_FORMAT;
01022 
01023                     switch (ptype)
01024                     {
01025                         case DTK_YEAR:
01026                             tm->tm_year = val;
01027                             tmask = DTK_M(YEAR);
01028                             break;
01029 
01030                         case DTK_MONTH:
01031 
01032                             /*
01033                              * already have a month and hour? then assume
01034                              * minutes
01035                              */
01036                             if ((fmask & DTK_M(MONTH)) != 0 &&
01037                                 (fmask & DTK_M(HOUR)) != 0)
01038                             {
01039                                 tm->tm_min = val;
01040                                 tmask = DTK_M(MINUTE);
01041                             }
01042                             else
01043                             {
01044                                 tm->tm_mon = val;
01045                                 tmask = DTK_M(MONTH);
01046                             }
01047                             break;
01048 
01049                         case DTK_DAY:
01050                             tm->tm_mday = val;
01051                             tmask = DTK_M(DAY);
01052                             break;
01053 
01054                         case DTK_HOUR:
01055                             tm->tm_hour = val;
01056                             tmask = DTK_M(HOUR);
01057                             break;
01058 
01059                         case DTK_MINUTE:
01060                             tm->tm_min = val;
01061                             tmask = DTK_M(MINUTE);
01062                             break;
01063 
01064                         case DTK_SECOND:
01065                             tm->tm_sec = val;
01066                             tmask = DTK_M(SECOND);
01067                             if (*cp == '.')
01068                             {
01069                                 dterr = ParseFractionalSecond(cp, fsec);
01070                                 if (dterr)
01071                                     return dterr;
01072                                 tmask = DTK_ALL_SECS_M;
01073                             }
01074                             break;
01075 
01076                         case DTK_TZ:
01077                             tmask = DTK_M(TZ);
01078                             dterr = DecodeTimezone(field[i], tzp);
01079                             if (dterr)
01080                                 return dterr;
01081                             break;
01082 
01083                         case DTK_JULIAN:
01084                             /* previous field was a label for "julian date" */
01085                             if (val < 0)
01086                                 return DTERR_FIELD_OVERFLOW;
01087                             tmask = DTK_DATE_M;
01088                             j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01089                             isjulian = TRUE;
01090 
01091                             /* fractional Julian Day? */
01092                             if (*cp == '.')
01093                             {
01094                                 double      time;
01095 
01096                                 errno = 0;
01097                                 time = strtod(cp, &cp);
01098                                 if (*cp != '\0' || errno != 0)
01099                                     return DTERR_BAD_FORMAT;
01100 
01101 #ifdef HAVE_INT64_TIMESTAMP
01102                                 time *= USECS_PER_DAY;
01103 #else
01104                                 time *= SECS_PER_DAY;
01105 #endif
01106                                 dt2time(time,
01107                                         &tm->tm_hour, &tm->tm_min,
01108                                         &tm->tm_sec, fsec);
01109                                 tmask |= DTK_TIME_M;
01110                             }
01111                             break;
01112 
01113                         case DTK_TIME:
01114                             /* previous field was "t" for ISO time */
01115                             dterr = DecodeNumberField(strlen(field[i]), field[i],
01116                                                       (fmask | DTK_DATE_M),
01117                                                       &tmask, tm,
01118                                                       fsec, &is2digits);
01119                             if (dterr < 0)
01120                                 return dterr;
01121                             if (tmask != DTK_TIME_M)
01122                                 return DTERR_BAD_FORMAT;
01123                             break;
01124 
01125                         default:
01126                             return DTERR_BAD_FORMAT;
01127                             break;
01128                     }
01129 
01130                     ptype = 0;
01131                     *dtype = DTK_DATE;
01132                 }
01133                 else
01134                 {
01135                     char       *cp;
01136                     int         flen;
01137 
01138                     flen = strlen(field[i]);
01139                     cp = strchr(field[i], '.');
01140 
01141                     /* Embedded decimal and no date yet? */
01142                     if (cp != NULL && !(fmask & DTK_DATE_M))
01143                     {
01144                         dterr = DecodeDate(field[i], fmask,
01145                                            &tmask, &is2digits, tm);
01146                         if (dterr)
01147                             return dterr;
01148                     }
01149                     /* embedded decimal and several digits before? */
01150                     else if (cp != NULL && flen - strlen(cp) > 2)
01151                     {
01152                         /*
01153                          * Interpret as a concatenated date or time Set the
01154                          * type field to allow decoding other fields later.
01155                          * Example: 20011223 or 040506
01156                          */
01157                         dterr = DecodeNumberField(flen, field[i], fmask,
01158                                                   &tmask, tm,
01159                                                   fsec, &is2digits);
01160                         if (dterr < 0)
01161                             return dterr;
01162                     }
01163                     else if (flen > 4)
01164                     {
01165                         dterr = DecodeNumberField(flen, field[i], fmask,
01166                                                   &tmask, tm,
01167                                                   fsec, &is2digits);
01168                         if (dterr < 0)
01169                             return dterr;
01170                     }
01171                     /* otherwise it is a single date/time field... */
01172                     else
01173                     {
01174                         dterr = DecodeNumber(flen, field[i],
01175                                              haveTextMonth, fmask,
01176                                              &tmask, tm,
01177                                              fsec, &is2digits);
01178                         if (dterr)
01179                             return dterr;
01180                     }
01181                 }
01182                 break;
01183 
01184             case DTK_STRING:
01185             case DTK_SPECIAL:
01186                 type = DecodeSpecial(i, field[i], &val);
01187                 if (type == IGNORE_DTF)
01188                     continue;
01189 
01190                 tmask = DTK_M(type);
01191                 switch (type)
01192                 {
01193                     case RESERV:
01194                         switch (val)
01195                         {
01196                             case DTK_CURRENT:
01197                                 ereport(ERROR,
01198                                      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01199                                       errmsg("date/time value \"current\" is no longer supported")));
01200 
01201                                 return DTERR_BAD_FORMAT;
01202                                 break;
01203 
01204                             case DTK_NOW:
01205                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
01206                                 *dtype = DTK_DATE;
01207                                 GetCurrentTimeUsec(tm, fsec, tzp);
01208                                 break;
01209 
01210                             case DTK_YESTERDAY:
01211                                 tmask = DTK_DATE_M;
01212                                 *dtype = DTK_DATE;
01213                                 GetCurrentDateTime(&cur_tm);
01214                                 j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
01215                                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01216                                 break;
01217 
01218                             case DTK_TODAY:
01219                                 tmask = DTK_DATE_M;
01220                                 *dtype = DTK_DATE;
01221                                 GetCurrentDateTime(&cur_tm);
01222                                 tm->tm_year = cur_tm.tm_year;
01223                                 tm->tm_mon = cur_tm.tm_mon;
01224                                 tm->tm_mday = cur_tm.tm_mday;
01225                                 break;
01226 
01227                             case DTK_TOMORROW:
01228                                 tmask = DTK_DATE_M;
01229                                 *dtype = DTK_DATE;
01230                                 GetCurrentDateTime(&cur_tm);
01231                                 j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
01232                                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01233                                 break;
01234 
01235                             case DTK_ZULU:
01236                                 tmask = (DTK_TIME_M | DTK_M(TZ));
01237                                 *dtype = DTK_DATE;
01238                                 tm->tm_hour = 0;
01239                                 tm->tm_min = 0;
01240                                 tm->tm_sec = 0;
01241                                 if (tzp != NULL)
01242                                     *tzp = 0;
01243                                 break;
01244 
01245                             default:
01246                                 *dtype = val;
01247                         }
01248 
01249                         break;
01250 
01251                     case MONTH:
01252 
01253                         /*
01254                          * already have a (numeric) month? then see if we can
01255                          * substitute...
01256                          */
01257                         if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
01258                             !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 &&
01259                             tm->tm_mon <= 31)
01260                         {
01261                             tm->tm_mday = tm->tm_mon;
01262                             tmask = DTK_M(DAY);
01263                         }
01264                         haveTextMonth = TRUE;
01265                         tm->tm_mon = val;
01266                         break;
01267 
01268                     case DTZMOD:
01269 
01270                         /*
01271                          * daylight savings time modifier (solves "MET DST"
01272                          * syntax)
01273                          */
01274                         tmask |= DTK_M(DTZ);
01275                         tm->tm_isdst = 1;
01276                         if (tzp == NULL)
01277                             return DTERR_BAD_FORMAT;
01278                         *tzp += val * MINS_PER_HOUR;
01279                         break;
01280 
01281                     case DTZ:
01282 
01283                         /*
01284                          * set mask for TZ here _or_ check for DTZ later when
01285                          * getting default timezone
01286                          */
01287                         tmask |= DTK_M(TZ);
01288                         tm->tm_isdst = 1;
01289                         if (tzp == NULL)
01290                             return DTERR_BAD_FORMAT;
01291                         *tzp = val * MINS_PER_HOUR;
01292                         break;
01293 
01294                     case TZ:
01295                         tm->tm_isdst = 0;
01296                         if (tzp == NULL)
01297                             return DTERR_BAD_FORMAT;
01298                         *tzp = val * MINS_PER_HOUR;
01299                         break;
01300 
01301                     case IGNORE_DTF:
01302                         break;
01303 
01304                     case AMPM:
01305                         mer = val;
01306                         break;
01307 
01308                     case ADBC:
01309                         bc = (val == BC);
01310                         break;
01311 
01312                     case DOW:
01313                         tm->tm_wday = val;
01314                         break;
01315 
01316                     case UNITS:
01317                         tmask = 0;
01318                         ptype = val;
01319                         break;
01320 
01321                     case ISOTIME:
01322 
01323                         /*
01324                          * This is a filler field "t" indicating that the next
01325                          * field is time. Try to verify that this is sensible.
01326                          */
01327                         tmask = 0;
01328 
01329                         /* No preceding date? Then quit... */
01330                         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
01331                             return DTERR_BAD_FORMAT;
01332 
01333                         /***
01334                          * We will need one of the following fields:
01335                          *  DTK_NUMBER should be hhmmss.fff
01336                          *  DTK_TIME should be hh:mm:ss.fff
01337                          *  DTK_DATE should be hhmmss-zz
01338                          ***/
01339                         if (i >= nf - 1 ||
01340                             (ftype[i + 1] != DTK_NUMBER &&
01341                              ftype[i + 1] != DTK_TIME &&
01342                              ftype[i + 1] != DTK_DATE))
01343                             return DTERR_BAD_FORMAT;
01344 
01345                         ptype = val;
01346                         break;
01347 
01348                     case UNKNOWN_FIELD:
01349 
01350                         /*
01351                          * Before giving up and declaring error, check to see
01352                          * if it is an all-alpha timezone name.
01353                          */
01354                         namedTz = pg_tzset(field[i]);
01355                         if (!namedTz)
01356                             return DTERR_BAD_FORMAT;
01357                         /* we'll apply the zone setting below */
01358                         tmask = DTK_M(TZ);
01359                         break;
01360 
01361                     default:
01362                         return DTERR_BAD_FORMAT;
01363                 }
01364                 break;
01365 
01366             default:
01367                 return DTERR_BAD_FORMAT;
01368         }
01369 
01370         if (tmask & fmask)
01371             return DTERR_BAD_FORMAT;
01372         fmask |= tmask;
01373     }                           /* end loop over fields */
01374 
01375     /* do final checking/adjustment of Y/M/D fields */
01376     dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
01377     if (dterr)
01378         return dterr;
01379 
01380     /* handle AM/PM */
01381     if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
01382         return DTERR_FIELD_OVERFLOW;
01383     if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
01384         tm->tm_hour = 0;
01385     else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
01386         tm->tm_hour += HOURS_PER_DAY / 2;
01387 
01388     /* do additional checking for full date specs... */
01389     if (*dtype == DTK_DATE)
01390     {
01391         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
01392         {
01393             if ((fmask & DTK_TIME_M) == DTK_TIME_M)
01394                 return 1;
01395             return DTERR_BAD_FORMAT;
01396         }
01397 
01398         /*
01399          * If we had a full timezone spec, compute the offset (we could not do
01400          * it before, because we need the date to resolve DST status).
01401          */
01402         if (namedTz != NULL)
01403         {
01404             /* daylight savings time modifier disallowed with full TZ */
01405             if (fmask & DTK_M(DTZMOD))
01406                 return DTERR_BAD_FORMAT;
01407 
01408             *tzp = DetermineTimeZoneOffset(tm, namedTz);
01409         }
01410 
01411         /* timezone not specified? then find local timezone if possible */
01412         if (tzp != NULL && !(fmask & DTK_M(TZ)))
01413         {
01414             /*
01415              * daylight savings time modifier but no standard timezone? then
01416              * error
01417              */
01418             if (fmask & DTK_M(DTZMOD))
01419                 return DTERR_BAD_FORMAT;
01420 
01421             *tzp = DetermineTimeZoneOffset(tm, session_timezone);
01422         }
01423     }
01424 
01425     return 0;
01426 }
01427 
01428 
01429 /* DetermineTimeZoneOffset()
01430  *
01431  * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
01432  * tm_sec fields are set, attempt to determine the applicable time zone
01433  * (ie, regular or daylight-savings time) at that time.  Set the struct pg_tm's
01434  * tm_isdst field accordingly, and return the actual timezone offset.
01435  *
01436  * Note: it might seem that we should use mktime() for this, but bitter
01437  * experience teaches otherwise.  This code is much faster than most versions
01438  * of mktime(), anyway.
01439  */
01440 int
01441 DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
01442 {
01443     int         date,
01444                 sec;
01445     pg_time_t   day,
01446                 mytime,
01447                 prevtime,
01448                 boundary,
01449                 beforetime,
01450                 aftertime;
01451     long int    before_gmtoff,
01452                 after_gmtoff;
01453     int         before_isdst,
01454                 after_isdst;
01455     int         res;
01456 
01457     if (tzp == session_timezone && HasCTZSet)
01458     {
01459         tm->tm_isdst = 0;       /* for lack of a better idea */
01460         return CTimeZone;
01461     }
01462 
01463     /*
01464      * First, generate the pg_time_t value corresponding to the given
01465      * y/m/d/h/m/s taken as GMT time.  If this overflows, punt and decide the
01466      * timezone is GMT.  (We only need to worry about overflow on machines
01467      * where pg_time_t is 32 bits.)
01468      */
01469     if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
01470         goto overflow;
01471     date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
01472 
01473     day = ((pg_time_t) date) * SECS_PER_DAY;
01474     if (day / SECS_PER_DAY != date)
01475         goto overflow;
01476     sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
01477     mytime = day + sec;
01478     /* since sec >= 0, overflow could only be from +day to -mytime */
01479     if (mytime < 0 && day > 0)
01480         goto overflow;
01481 
01482     /*
01483      * Find the DST time boundary just before or following the target time. We
01484      * assume that all zones have GMT offsets less than 24 hours, and that DST
01485      * boundaries can't be closer together than 48 hours, so backing up 24
01486      * hours and finding the "next" boundary will work.
01487      */
01488     prevtime = mytime - SECS_PER_DAY;
01489     if (mytime < 0 && prevtime > 0)
01490         goto overflow;
01491 
01492     res = pg_next_dst_boundary(&prevtime,
01493                                &before_gmtoff, &before_isdst,
01494                                &boundary,
01495                                &after_gmtoff, &after_isdst,
01496                                tzp);
01497     if (res < 0)
01498         goto overflow;          /* failure? */
01499 
01500     if (res == 0)
01501     {
01502         /* Non-DST zone, life is simple */
01503         tm->tm_isdst = before_isdst;
01504         return -(int) before_gmtoff;
01505     }
01506 
01507     /*
01508      * Form the candidate pg_time_t values with local-time adjustment
01509      */
01510     beforetime = mytime - before_gmtoff;
01511     if ((before_gmtoff > 0 &&
01512          mytime < 0 && beforetime > 0) ||
01513         (before_gmtoff <= 0 &&
01514          mytime > 0 && beforetime < 0))
01515         goto overflow;
01516     aftertime = mytime - after_gmtoff;
01517     if ((after_gmtoff > 0 &&
01518          mytime < 0 && aftertime > 0) ||
01519         (after_gmtoff <= 0 &&
01520          mytime > 0 && aftertime < 0))
01521         goto overflow;
01522 
01523     /*
01524      * If both before or both after the boundary time, we know what to do
01525      */
01526     if (beforetime <= boundary && aftertime < boundary)
01527     {
01528         tm->tm_isdst = before_isdst;
01529         return -(int) before_gmtoff;
01530     }
01531     if (beforetime > boundary && aftertime >= boundary)
01532     {
01533         tm->tm_isdst = after_isdst;
01534         return -(int) after_gmtoff;
01535     }
01536 
01537     /*
01538      * It's an invalid or ambiguous time due to timezone transition. Prefer
01539      * the standard-time interpretation.
01540      */
01541     if (after_isdst == 0)
01542     {
01543         tm->tm_isdst = after_isdst;
01544         return -(int) after_gmtoff;
01545     }
01546     tm->tm_isdst = before_isdst;
01547     return -(int) before_gmtoff;
01548 
01549 overflow:
01550     /* Given date is out of range, so assume UTC */
01551     tm->tm_isdst = 0;
01552     return 0;
01553 }
01554 
01555 
01556 /* DecodeTimeOnly()
01557  * Interpret parsed string as time fields only.
01558  * Returns 0 if successful, DTERR code if bogus input detected.
01559  *
01560  * Note that support for time zone is here for
01561  * SQL TIME WITH TIME ZONE, but it reveals
01562  * bogosity with SQL date/time standards, since
01563  * we must infer a time zone from current time.
01564  * - thomas 2000-03-10
01565  * Allow specifying date to get a better time zone,
01566  * if time zones are allowed. - thomas 2001-12-26
01567  */
01568 int
01569 DecodeTimeOnly(char **field, int *ftype, int nf,
01570                int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
01571 {
01572     int         fmask = 0,
01573                 tmask,
01574                 type;
01575     int         ptype = 0;      /* "prefix type" for ISO h04mm05s06 format */
01576     int         i;
01577     int         val;
01578     int         dterr;
01579     bool        isjulian = FALSE;
01580     bool        is2digits = FALSE;
01581     bool        bc = FALSE;
01582     int         mer = HR24;
01583     pg_tz      *namedTz = NULL;
01584 
01585     *dtype = DTK_TIME;
01586     tm->tm_hour = 0;
01587     tm->tm_min = 0;
01588     tm->tm_sec = 0;
01589     *fsec = 0;
01590     /* don't know daylight savings time status apriori */
01591     tm->tm_isdst = -1;
01592 
01593     if (tzp != NULL)
01594         *tzp = 0;
01595 
01596     for (i = 0; i < nf; i++)
01597     {
01598         switch (ftype[i])
01599         {
01600             case DTK_DATE:
01601 
01602                 /*
01603                  * Time zone not allowed? Then should not accept dates or time
01604                  * zones no matter what else!
01605                  */
01606                 if (tzp == NULL)
01607                     return DTERR_BAD_FORMAT;
01608 
01609                 /* Under limited circumstances, we will accept a date... */
01610                 if (i == 0 && nf >= 2 &&
01611                     (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
01612                 {
01613                     dterr = DecodeDate(field[i], fmask,
01614                                        &tmask, &is2digits, tm);
01615                     if (dterr)
01616                         return dterr;
01617                 }
01618                 /* otherwise, this is a time and/or time zone */
01619                 else
01620                 {
01621                     if (isdigit((unsigned char) *field[i]))
01622                     {
01623                         char       *cp;
01624 
01625                         /*
01626                          * Starts with a digit but we already have a time
01627                          * field? Then we are in trouble with time already...
01628                          */
01629                         if ((fmask & DTK_TIME_M) == DTK_TIME_M)
01630                             return DTERR_BAD_FORMAT;
01631 
01632                         /*
01633                          * Should not get here and fail. Sanity check only...
01634                          */
01635                         if ((cp = strchr(field[i], '-')) == NULL)
01636                             return DTERR_BAD_FORMAT;
01637 
01638                         /* Get the time zone from the end of the string */
01639                         dterr = DecodeTimezone(cp, tzp);
01640                         if (dterr)
01641                             return dterr;
01642                         *cp = '\0';
01643 
01644                         /*
01645                          * Then read the rest of the field as a concatenated
01646                          * time
01647                          */
01648                         dterr = DecodeNumberField(strlen(field[i]), field[i],
01649                                                   (fmask | DTK_DATE_M),
01650                                                   &tmask, tm,
01651                                                   fsec, &is2digits);
01652                         if (dterr < 0)
01653                             return dterr;
01654                         ftype[i] = dterr;
01655 
01656                         tmask |= DTK_M(TZ);
01657                     }
01658                     else
01659                     {
01660                         namedTz = pg_tzset(field[i]);
01661                         if (!namedTz)
01662                         {
01663                             /*
01664                              * We should return an error code instead of
01665                              * ereport'ing directly, but then there is no way
01666                              * to report the bad time zone name.
01667                              */
01668                             ereport(ERROR,
01669                                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01670                                      errmsg("time zone \"%s\" not recognized",
01671                                             field[i])));
01672                         }
01673                         /* we'll apply the zone setting below */
01674                         ftype[i] = DTK_TZ;
01675                         tmask = DTK_M(TZ);
01676                     }
01677                 }
01678                 break;
01679 
01680             case DTK_TIME:
01681                 dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
01682                                    INTERVAL_FULL_RANGE,
01683                                    &tmask, tm, fsec);
01684                 if (dterr)
01685                     return dterr;
01686                 break;
01687 
01688             case DTK_TZ:
01689                 {
01690                     int         tz;
01691 
01692                     if (tzp == NULL)
01693                         return DTERR_BAD_FORMAT;
01694 
01695                     dterr = DecodeTimezone(field[i], &tz);
01696                     if (dterr)
01697                         return dterr;
01698                     *tzp = tz;
01699                     tmask = DTK_M(TZ);
01700                 }
01701                 break;
01702 
01703             case DTK_NUMBER:
01704 
01705                 /*
01706                  * Was this an "ISO time" with embedded field labels? An
01707                  * example is "h04m05s06" - thomas 2001-02-04
01708                  */
01709                 if (ptype != 0)
01710                 {
01711                     char       *cp;
01712                     int         val;
01713 
01714                     /* Only accept a date under limited circumstances */
01715                     switch (ptype)
01716                     {
01717                         case DTK_JULIAN:
01718                         case DTK_YEAR:
01719                         case DTK_MONTH:
01720                         case DTK_DAY:
01721                             if (tzp == NULL)
01722                                 return DTERR_BAD_FORMAT;
01723                         default:
01724                             break;
01725                     }
01726 
01727                     errno = 0;
01728                     val = strtoi(field[i], &cp, 10);
01729                     if (errno == ERANGE)
01730                         return DTERR_FIELD_OVERFLOW;
01731 
01732                     /*
01733                      * only a few kinds are allowed to have an embedded
01734                      * decimal
01735                      */
01736                     if (*cp == '.')
01737                         switch (ptype)
01738                         {
01739                             case DTK_JULIAN:
01740                             case DTK_TIME:
01741                             case DTK_SECOND:
01742                                 break;
01743                             default:
01744                                 return DTERR_BAD_FORMAT;
01745                                 break;
01746                         }
01747                     else if (*cp != '\0')
01748                         return DTERR_BAD_FORMAT;
01749 
01750                     switch (ptype)
01751                     {
01752                         case DTK_YEAR:
01753                             tm->tm_year = val;
01754                             tmask = DTK_M(YEAR);
01755                             break;
01756 
01757                         case DTK_MONTH:
01758 
01759                             /*
01760                              * already have a month and hour? then assume
01761                              * minutes
01762                              */
01763                             if ((fmask & DTK_M(MONTH)) != 0 &&
01764                                 (fmask & DTK_M(HOUR)) != 0)
01765                             {
01766                                 tm->tm_min = val;
01767                                 tmask = DTK_M(MINUTE);
01768                             }
01769                             else
01770                             {
01771                                 tm->tm_mon = val;
01772                                 tmask = DTK_M(MONTH);
01773                             }
01774                             break;
01775 
01776                         case DTK_DAY:
01777                             tm->tm_mday = val;
01778                             tmask = DTK_M(DAY);
01779                             break;
01780 
01781                         case DTK_HOUR:
01782                             tm->tm_hour = val;
01783                             tmask = DTK_M(HOUR);
01784                             break;
01785 
01786                         case DTK_MINUTE:
01787                             tm->tm_min = val;
01788                             tmask = DTK_M(MINUTE);
01789                             break;
01790 
01791                         case DTK_SECOND:
01792                             tm->tm_sec = val;
01793                             tmask = DTK_M(SECOND);
01794                             if (*cp == '.')
01795                             {
01796                                 dterr = ParseFractionalSecond(cp, fsec);
01797                                 if (dterr)
01798                                     return dterr;
01799                                 tmask = DTK_ALL_SECS_M;
01800                             }
01801                             break;
01802 
01803                         case DTK_TZ:
01804                             tmask = DTK_M(TZ);
01805                             dterr = DecodeTimezone(field[i], tzp);
01806                             if (dterr)
01807                                 return dterr;
01808                             break;
01809 
01810                         case DTK_JULIAN:
01811                             /* previous field was a label for "julian date" */
01812                             if (val < 0)
01813                                 return DTERR_FIELD_OVERFLOW;
01814                             tmask = DTK_DATE_M;
01815                             j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01816                             isjulian = TRUE;
01817 
01818                             if (*cp == '.')
01819                             {
01820                                 double      time;
01821 
01822                                 errno = 0;
01823                                 time = strtod(cp, &cp);
01824                                 if (*cp != '\0' || errno != 0)
01825                                     return DTERR_BAD_FORMAT;
01826 
01827 #ifdef HAVE_INT64_TIMESTAMP
01828                                 time *= USECS_PER_DAY;
01829 #else
01830                                 time *= SECS_PER_DAY;
01831 #endif
01832                                 dt2time(time,
01833                                         &tm->tm_hour, &tm->tm_min,
01834                                         &tm->tm_sec, fsec);
01835                                 tmask |= DTK_TIME_M;
01836                             }
01837                             break;
01838 
01839                         case DTK_TIME:
01840                             /* previous field was "t" for ISO time */
01841                             dterr = DecodeNumberField(strlen(field[i]), field[i],
01842                                                       (fmask | DTK_DATE_M),
01843                                                       &tmask, tm,
01844                                                       fsec, &is2digits);
01845                             if (dterr < 0)
01846                                 return dterr;
01847                             ftype[i] = dterr;
01848 
01849                             if (tmask != DTK_TIME_M)
01850                                 return DTERR_BAD_FORMAT;
01851                             break;
01852 
01853                         default:
01854                             return DTERR_BAD_FORMAT;
01855                             break;
01856                     }
01857 
01858                     ptype = 0;
01859                     *dtype = DTK_DATE;
01860                 }
01861                 else
01862                 {
01863                     char       *cp;
01864                     int         flen;
01865 
01866                     flen = strlen(field[i]);
01867                     cp = strchr(field[i], '.');
01868 
01869                     /* Embedded decimal? */
01870                     if (cp != NULL)
01871                     {
01872                         /*
01873                          * Under limited circumstances, we will accept a
01874                          * date...
01875                          */
01876                         if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
01877                         {
01878                             dterr = DecodeDate(field[i], fmask,
01879                                                &tmask, &is2digits, tm);
01880                             if (dterr)
01881                                 return dterr;
01882                         }
01883                         /* embedded decimal and several digits before? */
01884                         else if (flen - strlen(cp) > 2)
01885                         {
01886                             /*
01887                              * Interpret as a concatenated date or time Set
01888                              * the type field to allow decoding other fields
01889                              * later. Example: 20011223 or 040506
01890                              */
01891                             dterr = DecodeNumberField(flen, field[i],
01892                                                       (fmask | DTK_DATE_M),
01893                                                       &tmask, tm,
01894                                                       fsec, &is2digits);
01895                             if (dterr < 0)
01896                                 return dterr;
01897                             ftype[i] = dterr;
01898                         }
01899                         else
01900                             return DTERR_BAD_FORMAT;
01901                     }
01902                     else if (flen > 4)
01903                     {
01904                         dterr = DecodeNumberField(flen, field[i],
01905                                                   (fmask | DTK_DATE_M),
01906                                                   &tmask, tm,
01907                                                   fsec, &is2digits);
01908                         if (dterr < 0)
01909                             return dterr;
01910                         ftype[i] = dterr;
01911                     }
01912                     /* otherwise it is a single date/time field... */
01913                     else
01914                     {
01915                         dterr = DecodeNumber(flen, field[i],
01916                                              FALSE,
01917                                              (fmask | DTK_DATE_M),
01918                                              &tmask, tm,
01919                                              fsec, &is2digits);
01920                         if (dterr)
01921                             return dterr;
01922                     }
01923                 }
01924                 break;
01925 
01926             case DTK_STRING:
01927             case DTK_SPECIAL:
01928                 type = DecodeSpecial(i, field[i], &val);
01929                 if (type == IGNORE_DTF)
01930                     continue;
01931 
01932                 tmask = DTK_M(type);
01933                 switch (type)
01934                 {
01935                     case RESERV:
01936                         switch (val)
01937                         {
01938                             case DTK_CURRENT:
01939                                 ereport(ERROR,
01940                                      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01941                                       errmsg("date/time value \"current\" is no longer supported")));
01942                                 return DTERR_BAD_FORMAT;
01943                                 break;
01944 
01945                             case DTK_NOW:
01946                                 tmask = DTK_TIME_M;
01947                                 *dtype = DTK_TIME;
01948                                 GetCurrentTimeUsec(tm, fsec, NULL);
01949                                 break;
01950 
01951                             case DTK_ZULU:
01952                                 tmask = (DTK_TIME_M | DTK_M(TZ));
01953                                 *dtype = DTK_TIME;
01954                                 tm->tm_hour = 0;
01955                                 tm->tm_min = 0;
01956                                 tm->tm_sec = 0;
01957                                 tm->tm_isdst = 0;
01958                                 break;
01959 
01960                             default:
01961                                 return DTERR_BAD_FORMAT;
01962                         }
01963 
01964                         break;
01965 
01966                     case DTZMOD:
01967 
01968                         /*
01969                          * daylight savings time modifier (solves "MET DST"
01970                          * syntax)
01971                          */
01972                         tmask |= DTK_M(DTZ);
01973                         tm->tm_isdst = 1;
01974                         if (tzp == NULL)
01975                             return DTERR_BAD_FORMAT;
01976                         *tzp += val * MINS_PER_HOUR;
01977                         break;
01978 
01979                     case DTZ:
01980 
01981                         /*
01982                          * set mask for TZ here _or_ check for DTZ later when
01983                          * getting default timezone
01984                          */
01985                         tmask |= DTK_M(TZ);
01986                         tm->tm_isdst = 1;
01987                         if (tzp == NULL)
01988                             return DTERR_BAD_FORMAT;
01989                         *tzp = val * MINS_PER_HOUR;
01990                         ftype[i] = DTK_TZ;
01991                         break;
01992 
01993                     case TZ:
01994                         tm->tm_isdst = 0;
01995                         if (tzp == NULL)
01996                             return DTERR_BAD_FORMAT;
01997                         *tzp = val * MINS_PER_HOUR;
01998                         ftype[i] = DTK_TZ;
01999                         break;
02000 
02001                     case IGNORE_DTF:
02002                         break;
02003 
02004                     case AMPM:
02005                         mer = val;
02006                         break;
02007 
02008                     case ADBC:
02009                         bc = (val == BC);
02010                         break;
02011 
02012                     case UNITS:
02013                         tmask = 0;
02014                         ptype = val;
02015                         break;
02016 
02017                     case ISOTIME:
02018                         tmask = 0;
02019 
02020                         /***
02021                          * We will need one of the following fields:
02022                          *  DTK_NUMBER should be hhmmss.fff
02023                          *  DTK_TIME should be hh:mm:ss.fff
02024                          *  DTK_DATE should be hhmmss-zz
02025                          ***/
02026                         if (i >= nf - 1 ||
02027                             (ftype[i + 1] != DTK_NUMBER &&
02028                              ftype[i + 1] != DTK_TIME &&
02029                              ftype[i + 1] != DTK_DATE))
02030                             return DTERR_BAD_FORMAT;
02031 
02032                         ptype = val;
02033                         break;
02034 
02035                     case UNKNOWN_FIELD:
02036 
02037                         /*
02038                          * Before giving up and declaring error, check to see
02039                          * if it is an all-alpha timezone name.
02040                          */
02041                         namedTz = pg_tzset(field[i]);
02042                         if (!namedTz)
02043                             return DTERR_BAD_FORMAT;
02044                         /* we'll apply the zone setting below */
02045                         tmask = DTK_M(TZ);
02046                         break;
02047 
02048                     default:
02049                         return DTERR_BAD_FORMAT;
02050                 }
02051                 break;
02052 
02053             default:
02054                 return DTERR_BAD_FORMAT;
02055         }
02056 
02057         if (tmask & fmask)
02058             return DTERR_BAD_FORMAT;
02059         fmask |= tmask;
02060     }                           /* end loop over fields */
02061 
02062     /* do final checking/adjustment of Y/M/D fields */
02063     dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
02064     if (dterr)
02065         return dterr;
02066 
02067     /* handle AM/PM */
02068     if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
02069         return DTERR_FIELD_OVERFLOW;
02070     if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
02071         tm->tm_hour = 0;
02072     else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
02073         tm->tm_hour += HOURS_PER_DAY / 2;
02074 
02075     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 ||
02076         tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE ||
02077         tm->tm_hour > HOURS_PER_DAY ||
02078     /* test for > 24:00:00 */
02079         (tm->tm_hour == HOURS_PER_DAY &&
02080          (tm->tm_min > 0 || tm->tm_sec > 0 || *fsec > 0)) ||
02081 #ifdef HAVE_INT64_TIMESTAMP
02082         *fsec < INT64CONST(0) || *fsec > USECS_PER_SEC
02083 #else
02084         *fsec < 0 || *fsec > 1
02085 #endif
02086         )
02087         return DTERR_FIELD_OVERFLOW;
02088 
02089     if ((fmask & DTK_TIME_M) != DTK_TIME_M)
02090         return DTERR_BAD_FORMAT;
02091 
02092     /*
02093      * If we had a full timezone spec, compute the offset (we could not do it
02094      * before, because we may need the date to resolve DST status).
02095      */
02096     if (namedTz != NULL)
02097     {
02098         long int    gmtoff;
02099 
02100         /* daylight savings time modifier disallowed with full TZ */
02101         if (fmask & DTK_M(DTZMOD))
02102             return DTERR_BAD_FORMAT;
02103 
02104         /* if non-DST zone, we do not need to know the date */
02105         if (pg_get_timezone_offset(namedTz, &gmtoff))
02106         {
02107             *tzp = -(int) gmtoff;
02108         }
02109         else
02110         {
02111             /* a date has to be specified */
02112             if ((fmask & DTK_DATE_M) != DTK_DATE_M)
02113                 return DTERR_BAD_FORMAT;
02114             *tzp = DetermineTimeZoneOffset(tm, namedTz);
02115         }
02116     }
02117 
02118     /* timezone not specified? then find local timezone if possible */
02119     if (tzp != NULL && !(fmask & DTK_M(TZ)))
02120     {
02121         struct pg_tm tt,
02122                    *tmp = &tt;
02123 
02124         /*
02125          * daylight savings time modifier but no standard timezone? then error
02126          */
02127         if (fmask & DTK_M(DTZMOD))
02128             return DTERR_BAD_FORMAT;
02129 
02130         if ((fmask & DTK_DATE_M) == 0)
02131             GetCurrentDateTime(tmp);
02132         else
02133         {
02134             tmp->tm_year = tm->tm_year;
02135             tmp->tm_mon = tm->tm_mon;
02136             tmp->tm_mday = tm->tm_mday;
02137         }
02138         tmp->tm_hour = tm->tm_hour;
02139         tmp->tm_min = tm->tm_min;
02140         tmp->tm_sec = tm->tm_sec;
02141         *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
02142         tm->tm_isdst = tmp->tm_isdst;
02143     }
02144 
02145     return 0;
02146 }
02147 
02148 /* DecodeDate()
02149  * Decode date string which includes delimiters.
02150  * Return 0 if okay, a DTERR code if not.
02151  *
02152  *  str: field to be parsed
02153  *  fmask: bitmask for field types already seen
02154  *  *tmask: receives bitmask for fields found here
02155  *  *is2digits: set to TRUE if we find 2-digit year
02156  *  *tm: field values are stored into appropriate members of this struct
02157  */
02158 static int
02159 DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
02160            struct pg_tm * tm)
02161 {
02162     fsec_t      fsec;
02163     int         nf = 0;
02164     int         i,
02165                 len;
02166     int         dterr;
02167     bool        haveTextMonth = FALSE;
02168     int         type,
02169                 val,
02170                 dmask = 0;
02171     char       *field[MAXDATEFIELDS];
02172 
02173     *tmask = 0;
02174 
02175     /* parse this string... */
02176     while (*str != '\0' && nf < MAXDATEFIELDS)
02177     {
02178         /* skip field separators */
02179         while (*str != '\0' && !isalnum((unsigned char) *str))
02180             str++;
02181 
02182         if (*str == '\0')
02183             return DTERR_BAD_FORMAT;        /* end of string after separator */
02184 
02185         field[nf] = str;
02186         if (isdigit((unsigned char) *str))
02187         {
02188             while (isdigit((unsigned char) *str))
02189                 str++;
02190         }
02191         else if (isalpha((unsigned char) *str))
02192         {
02193             while (isalpha((unsigned char) *str))
02194                 str++;
02195         }
02196 
02197         /* Just get rid of any non-digit, non-alpha characters... */
02198         if (*str != '\0')
02199             *str++ = '\0';
02200         nf++;
02201     }
02202 
02203     /* look first for text fields, since that will be unambiguous month */
02204     for (i = 0; i < nf; i++)
02205     {
02206         if (isalpha((unsigned char) *field[i]))
02207         {
02208             type = DecodeSpecial(i, field[i], &val);
02209             if (type == IGNORE_DTF)
02210                 continue;
02211 
02212             dmask = DTK_M(type);
02213             switch (type)
02214             {
02215                 case MONTH:
02216                     tm->tm_mon = val;
02217                     haveTextMonth = TRUE;
02218                     break;
02219 
02220                 default:
02221                     return DTERR_BAD_FORMAT;
02222             }
02223             if (fmask & dmask)
02224                 return DTERR_BAD_FORMAT;
02225 
02226             fmask |= dmask;
02227             *tmask |= dmask;
02228 
02229             /* mark this field as being completed */
02230             field[i] = NULL;
02231         }
02232     }
02233 
02234     /* now pick up remaining numeric fields */
02235     for (i = 0; i < nf; i++)
02236     {
02237         if (field[i] == NULL)
02238             continue;
02239 
02240         if ((len = strlen(field[i])) <= 0)
02241             return DTERR_BAD_FORMAT;
02242 
02243         dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
02244                              &dmask, tm,
02245                              &fsec, is2digits);
02246         if (dterr)
02247             return dterr;
02248 
02249         if (fmask & dmask)
02250             return DTERR_BAD_FORMAT;
02251 
02252         fmask |= dmask;
02253         *tmask |= dmask;
02254     }
02255 
02256     if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
02257         return DTERR_BAD_FORMAT;
02258 
02259     /* validation of the field values must wait until ValidateDate() */
02260 
02261     return 0;
02262 }
02263 
02264 /* ValidateDate()
02265  * Check valid year/month/day values, handle BC and DOY cases
02266  * Return 0 if okay, a DTERR code if not.
02267  */
02268 static int
02269 ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
02270              struct pg_tm * tm)
02271 {
02272     if (fmask & DTK_M(YEAR))
02273     {
02274         if (isjulian)
02275         {
02276             /* tm_year is correct and should not be touched */
02277         }
02278         else if (bc)
02279         {
02280             /* there is no year zero in AD/BC notation */
02281             if (tm->tm_year <= 0)
02282                 return DTERR_FIELD_OVERFLOW;
02283             /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
02284             tm->tm_year = -(tm->tm_year - 1);
02285         }
02286         else if (is2digits)
02287         {
02288             /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */
02289             if (tm->tm_year < 0)    /* just paranoia */
02290                 return DTERR_FIELD_OVERFLOW;
02291             if (tm->tm_year < 70)
02292                 tm->tm_year += 2000;
02293             else if (tm->tm_year < 100)
02294                 tm->tm_year += 1900;
02295         }
02296         else
02297         {
02298             /* there is no year zero in AD/BC notation */
02299             if (tm->tm_year <= 0)
02300                 return DTERR_FIELD_OVERFLOW;
02301         }
02302     }
02303 
02304     /* now that we have correct year, decode DOY */
02305     if (fmask & DTK_M(DOY))
02306     {
02307         j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
02308                &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
02309     }
02310 
02311     /* check for valid month */
02312     if (fmask & DTK_M(MONTH))
02313     {
02314         if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
02315             return DTERR_MD_FIELD_OVERFLOW;
02316     }
02317 
02318     /* minimal check for valid day */
02319     if (fmask & DTK_M(DAY))
02320     {
02321         if (tm->tm_mday < 1 || tm->tm_mday > 31)
02322             return DTERR_MD_FIELD_OVERFLOW;
02323     }
02324 
02325     if ((fmask & DTK_DATE_M) == DTK_DATE_M)
02326     {
02327         /*
02328          * Check for valid day of month, now that we know for sure the month
02329          * and year.  Note we don't use MD_FIELD_OVERFLOW here, since it seems
02330          * unlikely that "Feb 29" is a YMD-order error.
02331          */
02332         if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
02333             return DTERR_FIELD_OVERFLOW;
02334     }
02335 
02336     return 0;
02337 }
02338 
02339 
02340 /* DecodeTime()
02341  * Decode time string which includes delimiters.
02342  * Return 0 if okay, a DTERR code if not.
02343  *
02344  * Only check the lower limit on hours, since this same code can be
02345  * used to represent time spans.
02346  */
02347 static int
02348 DecodeTime(char *str, int fmask, int range,
02349            int *tmask, struct pg_tm * tm, fsec_t *fsec)
02350 {
02351     char       *cp;
02352     int         dterr;
02353 
02354     *tmask = DTK_TIME_M;
02355 
02356     errno = 0;
02357     tm->tm_hour = strtoi(str, &cp, 10);
02358     if (errno == ERANGE)
02359         return DTERR_FIELD_OVERFLOW;
02360     if (*cp != ':')
02361         return DTERR_BAD_FORMAT;
02362     errno = 0;
02363     tm->tm_min = strtoi(cp + 1, &cp, 10);
02364     if (errno == ERANGE)
02365         return DTERR_FIELD_OVERFLOW;
02366     if (*cp == '\0')
02367     {
02368         tm->tm_sec = 0;
02369         *fsec = 0;
02370         /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
02371         if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
02372         {
02373             tm->tm_sec = tm->tm_min;
02374             tm->tm_min = tm->tm_hour;
02375             tm->tm_hour = 0;
02376         }
02377     }
02378     else if (*cp == '.')
02379     {
02380         /* always assume mm:ss.sss is MINUTE TO SECOND */
02381         dterr = ParseFractionalSecond(cp, fsec);
02382         if (dterr)
02383             return dterr;
02384         tm->tm_sec = tm->tm_min;
02385         tm->tm_min = tm->tm_hour;
02386         tm->tm_hour = 0;
02387     }
02388     else if (*cp == ':')
02389     {
02390         errno = 0;
02391         tm->tm_sec = strtoi(cp + 1, &cp, 10);
02392         if (errno == ERANGE)
02393             return DTERR_FIELD_OVERFLOW;
02394         if (*cp == '\0')
02395             *fsec = 0;
02396         else if (*cp == '.')
02397         {
02398             dterr = ParseFractionalSecond(cp, fsec);
02399             if (dterr)
02400                 return dterr;
02401         }
02402         else
02403             return DTERR_BAD_FORMAT;
02404     }
02405     else
02406         return DTERR_BAD_FORMAT;
02407 
02408     /* do a sanity check */
02409 #ifdef HAVE_INT64_TIMESTAMP
02410     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 ||
02411         tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE ||
02412         *fsec < INT64CONST(0) ||
02413         *fsec > USECS_PER_SEC)
02414         return DTERR_FIELD_OVERFLOW;
02415 #else
02416     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 ||
02417         tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE ||
02418         *fsec < 0 || *fsec > 1)
02419         return DTERR_FIELD_OVERFLOW;
02420 #endif
02421 
02422     return 0;
02423 }
02424 
02425 
02426 /* DecodeNumber()
02427  * Interpret plain numeric field as a date value in context.
02428  * Return 0 if okay, a DTERR code if not.
02429  */
02430 static int
02431 DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
02432              int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits)
02433 {
02434     int         val;
02435     char       *cp;
02436     int         dterr;
02437 
02438     *tmask = 0;
02439 
02440     errno = 0;
02441     val = strtoi(str, &cp, 10);
02442     if (errno == ERANGE)
02443         return DTERR_FIELD_OVERFLOW;
02444     if (cp == str)
02445         return DTERR_BAD_FORMAT;
02446 
02447     if (*cp == '.')
02448     {
02449         /*
02450          * More than two digits before decimal point? Then could be a date or
02451          * a run-together time: 2001.360 20011225 040506.789
02452          */
02453         if (cp - str > 2)
02454         {
02455             dterr = DecodeNumberField(flen, str,
02456                                       (fmask | DTK_DATE_M),
02457                                       tmask, tm,
02458                                       fsec, is2digits);
02459             if (dterr < 0)
02460                 return dterr;
02461             return 0;
02462         }
02463 
02464         dterr = ParseFractionalSecond(cp, fsec);
02465         if (dterr)
02466             return dterr;
02467     }
02468     else if (*cp != '\0')
02469         return DTERR_BAD_FORMAT;
02470 
02471     /* Special case for day of year */
02472     if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&
02473         val <= 366)
02474     {
02475         *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
02476         tm->tm_yday = val;
02477         /* tm_mon and tm_mday can't actually be set yet ... */
02478         return 0;
02479     }
02480 
02481     /* Switch based on what we have so far */
02482     switch (fmask & DTK_DATE_M)
02483     {
02484         case 0:
02485 
02486             /*
02487              * Nothing so far; make a decision about what we think the input
02488              * is.  There used to be lots of heuristics here, but the
02489              * consensus now is to be paranoid.  It *must* be either
02490              * YYYY-MM-DD (with a more-than-two-digit year field), or the
02491              * field order defined by DateOrder.
02492              */
02493             if (flen >= 3 || DateOrder == DATEORDER_YMD)
02494             {
02495                 *tmask = DTK_M(YEAR);
02496                 tm->tm_year = val;
02497             }
02498             else if (DateOrder == DATEORDER_DMY)
02499             {
02500                 *tmask = DTK_M(DAY);
02501                 tm->tm_mday = val;
02502             }
02503             else
02504             {
02505                 *tmask = DTK_M(MONTH);
02506                 tm->tm_mon = val;
02507             }
02508             break;
02509 
02510         case (DTK_M(YEAR)):
02511             /* Must be at second field of YY-MM-DD */
02512             *tmask = DTK_M(MONTH);
02513             tm->tm_mon = val;
02514             break;
02515 
02516         case (DTK_M(MONTH)):
02517             if (haveTextMonth)
02518             {
02519                 /*
02520                  * We are at the first numeric field of a date that included a
02521                  * textual month name.  We want to support the variants
02522                  * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
02523                  * inputs.  We will also accept MON-DD-YY or DD-MON-YY in
02524                  * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
02525                  */
02526                 if (flen >= 3 || DateOrder == DATEORDER_YMD)
02527                 {
02528                     *tmask = DTK_M(YEAR);
02529                     tm->tm_year = val;
02530                 }
02531                 else
02532                 {
02533                     *tmask = DTK_M(DAY);
02534                     tm->tm_mday = val;
02535                 }
02536             }
02537             else
02538             {
02539                 /* Must be at second field of MM-DD-YY */
02540                 *tmask = DTK_M(DAY);
02541                 tm->tm_mday = val;
02542             }
02543             break;
02544 
02545         case (DTK_M(YEAR) | DTK_M(MONTH)):
02546             if (haveTextMonth)
02547             {
02548                 /* Need to accept DD-MON-YYYY even in YMD mode */
02549                 if (flen >= 3 && *is2digits)
02550                 {
02551                     /* Guess that first numeric field is day was wrong */
02552                     *tmask = DTK_M(DAY);        /* YEAR is already set */
02553                     tm->tm_mday = tm->tm_year;
02554                     tm->tm_year = val;
02555                     *is2digits = FALSE;
02556                 }
02557                 else
02558                 {
02559                     *tmask = DTK_M(DAY);
02560                     tm->tm_mday = val;
02561                 }
02562             }
02563             else
02564             {
02565                 /* Must be at third field of YY-MM-DD */
02566                 *tmask = DTK_M(DAY);
02567                 tm->tm_mday = val;
02568             }
02569             break;
02570 
02571         case (DTK_M(DAY)):
02572             /* Must be at second field of DD-MM-YY */
02573             *tmask = DTK_M(MONTH);
02574             tm->tm_mon = val;
02575             break;
02576 
02577         case (DTK_M(MONTH) | DTK_M(DAY)):
02578             /* Must be at third field of DD-MM-YY or MM-DD-YY */
02579             *tmask = DTK_M(YEAR);
02580             tm->tm_year = val;
02581             break;
02582 
02583         case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
02584             /* we have all the date, so it must be a time field */
02585             dterr = DecodeNumberField(flen, str, fmask,
02586                                       tmask, tm,
02587                                       fsec, is2digits);
02588             if (dterr < 0)
02589                 return dterr;
02590             return 0;
02591 
02592         default:
02593             /* Anything else is bogus input */
02594             return DTERR_BAD_FORMAT;
02595     }
02596 
02597     /*
02598      * When processing a year field, mark it for adjustment if it's only one
02599      * or two digits.
02600      */
02601     if (*tmask == DTK_M(YEAR))
02602         *is2digits = (flen <= 2);
02603 
02604     return 0;
02605 }
02606 
02607 
02608 /* DecodeNumberField()
02609  * Interpret numeric string as a concatenated date or time field.
02610  * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
02611  *
02612  * Use the context of previously decoded fields to help with
02613  * the interpretation.
02614  */
02615 static int
02616 DecodeNumberField(int len, char *str, int fmask,
02617                 int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits)
02618 {
02619     char       *cp;
02620 
02621     /*
02622      * Have a decimal point? Then this is a date or something with a seconds
02623      * field...
02624      */
02625     if ((cp = strchr(str, '.')) != NULL)
02626     {
02627         /*
02628          * Can we use ParseFractionalSecond here?  Not clear whether trailing
02629          * junk should be rejected ...
02630          */
02631         double      frac;
02632 
02633         errno = 0;
02634         frac = strtod(cp, NULL);
02635         if (errno != 0)
02636             return DTERR_BAD_FORMAT;
02637 #ifdef HAVE_INT64_TIMESTAMP
02638         *fsec = rint(frac * 1000000);
02639 #else
02640         *fsec = frac;
02641 #endif
02642         /* Now truncate off the fraction for further processing */
02643         *cp = '\0';
02644         len = strlen(str);
02645     }
02646     /* No decimal point and no complete date yet? */
02647     else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
02648     {
02649         /* yyyymmdd? */
02650         if (len == 8)
02651         {
02652             *tmask = DTK_DATE_M;
02653 
02654             tm->tm_mday = atoi(str + 6);
02655             *(str + 6) = '\0';
02656             tm->tm_mon = atoi(str + 4);
02657             *(str + 4) = '\0';
02658             tm->tm_year = atoi(str + 0);
02659 
02660             return DTK_DATE;
02661         }
02662         /* yymmdd? */
02663         else if (len == 6)
02664         {
02665             *tmask = DTK_DATE_M;
02666             tm->tm_mday = atoi(str + 4);
02667             *(str + 4) = '\0';
02668             tm->tm_mon = atoi(str + 2);
02669             *(str + 2) = '\0';
02670             tm->tm_year = atoi(str + 0);
02671             *is2digits = TRUE;
02672 
02673             return DTK_DATE;
02674         }
02675     }
02676 
02677     /* not all time fields are specified? */
02678     if ((fmask & DTK_TIME_M) != DTK_TIME_M)
02679     {
02680         /* hhmmss */
02681         if (len == 6)
02682         {
02683             *tmask = DTK_TIME_M;
02684             tm->tm_sec = atoi(str + 4);
02685             *(str + 4) = '\0';
02686             tm->tm_min = atoi(str + 2);
02687             *(str + 2) = '\0';
02688             tm->tm_hour = atoi(str + 0);
02689 
02690             return DTK_TIME;
02691         }
02692         /* hhmm? */
02693         else if (len == 4)
02694         {
02695             *tmask = DTK_TIME_M;
02696             tm->tm_sec = 0;
02697             tm->tm_min = atoi(str + 2);
02698             *(str + 2) = '\0';
02699             tm->tm_hour = atoi(str + 0);
02700 
02701             return DTK_TIME;
02702         }
02703     }
02704 
02705     return DTERR_BAD_FORMAT;
02706 }
02707 
02708 
02709 /* DecodeTimezone()
02710  * Interpret string as a numeric timezone.
02711  *
02712  * Return 0 if okay (and set *tzp), a DTERR code if not okay.
02713  *
02714  * NB: this must *not* ereport on failure; see commands/variable.c.
02715  */
02716 static int
02717 DecodeTimezone(char *str, int *tzp)
02718 {
02719     int         tz;
02720     int         hr,
02721                 min,
02722                 sec = 0;
02723     char       *cp;
02724 
02725     /* leading character must be "+" or "-" */
02726     if (*str != '+' && *str != '-')
02727         return DTERR_BAD_FORMAT;
02728 
02729     errno = 0;
02730     hr = strtoi(str + 1, &cp, 10);
02731     if (errno == ERANGE)
02732         return DTERR_TZDISP_OVERFLOW;
02733 
02734     /* explicit delimiter? */
02735     if (*cp == ':')
02736     {
02737         errno = 0;
02738         min = strtoi(cp + 1, &cp, 10);
02739         if (errno == ERANGE)
02740             return DTERR_TZDISP_OVERFLOW;
02741         if (*cp == ':')
02742         {
02743             errno = 0;
02744             sec = strtoi(cp + 1, &cp, 10);
02745             if (errno == ERANGE)
02746                 return DTERR_TZDISP_OVERFLOW;
02747         }
02748     }
02749     /* otherwise, might have run things together... */
02750     else if (*cp == '\0' && strlen(str) > 3)
02751     {
02752         min = hr % 100;
02753         hr = hr / 100;
02754         /* we could, but don't, support a run-together hhmmss format */
02755     }
02756     else
02757         min = 0;
02758 
02759     /* Range-check the values; see notes in datatype/timestamp.h */
02760     if (hr < 0 || hr > MAX_TZDISP_HOUR)
02761         return DTERR_TZDISP_OVERFLOW;
02762     if (min < 0 || min >= MINS_PER_HOUR)
02763         return DTERR_TZDISP_OVERFLOW;
02764     if (sec < 0 || sec >= SECS_PER_MINUTE)
02765         return DTERR_TZDISP_OVERFLOW;
02766 
02767     tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
02768     if (*str == '-')
02769         tz = -tz;
02770 
02771     *tzp = -tz;
02772 
02773     if (*cp != '\0')
02774         return DTERR_BAD_FORMAT;
02775 
02776     return 0;
02777 }
02778 
02779 /* DecodeSpecial()
02780  * Decode text string using lookup table.
02781  *
02782  * Implement a cache lookup since it is likely that dates
02783  *  will be related in format.
02784  *
02785  * NB: this must *not* ereport on failure;
02786  * see commands/variable.c.
02787  */
02788 int
02789 DecodeSpecial(int field, char *lowtoken, int *val)
02790 {
02791     int         type;
02792     const datetkn *tp;
02793 
02794     tp = datecache[field];
02795     if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
02796     {
02797         tp = datebsearch(lowtoken, timezonetktbl, sztimezonetktbl);
02798         if (tp == NULL)
02799             tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
02800     }
02801     if (tp == NULL)
02802     {
02803         type = UNKNOWN_FIELD;
02804         *val = 0;
02805     }
02806     else
02807     {
02808         datecache[field] = tp;
02809         type = tp->type;
02810         switch (type)
02811         {
02812             case TZ:
02813             case DTZ:
02814             case DTZMOD:
02815                 *val = FROMVAL(tp);
02816                 break;
02817 
02818             default:
02819                 *val = tp->value;
02820                 break;
02821         }
02822     }
02823 
02824     return type;
02825 }
02826 
02827 
02828 /* ClearPgTM
02829  *
02830  * Zero out a pg_tm and associated fsec_t
02831  */
02832 static inline void
02833 ClearPgTm(struct pg_tm * tm, fsec_t *fsec)
02834 {
02835     tm->tm_year = 0;
02836     tm->tm_mon = 0;
02837     tm->tm_mday = 0;
02838     tm->tm_hour = 0;
02839     tm->tm_min = 0;
02840     tm->tm_sec = 0;
02841     *fsec = 0;
02842 }
02843 
02844 
02845 /* DecodeInterval()
02846  * Interpret previously parsed fields for general time interval.
02847  * Returns 0 if successful, DTERR code if bogus input detected.
02848  * dtype, tm, fsec are output parameters.
02849  *
02850  * Allow "date" field DTK_DATE since this could be just
02851  *  an unsigned floating point number. - thomas 1997-11-16
02852  *
02853  * Allow ISO-style time span, with implicit units on number of days
02854  *  preceding an hh:mm:ss field. - thomas 1998-04-30
02855  */
02856 int
02857 DecodeInterval(char **field, int *ftype, int nf, int range,
02858                int *dtype, struct pg_tm * tm, fsec_t *fsec)
02859 {
02860     bool        is_before = FALSE;
02861     char       *cp;
02862     int         fmask = 0,
02863                 tmask,
02864                 type;
02865     int         i;
02866     int         dterr;
02867     int         val;
02868     double      fval;
02869 
02870     *dtype = DTK_DELTA;
02871     type = IGNORE_DTF;
02872     ClearPgTm(tm, fsec);
02873 
02874     /* read through list backwards to pick up units before values */
02875     for (i = nf - 1; i >= 0; i--)
02876     {
02877         switch (ftype[i])
02878         {
02879             case DTK_TIME:
02880                 dterr = DecodeTime(field[i], fmask, range,
02881                                    &tmask, tm, fsec);
02882                 if (dterr)
02883                     return dterr;
02884                 type = DTK_DAY;
02885                 break;
02886 
02887             case DTK_TZ:
02888 
02889                 /*
02890                  * Timezone means a token with a leading sign character and at
02891                  * least one digit; there could be ':', '.', '-' embedded in
02892                  * it as well.
02893                  */
02894                 Assert(*field[i] == '-' || *field[i] == '+');
02895 
02896                 /*
02897                  * Check for signed hh:mm or hh:mm:ss.  If so, process exactly
02898                  * like DTK_TIME case above, plus handling the sign.
02899                  */
02900                 if (strchr(field[i] + 1, ':') != NULL &&
02901                     DecodeTime(field[i] + 1, fmask, range,
02902                                &tmask, tm, fsec) == 0)
02903                 {
02904                     if (*field[i] == '-')
02905                     {
02906                         /* flip the sign on all fields */
02907                         tm->tm_hour = -tm->tm_hour;
02908                         tm->tm_min = -tm->tm_min;
02909                         tm->tm_sec = -tm->tm_sec;
02910                         *fsec = -(*fsec);
02911                     }
02912 
02913                     /*
02914                      * Set the next type to be a day, if units are not
02915                      * specified. This handles the case of '1 +02:03' since we
02916                      * are reading right to left.
02917                      */
02918                     type = DTK_DAY;
02919                     break;
02920                 }
02921 
02922                 /*
02923                  * Otherwise, fall through to DTK_NUMBER case, which can
02924                  * handle signed float numbers and signed year-month values.
02925                  */
02926 
02927                 /* FALL THROUGH */
02928 
02929             case DTK_DATE:
02930             case DTK_NUMBER:
02931                 if (type == IGNORE_DTF)
02932                 {
02933                     /* use typmod to decide what rightmost field is */
02934                     switch (range)
02935                     {
02936                         case INTERVAL_MASK(YEAR):
02937                             type = DTK_YEAR;
02938                             break;
02939                         case INTERVAL_MASK(MONTH):
02940                         case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
02941                             type = DTK_MONTH;
02942                             break;
02943                         case INTERVAL_MASK(DAY):
02944                             type = DTK_DAY;
02945                             break;
02946                         case INTERVAL_MASK(HOUR):
02947                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
02948                             type = DTK_HOUR;
02949                             break;
02950                         case INTERVAL_MASK(MINUTE):
02951                         case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
02952                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
02953                             type = DTK_MINUTE;
02954                             break;
02955                         case INTERVAL_MASK(SECOND):
02956                         case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
02957                         case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
02958                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
02959                             type = DTK_SECOND;
02960                             break;
02961                         default:
02962                             type = DTK_SECOND;
02963                             break;
02964                     }
02965                 }
02966 
02967                 errno = 0;
02968                 val = strtoi(field[i], &cp, 10);
02969                 if (errno == ERANGE)
02970                     return DTERR_FIELD_OVERFLOW;
02971 
02972                 if (*cp == '-')
02973                 {
02974                     /* SQL "years-months" syntax */
02975                     int         val2;
02976 
02977                     val2 = strtoi(cp + 1, &cp, 10);
02978                     if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
02979                         return DTERR_FIELD_OVERFLOW;
02980                     if (*cp != '\0')
02981                         return DTERR_BAD_FORMAT;
02982                     type = DTK_MONTH;
02983                     if (*field[i] == '-')
02984                         val2 = -val2;
02985                     val = val * MONTHS_PER_YEAR + val2;
02986                     fval = 0;
02987                 }
02988                 else if (*cp == '.')
02989                 {
02990                     errno = 0;
02991                     fval = strtod(cp, &cp);
02992                     if (*cp != '\0' || errno != 0)
02993                         return DTERR_BAD_FORMAT;
02994 
02995                     if (*field[i] == '-')
02996                         fval = -fval;
02997                 }
02998                 else if (*cp == '\0')
02999                     fval = 0;
03000                 else
03001                     return DTERR_BAD_FORMAT;
03002 
03003                 tmask = 0;      /* DTK_M(type); */
03004 
03005                 switch (type)
03006                 {
03007                     case DTK_MICROSEC:
03008 #ifdef HAVE_INT64_TIMESTAMP
03009                         *fsec += rint(val + fval);
03010 #else
03011                         *fsec += (val + fval) * 1e-6;
03012 #endif
03013                         tmask = DTK_M(MICROSECOND);
03014                         break;
03015 
03016                     case DTK_MILLISEC:
03017                         /* avoid overflowing the fsec field */
03018                         tm->tm_sec += val / 1000;
03019                         val -= (val / 1000) * 1000;
03020 #ifdef HAVE_INT64_TIMESTAMP
03021                         *fsec += rint((val + fval) * 1000);
03022 #else
03023                         *fsec += (val + fval) * 1e-3;
03024 #endif
03025                         tmask = DTK_M(MILLISECOND);
03026                         break;
03027 
03028                     case DTK_SECOND:
03029                         tm->tm_sec += val;
03030 #ifdef HAVE_INT64_TIMESTAMP
03031                         *fsec += rint(fval * 1000000);
03032 #else
03033                         *fsec += fval;
03034 #endif
03035 
03036                         /*
03037                          * If any subseconds were specified, consider this
03038                          * microsecond and millisecond input as well.
03039                          */
03040                         if (fval == 0)
03041                             tmask = DTK_M(SECOND);
03042                         else
03043                             tmask = DTK_ALL_SECS_M;
03044                         break;
03045 
03046                     case DTK_MINUTE:
03047                         tm->tm_min += val;
03048                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
03049                         tmask = DTK_M(MINUTE);
03050                         break;
03051 
03052                     case DTK_HOUR:
03053                         tm->tm_hour += val;
03054                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
03055                         tmask = DTK_M(HOUR);
03056                         type = DTK_DAY; /* set for next field */
03057                         break;
03058 
03059                     case DTK_DAY:
03060                         tm->tm_mday += val;
03061                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
03062                         tmask = DTK_M(DAY);
03063                         break;
03064 
03065                     case DTK_WEEK:
03066                         tm->tm_mday += val * 7;
03067                         AdjustFractDays(fval, tm, fsec, 7);
03068                         tmask = DTK_M(WEEK);
03069                         break;
03070 
03071                     case DTK_MONTH:
03072                         tm->tm_mon += val;
03073                         AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
03074                         tmask = DTK_M(MONTH);
03075                         break;
03076 
03077                     case DTK_YEAR:
03078                         tm->tm_year += val;
03079                         if (fval != 0)
03080                             tm->tm_mon += fval * MONTHS_PER_YEAR;
03081                         tmask = DTK_M(YEAR);
03082                         break;
03083 
03084                     case DTK_DECADE:
03085                         tm->tm_year += val * 10;
03086                         if (fval != 0)
03087                             tm->tm_mon += fval * MONTHS_PER_YEAR * 10;
03088                         tmask = DTK_M(DECADE);
03089                         break;
03090 
03091                     case DTK_CENTURY:
03092                         tm->tm_year += val * 100;
03093                         if (fval != 0)
03094                             tm->tm_mon += fval * MONTHS_PER_YEAR * 100;
03095                         tmask = DTK_M(CENTURY);
03096                         break;
03097 
03098                     case DTK_MILLENNIUM:
03099                         tm->tm_year += val * 1000;
03100                         if (fval != 0)
03101                             tm->tm_mon += fval * MONTHS_PER_YEAR * 1000;
03102                         tmask = DTK_M(MILLENNIUM);
03103                         break;
03104 
03105                     default:
03106                         return DTERR_BAD_FORMAT;
03107                 }
03108                 break;
03109 
03110             case DTK_STRING:
03111             case DTK_SPECIAL:
03112                 type = DecodeUnits(i, field[i], &val);
03113                 if (type == IGNORE_DTF)
03114                     continue;
03115 
03116                 tmask = 0;      /* DTK_M(type); */
03117                 switch (type)
03118                 {
03119                     case UNITS:
03120                         type = val;
03121                         break;
03122 
03123                     case AGO:
03124                         is_before = TRUE;
03125                         type = val;
03126                         break;
03127 
03128                     case RESERV:
03129                         tmask = (DTK_DATE_M | DTK_TIME_M);
03130                         *dtype = val;
03131                         break;
03132 
03133                     default:
03134                         return DTERR_BAD_FORMAT;
03135                 }
03136                 break;
03137 
03138             default:
03139                 return DTERR_BAD_FORMAT;
03140         }
03141 
03142         if (tmask & fmask)
03143             return DTERR_BAD_FORMAT;
03144         fmask |= tmask;
03145     }
03146 
03147     /* ensure that at least one time field has been found */
03148     if (fmask == 0)
03149         return DTERR_BAD_FORMAT;
03150 
03151     /* ensure fractional seconds are fractional */
03152     if (*fsec != 0)
03153     {
03154         int         sec;
03155 
03156 #ifdef HAVE_INT64_TIMESTAMP
03157         sec = *fsec / USECS_PER_SEC;
03158         *fsec -= sec * USECS_PER_SEC;
03159 #else
03160         TMODULO(*fsec, sec, 1.0);
03161 #endif
03162         tm->tm_sec += sec;
03163     }
03164 
03165     /*----------
03166      * The SQL standard defines the interval literal
03167      *   '-1 1:00:00'
03168      * to mean "negative 1 days and negative 1 hours", while Postgres
03169      * traditionally treats this as meaning "negative 1 days and positive
03170      * 1 hours".  In SQL_STANDARD intervalstyle, we apply the leading sign
03171      * to all fields if there are no other explicit signs.
03172      *
03173      * We leave the signs alone if there are additional explicit signs.
03174      * This protects us against misinterpreting postgres-style dump output,
03175      * since the postgres-style output code has always put an explicit sign on
03176      * all fields following a negative field.  But note that SQL-spec output
03177      * is ambiguous and can be misinterpreted on load!  (So it's best practice
03178      * to dump in postgres style, not SQL style.)
03179      *----------
03180      */
03181     if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
03182     {
03183         /* Check for additional explicit signs */
03184         bool        more_signs = false;
03185 
03186         for (i = 1; i < nf; i++)
03187         {
03188             if (*field[i] == '-' || *field[i] == '+')
03189             {
03190                 more_signs = true;
03191                 break;
03192             }
03193         }
03194 
03195         if (!more_signs)
03196         {
03197             /*
03198              * Rather than re-determining which field was field[0], just force
03199              * 'em all negative.
03200              */
03201             if (*fsec > 0)
03202                 *fsec = -(*fsec);
03203             if (tm->tm_sec > 0)
03204                 tm->tm_sec = -tm->tm_sec;
03205             if (tm->tm_min > 0)
03206                 tm->tm_min = -tm->tm_min;
03207             if (tm->tm_hour > 0)
03208                 tm->tm_hour = -tm->tm_hour;
03209             if (tm->tm_mday > 0)
03210                 tm->tm_mday = -tm->tm_mday;
03211             if (tm->tm_mon > 0)
03212                 tm->tm_mon = -tm->tm_mon;
03213             if (tm->tm_year > 0)
03214                 tm->tm_year = -tm->tm_year;
03215         }
03216     }
03217 
03218     /* finally, AGO negates everything */
03219     if (is_before)
03220     {
03221         *fsec = -(*fsec);
03222         tm->tm_sec = -tm->tm_sec;
03223         tm->tm_min = -tm->tm_min;
03224         tm->tm_hour = -tm->tm_hour;
03225         tm->tm_mday = -tm->tm_mday;
03226         tm->tm_mon = -tm->tm_mon;
03227         tm->tm_year = -tm->tm_year;
03228     }
03229 
03230     return 0;
03231 }
03232 
03233 
03234 /*
03235  * Helper functions to avoid duplicated code in DecodeISO8601Interval.
03236  *
03237  * Parse a decimal value and break it into integer and fractional parts.
03238  * Returns 0 or DTERR code.
03239  */
03240 static int
03241 ParseISO8601Number(char *str, char **endptr, int *ipart, double *fpart)
03242 {
03243     double      val;
03244 
03245     if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
03246         return DTERR_BAD_FORMAT;
03247     errno = 0;
03248     val = strtod(str, endptr);
03249     /* did we not see anything that looks like a double? */
03250     if (*endptr == str || errno != 0)
03251         return DTERR_BAD_FORMAT;
03252     /* watch out for overflow */
03253     if (val < INT_MIN || val > INT_MAX)
03254         return DTERR_FIELD_OVERFLOW;
03255     /* be very sure we truncate towards zero (cf dtrunc()) */
03256     if (val >= 0)
03257         *ipart = (int) floor(val);
03258     else
03259         *ipart = (int) -floor(-val);
03260     *fpart = val - *ipart;
03261     return 0;
03262 }
03263 
03264 /*
03265  * Determine number of integral digits in a valid ISO 8601 number field
03266  * (we should ignore sign and any fraction part)
03267  */
03268 static int
03269 ISO8601IntegerWidth(char *fieldstart)
03270 {
03271     /* We might have had a leading '-' */
03272     if (*fieldstart == '-')
03273         fieldstart++;
03274     return strspn(fieldstart, "0123456789");
03275 }
03276 
03277 
03278 /* DecodeISO8601Interval()
03279  *  Decode an ISO 8601 time interval of the "format with designators"
03280  *  (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
03281  *  Examples:  P1D  for 1 day
03282  *             PT1H for 1 hour
03283  *             P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
03284  *             P0002-06-07T01:30:00 the same value in alternative format
03285  *
03286  * Returns 0 if successful, DTERR code if bogus input detected.
03287  * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
03288  * ISO8601, otherwise this could cause unexpected error messages.
03289  * dtype, tm, fsec are output parameters.
03290  *
03291  *  A couple exceptions from the spec:
03292  *   - a week field ('W') may coexist with other units
03293  *   - allows decimals in fields other than the least significant unit.
03294  */
03295 int
03296 DecodeISO8601Interval(char *str,
03297                       int *dtype, struct pg_tm * tm, fsec_t *fsec)
03298 {
03299     bool        datepart = true;
03300     bool        havefield = false;
03301 
03302     *dtype = DTK_DELTA;
03303     ClearPgTm(tm, fsec);
03304 
03305     if (strlen(str) < 2 || str[0] != 'P')
03306         return DTERR_BAD_FORMAT;
03307 
03308     str++;
03309     while (*str)
03310     {
03311         char       *fieldstart;
03312         int         val;
03313         double      fval;
03314         char        unit;
03315         int         dterr;
03316 
03317         if (*str == 'T')        /* T indicates the beginning of the time part */
03318         {
03319             datepart = false;
03320             havefield = false;
03321             str++;
03322             continue;
03323         }
03324 
03325         fieldstart = str;
03326         dterr = ParseISO8601Number(str, &str, &val, &fval);
03327         if (dterr)
03328             return dterr;
03329 
03330         /*
03331          * Note: we could step off the end of the string here.  Code below
03332          * *must* exit the loop if unit == '\0'.
03333          */
03334         unit = *str++;
03335 
03336         if (datepart)
03337         {
03338             switch (unit)       /* before T: Y M W D */
03339             {
03340                 case 'Y':
03341                     tm->tm_year += val;
03342                     tm->tm_mon += (fval * MONTHS_PER_YEAR);
03343                     break;
03344                 case 'M':
03345                     tm->tm_mon += val;
03346                     AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
03347                     break;
03348                 case 'W':
03349                     tm->tm_mday += val * 7;
03350                     AdjustFractDays(fval, tm, fsec, 7);
03351                     break;
03352                 case 'D':
03353                     tm->tm_mday += val;
03354                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
03355                     break;
03356                 case 'T':       /* ISO 8601 4.4.3.3 Alternative Format / Basic */
03357                 case '\0':
03358                     if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
03359                     {
03360                         tm->tm_year += val / 10000;
03361                         tm->tm_mon += (val / 100) % 100;
03362                         tm->tm_mday += val % 100;
03363                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
03364                         if (unit == '\0')
03365                             return 0;
03366                         datepart = false;
03367                         havefield = false;
03368                         continue;
03369                     }
03370                     /* Else fall through to extended alternative format */
03371                 case '-':       /* ISO 8601 4.4.3.3 Alternative Format,
03372                                  * Extended */
03373                     if (havefield)
03374                         return DTERR_BAD_FORMAT;
03375 
03376                     tm->tm_year += val;
03377                     tm->tm_mon += (fval * MONTHS_PER_YEAR);
03378                     if (unit == '\0')
03379                         return 0;
03380                     if (unit == 'T')
03381                     {
03382                         datepart = false;
03383                         havefield = false;
03384                         continue;
03385                     }
03386 
03387                     dterr = ParseISO8601Number(str, &str, &val, &fval);
03388                     if (dterr)
03389                         return dterr;
03390                     tm->tm_mon += val;
03391                     AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
03392                     if (*str == '\0')
03393                         return 0;
03394                     if (*str == 'T')
03395                     {
03396                         datepart = false;
03397                         havefield = false;
03398                         continue;
03399                     }
03400                     if (*str != '-')
03401                         return DTERR_BAD_FORMAT;
03402                     str++;
03403 
03404                     dterr = ParseISO8601Number(str, &str, &val, &fval);
03405                     if (dterr)
03406                         return dterr;
03407                     tm->tm_mday += val;
03408                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
03409                     if (*str == '\0')
03410                         return 0;
03411                     if (*str == 'T')
03412                     {
03413                         datepart = false;
03414                         havefield = false;
03415                         continue;
03416                     }
03417                     return DTERR_BAD_FORMAT;
03418                 default:
03419                     /* not a valid date unit suffix */
03420                     return DTERR_BAD_FORMAT;
03421             }
03422         }
03423         else
03424         {
03425             switch (unit)       /* after T: H M S */
03426             {
03427                 case 'H':
03428                     tm->tm_hour += val;
03429                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
03430                     break;
03431                 case 'M':
03432                     tm->tm_min += val;
03433                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
03434                     break;
03435                 case 'S':
03436                     tm->tm_sec += val;
03437                     AdjustFractSeconds(fval, tm, fsec, 1);
03438                     break;
03439                 case '\0':      /* ISO 8601 4.4.3.3 Alternative Format */
03440                     if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
03441                     {
03442                         tm->tm_hour += val / 10000;
03443                         tm->tm_min += (val / 100) % 100;
03444                         tm->tm_sec += val % 100;
03445                         AdjustFractSeconds(fval, tm, fsec, 1);
03446                         return 0;
03447                     }
03448                     /* Else fall through to extended alternative format */
03449                 case ':':       /* ISO 8601 4.4.3.3 Alternative Format,
03450                                  * Extended */
03451                     if (havefield)
03452                         return DTERR_BAD_FORMAT;
03453 
03454                     tm->tm_hour += val;
03455                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
03456                     if (unit == '\0')
03457                         return 0;
03458 
03459                     dterr = ParseISO8601Number(str, &str, &val, &fval);
03460                     if (dterr)
03461                         return dterr;
03462                     tm->tm_min += val;
03463                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
03464                     if (*str == '\0')
03465                         return 0;
03466                     if (*str != ':')
03467                         return DTERR_BAD_FORMAT;
03468                     str++;
03469 
03470                     dterr = ParseISO8601Number(str, &str, &val, &fval);
03471                     if (dterr)
03472                         return dterr;
03473                     tm->tm_sec += val;
03474                     AdjustFractSeconds(fval, tm, fsec, 1);
03475                     if (*str == '\0')
03476                         return 0;
03477                     return DTERR_BAD_FORMAT;
03478 
03479                 default:
03480                     /* not a valid time unit suffix */
03481                     return DTERR_BAD_FORMAT;
03482             }
03483         }
03484 
03485         havefield = true;
03486     }
03487 
03488     return 0;
03489 }
03490 
03491 
03492 /* DecodeUnits()
03493  * Decode text string using lookup table.
03494  * This routine supports time interval decoding
03495  * (hence, it need not recognize timezone names).
03496  */
03497 int
03498 DecodeUnits(int field, char *lowtoken, int *val)
03499 {
03500     int         type;
03501     const datetkn *tp;
03502 
03503     tp = deltacache[field];
03504     if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
03505     {
03506         tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
03507     }
03508     if (tp == NULL)
03509     {
03510         type = UNKNOWN_FIELD;
03511         *val = 0;
03512     }
03513     else
03514     {
03515         deltacache[field] = tp;
03516         type = tp->type;
03517         if (type == TZ || type == DTZ)
03518             *val = FROMVAL(tp);
03519         else
03520             *val = tp->value;
03521     }
03522 
03523     return type;
03524 }   /* DecodeUnits() */
03525 
03526 /*
03527  * Report an error detected by one of the datetime input processing routines.
03528  *
03529  * dterr is the error code, str is the original input string, datatype is
03530  * the name of the datatype we were trying to accept.
03531  *
03532  * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
03533  * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
03534  * separate SQLSTATE codes, so ...
03535  */
03536 void
03537 DateTimeParseError(int dterr, const char *str, const char *datatype)
03538 {
03539     switch (dterr)
03540     {
03541         case DTERR_FIELD_OVERFLOW:
03542             ereport(ERROR,
03543                     (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
03544                      errmsg("date/time field value out of range: \"%s\"",
03545                             str)));
03546             break;
03547         case DTERR_MD_FIELD_OVERFLOW:
03548             /* <nanny>same as above, but add hint about DateStyle</nanny> */
03549             ereport(ERROR,
03550                     (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
03551                      errmsg("date/time field value out of range: \"%s\"",
03552                             str),
03553             errhint("Perhaps you need a different \"datestyle\" setting.")));
03554             break;
03555         case DTERR_INTERVAL_OVERFLOW:
03556             ereport(ERROR,
03557                     (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
03558                      errmsg("interval field value out of range: \"%s\"",
03559                             str)));
03560             break;
03561         case DTERR_TZDISP_OVERFLOW:
03562             ereport(ERROR,
03563                     (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
03564                      errmsg("time zone displacement out of range: \"%s\"",
03565                             str)));
03566             break;
03567         case DTERR_BAD_FORMAT:
03568         default:
03569             ereport(ERROR,
03570                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
03571                      errmsg("invalid input syntax for type %s: \"%s\"",
03572                             datatype, str)));
03573             break;
03574     }
03575 }
03576 
03577 /* datebsearch()
03578  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
03579  * is WAY faster than the generic bsearch().
03580  */
03581 static const datetkn *
03582 datebsearch(const char *key, const datetkn *base, int nel)
03583 {
03584     if (nel > 0)
03585     {
03586         const datetkn *last = base + nel - 1,
03587                    *position;
03588         int         result;
03589 
03590         while (last >= base)
03591         {
03592             position = base + ((last - base) >> 1);
03593             result = key[0] - position->token[0];
03594             if (result == 0)
03595             {
03596                 result = strncmp(key, position->token, TOKMAXLEN);
03597                 if (result == 0)
03598                     return position;
03599             }
03600             if (result < 0)
03601                 last = position - 1;
03602             else
03603                 base = position + 1;
03604         }
03605     }
03606     return NULL;
03607 }
03608 
03609 /* EncodeTimezone()
03610  *      Append representation of a numeric timezone offset to str.
03611  */
03612 static void
03613 EncodeTimezone(char *str, int tz, int style)
03614 {
03615     int         hour,
03616                 min,
03617                 sec;
03618 
03619     sec = abs(tz);
03620     min = sec / SECS_PER_MINUTE;
03621     sec -= min * SECS_PER_MINUTE;
03622     hour = min / MINS_PER_HOUR;
03623     min -= hour * MINS_PER_HOUR;
03624 
03625     str += strlen(str);
03626     /* TZ is negated compared to sign we wish to display ... */
03627     *str++ = (tz <= 0 ? '+' : '-');
03628 
03629     if (sec != 0)
03630         sprintf(str, "%02d:%02d:%02d", hour, min, sec);
03631     else if (min != 0 || style == USE_XSD_DATES)
03632         sprintf(str, "%02d:%02d", hour, min);
03633     else
03634         sprintf(str, "%02d", hour);
03635 }
03636 
03637 /* EncodeDateOnly()
03638  * Encode date as local time.
03639  */
03640 void
03641 EncodeDateOnly(struct pg_tm * tm, int style, char *str)
03642 {
03643     Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
03644 
03645     switch (style)
03646     {
03647         case USE_ISO_DATES:
03648         case USE_XSD_DATES:
03649             /* compatible with ISO date formats */
03650             if (tm->tm_year > 0)
03651                 sprintf(str, "%04d-%02d-%02d",
03652                         tm->tm_year, tm->tm_mon, tm->tm_mday);
03653             else
03654                 sprintf(str, "%04d-%02d-%02d %s",
03655                         -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
03656             break;
03657 
03658         case USE_SQL_DATES:
03659             /* compatible with Oracle/Ingres date formats */
03660             if (DateOrder == DATEORDER_DMY)
03661                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
03662             else
03663                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
03664             if (tm->tm_year > 0)
03665                 sprintf(str + 5, "/%04d", tm->tm_year);
03666             else
03667                 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
03668             break;
03669 
03670         case USE_GERMAN_DATES:
03671             /* German-style date format */
03672             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
03673             if (tm->tm_year > 0)
03674                 sprintf(str + 5, ".%04d", tm->tm_year);
03675             else
03676                 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
03677             break;
03678 
03679         case USE_POSTGRES_DATES:
03680         default:
03681             /* traditional date-only style for Postgres */
03682             if (DateOrder == DATEORDER_DMY)
03683                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
03684             else
03685                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
03686             if (tm->tm_year > 0)
03687                 sprintf(str + 5, "-%04d", tm->tm_year);
03688             else
03689                 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
03690             break;
03691     }
03692 }
03693 
03694 
03695 /* EncodeTimeOnly()
03696  * Encode time fields only.
03697  *
03698  * tm and fsec are the value to encode, print_tz determines whether to include
03699  * a time zone (the difference between time and timetz types), tz is the
03700  * numeric time zone offset, style is the date style, str is where to write the
03701  * output.
03702  */
03703 void
03704 EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
03705 {
03706     sprintf(str, "%02d:%02d:", tm->tm_hour, tm->tm_min);
03707     str += strlen(str);
03708 
03709     AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
03710 
03711     if (print_tz)
03712         EncodeTimezone(str, tz, style);
03713 }
03714 
03715 
03716 /* EncodeDateTime()
03717  * Encode date and time interpreted as local time.
03718  *
03719  * tm and fsec are the value to encode, print_tz determines whether to include
03720  * a time zone (the difference between timestamp and timestamptz types), tz is
03721  * the numeric time zone offset, tzn is the textual time zone, which if
03722  * specified will be used instead of tz by some styles, style is the date
03723  * style, str is where to write the output.
03724  *
03725  * Supported date styles:
03726  *  Postgres - day mon hh:mm:ss yyyy tz
03727  *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
03728  *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
03729  *  German - dd.mm.yyyy hh:mm:ss tz
03730  *  XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
03731  */
03732 void
03733 EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
03734 {
03735     int         day;
03736 
03737     Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
03738 
03739     /*
03740      * Negative tm_isdst means we have no valid time zone translation.
03741      */
03742     if (tm->tm_isdst < 0)
03743         print_tz = false;
03744 
03745     switch (style)
03746     {
03747         case USE_ISO_DATES:
03748         case USE_XSD_DATES:
03749             /* Compatible with ISO-8601 date formats */
03750 
03751             if (style == USE_ISO_DATES)
03752                 sprintf(str, "%04d-%02d-%02d %02d:%02d:",
03753                         (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
03754                         tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
03755             else
03756                 sprintf(str, "%04d-%02d-%02dT%02d:%02d:",
03757                         (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
03758                         tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
03759 
03760             AppendTimestampSeconds(str + strlen(str), tm, fsec);
03761 
03762             if (print_tz)
03763                 EncodeTimezone(str, tz, style);
03764 
03765             if (tm->tm_year <= 0)
03766                 sprintf(str + strlen(str), " BC");
03767             break;
03768 
03769         case USE_SQL_DATES:
03770             /* Compatible with Oracle/Ingres date formats */
03771 
03772             if (DateOrder == DATEORDER_DMY)
03773                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
03774             else
03775                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
03776 
03777             sprintf(str + 5, "/%04d %02d:%02d:",
03778                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
03779                     tm->tm_hour, tm->tm_min);
03780 
03781             AppendTimestampSeconds(str + strlen(str), tm, fsec);
03782 
03783             /*
03784              * Note: the uses of %.*s in this function would be risky if the
03785              * timezone names ever contain non-ASCII characters.  However, all
03786              * TZ abbreviations in the Olson database are plain ASCII.
03787              */
03788 
03789             if (print_tz)
03790             {
03791                 if (tzn)
03792                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
03793                 else
03794                     EncodeTimezone(str, tz, style);
03795             }
03796 
03797             if (tm->tm_year <= 0)
03798                 sprintf(str + strlen(str), " BC");
03799             break;
03800 
03801         case USE_GERMAN_DATES:
03802             /* German variant on European style */
03803 
03804             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
03805 
03806             sprintf(str + 5, ".%04d %02d:%02d:",
03807                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
03808                     tm->tm_hour, tm->tm_min);
03809 
03810             AppendTimestampSeconds(str + strlen(str), tm, fsec);
03811 
03812             if (print_tz)
03813             {
03814                 if (tzn)
03815                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
03816                 else
03817                     EncodeTimezone(str, tz, style);
03818             }
03819 
03820             if (tm->tm_year <= 0)
03821                 sprintf(str + strlen(str), " BC");
03822             break;
03823 
03824         case USE_POSTGRES_DATES:
03825         default:
03826             /* Backward-compatible with traditional Postgres abstime dates */
03827 
03828             day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
03829             tm->tm_wday = j2day(day);
03830 
03831             strncpy(str, days[tm->tm_wday], 3);
03832             strcpy(str + 3, " ");
03833 
03834             if (DateOrder == DATEORDER_DMY)
03835                 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
03836             else
03837                 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
03838 
03839             sprintf(str + 10, " %02d:%02d:", tm->tm_hour, tm->tm_min);
03840 
03841             AppendTimestampSeconds(str + strlen(str), tm, fsec);
03842 
03843             sprintf(str + strlen(str), " %04d",
03844                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
03845 
03846             if (print_tz)
03847             {
03848                 if (tzn)
03849                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
03850                 else
03851                 {
03852                     /*
03853                      * We have a time zone, but no string version. Use the
03854                      * numeric form, but be sure to include a leading space to
03855                      * avoid formatting something which would be rejected by
03856                      * the date/time parser later. - thomas 2001-10-19
03857                      */
03858                     sprintf(str + strlen(str), " ");
03859                     EncodeTimezone(str, tz, style);
03860                 }
03861             }
03862 
03863             if (tm->tm_year <= 0)
03864                 sprintf(str + strlen(str), " BC");
03865             break;
03866     }
03867 }
03868 
03869 
03870 /*
03871  * Helper functions to avoid duplicated code in EncodeInterval.
03872  */
03873 
03874 /* Append an ISO-8601-style interval field, but only if value isn't zero */
03875 static char *
03876 AddISO8601IntPart(char *cp, int value, char units)
03877 {
03878     if (value == 0)
03879         return cp;
03880     sprintf(cp, "%d%c", value, units);
03881     return cp + strlen(cp);
03882 }
03883 
03884 /* Append a postgres-style interval field, but only if value isn't zero */
03885 static char *
03886 AddPostgresIntPart(char *cp, int value, const char *units,
03887                    bool *is_zero, bool *is_before)
03888 {
03889     if (value == 0)
03890         return cp;
03891     sprintf(cp, "%s%s%d %s%s",
03892             (!*is_zero) ? " " : "",
03893             (*is_before && value > 0) ? "+" : "",
03894             value,
03895             units,
03896             (value != 1) ? "s" : "");
03897 
03898     /*
03899      * Each nonzero field sets is_before for (only) the next one.  This is a
03900      * tad bizarre but it's how it worked before...
03901      */
03902     *is_before = (value < 0);
03903     *is_zero = FALSE;
03904     return cp + strlen(cp);
03905 }
03906 
03907 /* Append a verbose-style interval field, but only if value isn't zero */
03908 static char *
03909 AddVerboseIntPart(char *cp, int value, const char *units,
03910                   bool *is_zero, bool *is_before)
03911 {
03912     if (value == 0)
03913         return cp;
03914     /* first nonzero value sets is_before */
03915     if (*is_zero)
03916     {
03917         *is_before = (value < 0);
03918         value = abs(value);
03919     }
03920     else if (*is_before)
03921         value = -value;
03922     sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
03923     *is_zero = FALSE;
03924     return cp + strlen(cp);
03925 }
03926 
03927 
03928 /* EncodeInterval()
03929  * Interpret time structure as a delta time and convert to string.
03930  *
03931  * Support "traditional Postgres" and ISO-8601 styles.
03932  * Actually, afaik ISO does not address time interval formatting,
03933  *  but this looks similar to the spec for absolute date/time.
03934  * - thomas 1998-04-30
03935  *
03936  * Actually, afaik, ISO 8601 does specify formats for "time
03937  * intervals...[of the]...format with time-unit designators", which
03938  * are pretty ugly.  The format looks something like
03939  *     P1Y1M1DT1H1M1.12345S
03940  * but useful for exchanging data with computers instead of humans.
03941  * - ron 2003-07-14
03942  *
03943  * And ISO's SQL 2008 standard specifies standards for
03944  * "year-month literal"s (that look like '2-3') and
03945  * "day-time literal"s (that look like ('4 5:6:7')
03946  */
03947 void
03948 EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
03949 {
03950     char       *cp = str;
03951     int         year = tm->tm_year;
03952     int         mon = tm->tm_mon;
03953     int         mday = tm->tm_mday;
03954     int         hour = tm->tm_hour;
03955     int         min = tm->tm_min;
03956     int         sec = tm->tm_sec;
03957     bool        is_before = FALSE;
03958     bool        is_zero = TRUE;
03959 
03960     /*
03961      * The sign of year and month are guaranteed to match, since they are
03962      * stored internally as "month". But we'll need to check for is_before and
03963      * is_zero when determining the signs of day and hour/minute/seconds
03964      * fields.
03965      */
03966     switch (style)
03967     {
03968             /* SQL Standard interval format */
03969         case INTSTYLE_SQL_STANDARD:
03970             {
03971                 bool        has_negative = year < 0 || mon < 0 ||
03972                 mday < 0 || hour < 0 ||
03973                 min < 0 || sec < 0 || fsec < 0;
03974                 bool        has_positive = year > 0 || mon > 0 ||
03975                 mday > 0 || hour > 0 ||
03976                 min > 0 || sec > 0 || fsec > 0;
03977                 bool        has_year_month = year != 0 || mon != 0;
03978                 bool        has_day_time = mday != 0 || hour != 0 ||
03979                 min != 0 || sec != 0 || fsec != 0;
03980                 bool        has_day = mday != 0;
03981                 bool        sql_standard_value = !(has_negative && has_positive) &&
03982                 !(has_year_month && has_day_time);
03983 
03984                 /*
03985                  * SQL Standard wants only 1 "<sign>" preceding the whole
03986                  * interval ... but can't do that if mixed signs.
03987                  */
03988                 if (has_negative && sql_standard_value)
03989                 {
03990                     *cp++ = '-';
03991                     year = -year;
03992                     mon = -mon;
03993                     mday = -mday;
03994                     hour = -hour;
03995                     min = -min;
03996                     sec = -sec;
03997                     fsec = -fsec;
03998                 }
03999 
04000                 if (!has_negative && !has_positive)
04001                 {
04002                     sprintf(cp, "0");
04003                 }
04004                 else if (!sql_standard_value)
04005                 {
04006                     /*
04007                      * For non sql-standard interval values, force outputting
04008                      * the signs to avoid ambiguities with intervals with
04009                      * mixed sign components.
04010                      */
04011                     char        year_sign = (year < 0 || mon < 0) ? '-' : '+';
04012                     char        day_sign = (mday < 0) ? '-' : '+';
04013                     char        sec_sign = (hour < 0 || min < 0 ||
04014                                             sec < 0 || fsec < 0) ? '-' : '+';
04015 
04016                     sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
04017                             year_sign, abs(year), abs(mon),
04018                             day_sign, abs(mday),
04019                             sec_sign, abs(hour), abs(min));
04020                     cp += strlen(cp);
04021                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
04022                 }
04023                 else if (has_year_month)
04024                 {
04025                     sprintf(cp, "%d-%d", year, mon);
04026                 }
04027                 else if (has_day)
04028                 {
04029                     sprintf(cp, "%d %d:%02d:", mday, hour, min);
04030                     cp += strlen(cp);
04031                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
04032                 }
04033                 else
04034                 {
04035                     sprintf(cp, "%d:%02d:", hour, min);
04036                     cp += strlen(cp);
04037                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
04038                 }
04039             }
04040             break;
04041 
04042             /* ISO 8601 "time-intervals by duration only" */
04043         case INTSTYLE_ISO_8601:
04044             /* special-case zero to avoid printing nothing */
04045             if (year == 0 && mon == 0 && mday == 0 &&
04046                 hour == 0 && min == 0 && sec == 0 && fsec == 0)
04047             {
04048                 sprintf(cp, "PT0S");
04049                 break;
04050             }
04051             *cp++ = 'P';
04052             cp = AddISO8601IntPart(cp, year, 'Y');
04053             cp = AddISO8601IntPart(cp, mon, 'M');
04054             cp = AddISO8601IntPart(cp, mday, 'D');
04055             if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
04056                 *cp++ = 'T';
04057             cp = AddISO8601IntPart(cp, hour, 'H');
04058             cp = AddISO8601IntPart(cp, min, 'M');
04059             if (sec != 0 || fsec != 0)
04060             {
04061                 if (sec < 0 || fsec < 0)
04062                     *cp++ = '-';
04063                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
04064                 cp += strlen(cp);
04065                 *cp++ = 'S';
04066                 *cp++ = '\0';
04067             }
04068             break;
04069 
04070             /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
04071         case INTSTYLE_POSTGRES:
04072             cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
04073 
04074             /*
04075              * Ideally we should spell out "month" like we do for "year" and
04076              * "day".  However, for backward compatibility, we can't easily
04077              * fix this.  bjm 2011-05-24
04078              */
04079             cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
04080             cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
04081             if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
04082             {
04083                 bool        minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
04084 
04085                 sprintf(cp, "%s%s%02d:%02d:",
04086                         is_zero ? "" : " ",
04087                         (minus ? "-" : (is_before ? "+" : "")),
04088                         abs(hour), abs(min));
04089                 cp += strlen(cp);
04090                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
04091             }
04092             break;
04093 
04094             /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
04095         case INTSTYLE_POSTGRES_VERBOSE:
04096         default:
04097             strcpy(cp, "@");
04098             cp++;
04099             cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
04100             cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
04101             cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
04102             cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
04103             cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
04104             if (sec != 0 || fsec != 0)
04105             {
04106                 *cp++ = ' ';
04107                 if (sec < 0 || (sec == 0 && fsec < 0))
04108                 {
04109                     if (is_zero)
04110                         is_before = TRUE;
04111                     else if (!is_before)
04112                         *cp++ = '-';
04113                 }
04114                 else if (is_before)
04115                     *cp++ = '-';
04116                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
04117                 cp += strlen(cp);
04118                 sprintf(cp, " sec%s",
04119                         (abs(sec) != 1 || fsec != 0) ? "s" : "");
04120                 is_zero = FALSE;
04121             }
04122             /* identically zero? then put in a unitless zero... */
04123             if (is_zero)
04124                 strcat(cp, " 0");
04125             if (is_before)
04126                 strcat(cp, " ago");
04127             break;
04128     }
04129 }
04130 
04131 
04132 /*
04133  * We've been burnt by stupid errors in the ordering of the datetkn tables
04134  * once too often.  Arrange to check them during postmaster start.
04135  */
04136 static bool
04137 CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
04138 {
04139     bool        ok = true;
04140     int         i;
04141 
04142     for (i = 1; i < nel; i++)
04143     {
04144         if (strncmp(base[i - 1].token, base[i].token, TOKMAXLEN) >= 0)
04145         {
04146             /* %.*s is safe since all our tokens are ASCII */
04147             elog(LOG, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
04148                  tablename,
04149                  TOKMAXLEN, base[i - 1].token,
04150                  TOKMAXLEN, base[i].token);
04151             ok = false;
04152         }
04153     }
04154     return ok;
04155 }
04156 
04157 bool
04158 CheckDateTokenTables(void)
04159 {
04160     bool        ok = true;
04161 
04162     Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
04163     Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
04164 
04165     ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
04166     ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
04167     return ok;
04168 }
04169 
04170 /*
04171  * Common code for temporal protransform functions.  Types time, timetz,
04172  * timestamp and timestamptz each have a range of allowed precisions.  An
04173  * unspecified precision is rigorously equivalent to the highest specifiable
04174  * precision.
04175  *
04176  * Note: timestamp_scale throws an error when the typmod is out of range, but
04177  * we can't get there from a cast: our typmodin will have caught it already.
04178  */
04179 Node *
04180 TemporalTransform(int32 max_precis, Node *node)
04181 {
04182     FuncExpr   *expr = (FuncExpr *) node;
04183     Node       *ret = NULL;
04184     Node       *typmod;
04185 
04186     Assert(IsA(expr, FuncExpr));
04187     Assert(list_length(expr->args) >= 2);
04188 
04189     typmod = (Node *) lsecond(expr->args);
04190 
04191     if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
04192     {
04193         Node       *source = (Node *) linitial(expr->args);
04194         int32       old_precis = exprTypmod(source);
04195         int32       new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
04196 
04197         if (new_precis < 0 || new_precis == max_precis ||
04198             (old_precis >= 0 && new_precis >= old_precis))
04199             ret = relabel_to_typmod(source, new_precis);
04200     }
04201 
04202     return ret;
04203 }
04204 
04205 /*
04206  * This function gets called during timezone config file load or reload
04207  * to create the final array of timezone tokens.  The argument array
04208  * is already sorted in name order.  The data is converted to datetkn
04209  * format and installed in *tbl, which must be allocated by the caller.
04210  */
04211 void
04212 ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
04213                        struct tzEntry *abbrevs, int n)
04214 {
04215     datetkn    *newtbl = tbl->abbrevs;
04216     int         i;
04217 
04218     tbl->numabbrevs = n;
04219     for (i = 0; i < n; i++)
04220     {
04221         strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN);
04222         newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ;
04223         TOVAL(&newtbl[i], abbrevs[i].offset / MINS_PER_HOUR);
04224     }
04225 
04226     /* Check the ordering, if testing */
04227     Assert(CheckDateTokenTable("timezone offset", newtbl, n));
04228 }
04229 
04230 /*
04231  * Install a TimeZoneAbbrevTable as the active table.
04232  *
04233  * Caller is responsible that the passed table doesn't go away while in use.
04234  */
04235 void
04236 InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
04237 {
04238     int         i;
04239 
04240     timezonetktbl = tbl->abbrevs;
04241     sztimezonetktbl = tbl->numabbrevs;
04242 
04243     /* clear date cache in case it contains any stale timezone names */
04244     for (i = 0; i < MAXDATEFIELDS; i++)
04245         datecache[i] = NULL;
04246 }
04247 
04248 /*
04249  * This set-returning function reads all the available time zone abbreviations
04250  * and returns a set of (abbrev, utc_offset, is_dst).
04251  */
04252 Datum
04253 pg_timezone_abbrevs(PG_FUNCTION_ARGS)
04254 {
04255     FuncCallContext *funcctx;
04256     int        *pindex;
04257     Datum       result;
04258     HeapTuple   tuple;
04259     Datum       values[3];
04260     bool        nulls[3];
04261     char        buffer[TOKMAXLEN + 1];
04262     unsigned char *p;
04263     struct pg_tm tm;
04264     Interval   *resInterval;
04265 
04266     /* stuff done only on the first call of the function */
04267     if (SRF_IS_FIRSTCALL())
04268     {
04269         TupleDesc   tupdesc;
04270         MemoryContext oldcontext;
04271 
04272         /* create a function context for cross-call persistence */
04273         funcctx = SRF_FIRSTCALL_INIT();
04274 
04275         /*
04276          * switch to memory context appropriate for multiple function calls
04277          */
04278         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
04279 
04280         /* allocate memory for user context */
04281         pindex = (int *) palloc(sizeof(int));
04282         *pindex = 0;
04283         funcctx->user_fctx = (void *) pindex;
04284 
04285         /*
04286          * build tupdesc for result tuples. This must match this function's
04287          * pg_proc entry!
04288          */
04289         tupdesc = CreateTemplateTupleDesc(3, false);
04290         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev",
04291                            TEXTOID, -1, 0);
04292         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset",
04293                            INTERVALOID, -1, 0);
04294         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_dst",
04295                            BOOLOID, -1, 0);
04296 
04297         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
04298         MemoryContextSwitchTo(oldcontext);
04299     }
04300 
04301     /* stuff done on every call of the function */
04302     funcctx = SRF_PERCALL_SETUP();
04303     pindex = (int *) funcctx->user_fctx;
04304 
04305     if (*pindex >= sztimezonetktbl)
04306         SRF_RETURN_DONE(funcctx);
04307 
04308     MemSet(nulls, 0, sizeof(nulls));
04309 
04310     /*
04311      * Convert name to text, using upcasing conversion that is the inverse of
04312      * what ParseDateTime() uses.
04313      */
04314     strncpy(buffer, timezonetktbl[*pindex].token, TOKMAXLEN);
04315     buffer[TOKMAXLEN] = '\0';   /* may not be null-terminated */
04316     for (p = (unsigned char *) buffer; *p; p++)
04317         *p = pg_toupper(*p);
04318 
04319     values[0] = CStringGetTextDatum(buffer);
04320 
04321     MemSet(&tm, 0, sizeof(struct pg_tm));
04322     tm.tm_min = (-1) * FROMVAL(&timezonetktbl[*pindex]);
04323     resInterval = (Interval *) palloc(sizeof(Interval));
04324     tm2interval(&tm, 0, resInterval);
04325     values[1] = IntervalPGetDatum(resInterval);
04326 
04327     Assert(timezonetktbl[*pindex].type == DTZ ||
04328            timezonetktbl[*pindex].type == TZ);
04329     values[2] = BoolGetDatum(timezonetktbl[*pindex].type == DTZ);
04330 
04331     (*pindex)++;
04332 
04333     tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
04334     result = HeapTupleGetDatum(tuple);
04335 
04336     SRF_RETURN_NEXT(funcctx, result);
04337 }
04338 
04339 /*
04340  * This set-returning function reads all the available full time zones
04341  * and returns a set of (name, abbrev, utc_offset, is_dst).
04342  */
04343 Datum
04344 pg_timezone_names(PG_FUNCTION_ARGS)
04345 {
04346     MemoryContext oldcontext;
04347     FuncCallContext *funcctx;
04348     pg_tzenum  *tzenum;
04349     pg_tz      *tz;
04350     Datum       result;
04351     HeapTuple   tuple;
04352     Datum       values[4];
04353     bool        nulls[4];
04354     int         tzoff;
04355     struct pg_tm tm;
04356     fsec_t      fsec;
04357     const char *tzn;
04358     Interval   *resInterval;
04359     struct pg_tm itm;
04360 
04361     /* stuff done only on the first call of the function */
04362     if (SRF_IS_FIRSTCALL())
04363     {
04364         TupleDesc   tupdesc;
04365 
04366         /* create a function context for cross-call persistence */
04367         funcctx = SRF_FIRSTCALL_INIT();
04368 
04369         /*
04370          * switch to memory context appropriate for multiple function calls
04371          */
04372         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
04373 
04374         /* initialize timezone scanning code */
04375         tzenum = pg_tzenumerate_start();
04376         funcctx->user_fctx = (void *) tzenum;
04377 
04378         /*
04379          * build tupdesc for result tuples. This must match this function's
04380          * pg_proc entry!
04381          */
04382         tupdesc = CreateTemplateTupleDesc(4, false);
04383         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
04384                            TEXTOID, -1, 0);
04385         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev",
04386                            TEXTOID, -1, 0);
04387         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "utc_offset",
04388                            INTERVALOID, -1, 0);
04389         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_dst",
04390                            BOOLOID, -1, 0);
04391 
04392         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
04393         MemoryContextSwitchTo(oldcontext);
04394     }
04395 
04396     /* stuff done on every call of the function */
04397     funcctx = SRF_PERCALL_SETUP();
04398     tzenum = (pg_tzenum *) funcctx->user_fctx;
04399 
04400     /* search for another zone to display */
04401     for (;;)
04402     {
04403         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
04404         tz = pg_tzenumerate_next(tzenum);
04405         MemoryContextSwitchTo(oldcontext);
04406 
04407         if (!tz)
04408         {
04409             pg_tzenumerate_end(tzenum);
04410             funcctx->user_fctx = NULL;
04411             SRF_RETURN_DONE(funcctx);
04412         }
04413 
04414         /* Convert now() to local time in this zone */
04415         if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
04416                          &tzoff, &tm, &fsec, &tzn, tz) != 0)
04417             continue;           /* ignore if conversion fails */
04418 
04419         /* Ignore zic's rather silly "Factory" time zone */
04420         if (tzn && strcmp(tzn, "Local time zone must be set--see zic manual page") == 0)
04421             continue;
04422 
04423         /* Found a displayable zone */
04424         break;
04425     }
04426 
04427     MemSet(nulls, 0, sizeof(nulls));
04428 
04429     values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
04430     values[1] = CStringGetTextDatum(tzn ? tzn : "");
04431 
04432     MemSet(&itm, 0, sizeof(struct pg_tm));
04433     itm.tm_sec = -tzoff;
04434     resInterval = (Interval *) palloc(sizeof(Interval));
04435     tm2interval(&itm, 0, resInterval);
04436     values[2] = IntervalPGetDatum(resInterval);
04437 
04438     values[3] = BoolGetDatum(tm.tm_isdst > 0);
04439 
04440     tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
04441     result = HeapTupleGetDatum(tuple);
04442 
04443     SRF_RETURN_NEXT(funcctx, result);
04444 }