* datetime.c
* Support functions for date/time types.
*
- * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include "utils/tzparser.h"
-static int DecodeNumber(int flen, char *field, bool haveTextMonth,
- int fmask, int *tmask,
- struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
-static int DecodeNumberField(int len, char *str,
- int fmask, int *tmask,
- struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
-static int DecodeTime(char *str, int fmask, int range,
- int *tmask, struct pg_tm *tm, fsec_t *fsec);
+static int DecodeNumber(int flen, char *field, bool haveTextMonth,
+ int fmask, int *tmask,
+ struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
+static int DecodeNumberField(int len, char *str,
+ int fmask, int *tmask,
+ struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
+static int DecodeTime(char *str, int fmask, int range,
+ int *tmask, struct pg_tm *tm, fsec_t *fsec);
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
-static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
- struct pg_tm *tm);
+static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
+ struct pg_tm *tm);
static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
- int precision, bool fillzeros);
+ int precision, bool fillzeros);
static void AdjustFractSeconds(double frac, struct pg_tm *tm, fsec_t *fsec,
- int scale);
+ int scale);
static void AdjustFractDays(double frac, struct pg_tm *tm, fsec_t *fsec,
- int scale);
-static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
- pg_time_t *tp);
+ int scale);
+static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
+ pg_time_t *tp);
static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t,
- const char *abbr, pg_tz *tzp,
- int *offset, int *isdst);
+ const char *abbr, pg_tz *tzp,
+ int *offset, int *isdst);
static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp);
{"aug", MONTH, 8},
{"august", MONTH, 8},
{DB_C, ADBC, BC}, /* "bc" for years <= 0 */
- {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
{"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
{"dec", MONTH, 12},
{"december", MONTH, 12},
{"friday", DOW, 5},
{"h", UNITS, DTK_HOUR}, /* "hour" */
{LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
- {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
{"isodow", UNITS, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */
{"isoyear", UNITS, DTK_ISOYEAR}, /* year in terms of the ISO week date */
{"j", UNITS, DTK_JULIAN},
{"tue", DOW, 2},
{"tues", DOW, 2},
{"tuesday", DOW, 2},
- {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
{"wed", DOW, 3},
{"wednesday", DOW, 3},
{"weds", DOW, 3},
{YESTERDAY, RESERV, DTK_YESTERDAY} /* yesterday midnight */
};
-static int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
+static const int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
/*
* deltatktbl: same format as datetktbl, but holds keywords used to represent
{"hours", UNITS, DTK_HOUR}, /* "hours" relative */
{"hr", UNITS, DTK_HOUR}, /* "hour" relative */
{"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
- {INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */
{"m", UNITS, DTK_MINUTE}, /* "minute" relative */
{"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
{DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
{"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
{"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
- {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
{"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{"yrs", UNITS, DTK_YEAR} /* "years" relative */
};
-static int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
+static const int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
static TimeZoneAbbrevTable *zoneabbrevtbl = NULL;
* Is this a YMD or HMS specification, or a year number?
* YMD and HMS are required to be six digits or more, so
* if it is 5 digits, it is a year. If it is six or more
- * more digits, we assume it is YMD or HMS unless no date
- * and no time values have been specified. This forces 6+
+ * digits, we assume it is YMD or HMS unless no date and
+ * no time values have been specified. This forces 6+
* digit years to be at the end of the string, or to use
* the ISO date specification.
*/
case RESERV:
switch (val)
{
- case DTK_CURRENT:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("date/time value \"current\" is no longer supported")));
-
- return DTERR_BAD_FORMAT;
- break;
-
case DTK_NOW:
tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
*dtype = DTK_DATE;
/*
* Was this an "ISO time" with embedded field labels? An
- * example is "h04m05s06" - thomas 2001-02-04
+ * example is "h04mm05s06" - thomas 2001-02-04
*/
if (ptype != 0)
{
case RESERV:
switch (val)
{
- case DTK_CURRENT:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("date/time value \"current\" is no longer supported")));
- return DTERR_BAD_FORMAT;
- break;
-
case DTK_NOW:
tmask = DTK_TIME_M;
*dtype = DTK_TIME;
GetCurrentDateTime(tmp);
else
{
+ /* a date has to be specified */
+ if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+ return DTERR_BAD_FORMAT;
tmp->tm_year = tm->tm_year;
tmp->tm_mon = tm->tm_mon;
tmp->tm_mday = tm->tm_mday;
GetCurrentDateTime(tmp);
else
{
+ /* a date has to be specified */
+ if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+ return DTERR_BAD_FORMAT;
tmp->tm_year = tm->tm_year;
tmp->tm_mon = tm->tm_mon;
tmp->tm_mday = tm->tm_mday;
}
-/* ClearPgTM
+/* ClearPgTm
*
* Zero out a pg_tm and associated fsec_t
*/
}
/*
- * Common code for temporal protransform functions. Types time, timetz,
- * timestamp and timestamptz each have a range of allowed precisions. An
- * unspecified precision is rigorously equivalent to the highest specifiable
- * precision.
+ * Common code for temporal prosupport functions: simplify, if possible,
+ * a call to a temporal type's length-coercion function.
+ *
+ * Types time, timetz, timestamp and timestamptz each have a range of allowed
+ * precisions. An unspecified precision is rigorously equivalent to the
+ * highest specifiable precision. We can replace the function call with a
+ * no-op RelabelType if it is coercing to the same or higher precision as the
+ * input is known to have.
+ *
+ * The input Node is always a FuncExpr, but to reduce the #include footprint
+ * of datetime.h, we declare it as Node *.
*
* Note: timestamp_scale throws an error when the typmod is out of range, but
* we can't get there from a cast: our typmodin will have caught it already.
*/
Node *
-TemporalTransform(int32 max_precis, Node *node)
+TemporalSimplify(int32 max_precis, Node *node)
{
FuncExpr *expr = castNode(FuncExpr, node);
Node *ret = NULL;
* build tupdesc for result tuples. This must match this function's
* pg_proc entry!
*/
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset",
* build tupdesc for result tuples. This must match this function's
* pg_proc entry!
*/
- tupdesc = CreateTemplateTupleDesc(4, false);
+ tupdesc = CreateTemplateTupleDesc(4);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev",
continue; /* ignore if conversion fails */
/*
- * Ignore zic's rather silly "Factory" time zone. The long string
- * about "see zic manual page" is used in tzdata versions before
- * 2016g; we can drop it someday when we're pretty sure no such data
- * exists in the wild on platforms using --with-system-tzdata. In
- * 2016g and later, the time zone abbreviation "-00" is used for
- * "Factory" as well as some invalid cases, all of which we can
- * reasonably omit from the pg_timezone_names view.
+ * IANA's rather silly "Factory" time zone used to emit ridiculously
+ * long "abbreviations" such as "Local time zone must be set--see zic
+ * manual page" or "Local time zone must be set--use tzsetup". While
+ * modern versions of tzdb emit the much saner "-00", it seems some
+ * benighted packagers are hacking the IANA data so that it continues
+ * to produce these strings. To prevent producing a weirdly wide
+ * abbrev column, reject ridiculously long abbreviations.
*/
- if (tzn && (strcmp(tzn, "-00") == 0 ||
- strcmp(tzn, "Local time zone must be set--see zic manual page") == 0))
+ if (tzn && strlen(tzn) > 31)
continue;
/* Found a displayable zone */