* datetime.c
* Support functions for date/time types.
*
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include "postgres.h"
#include <ctype.h>
-#include <float.h>
#include <limits.h>
#include <math.h>
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
+#include "common/string.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#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);
-
-#ifndef HAVE_INT64_TIMESTAMP
-static char *TrimTrailingZeros(char *str);
-#endif /* HAVE_INT64_TIMESTAMP */
-
+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);
-static void AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec,
- 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 precision, bool fillzeros);
+static void AdjustFractSeconds(double frac, struct pg_tm *tm, fsec_t *fsec,
+ 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);
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);
/* token, type, value */
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
{DA_D, ADBC, AD}, /* "ad" for years > 0 */
- {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
+ {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
{"am", AMPM, AM},
{"apr", MONTH, 4},
{"april", MONTH, 4},
{"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 */
+ {"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},
{"jan", MONTH, 1},
{"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
{"@", IGNORE_DTF, 0}, /* postgres relative prefix */
{DAGO, AGO, 0}, /* "ago" indicates negative time offset */
{"c", UNITS, DTK_CENTURY}, /* "century" relative */
- {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
+ {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
{"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
- {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
+ {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
{"d", UNITS, DTK_DAY}, /* "day" relative */
{DDAY, UNITS, DTK_DAY}, /* "day" relative */
{"days", UNITS, DTK_DAY}, /* "days" relative */
{"dec", UNITS, DTK_DECADE}, /* "decade" relative */
- {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
- {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
+ {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
+ {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
{"decs", UNITS, DTK_DECADE}, /* "decades" relative */
{"h", UNITS, DTK_HOUR}, /* "hour" relative */
{DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
{"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 */
- {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
- {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
- {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
+ {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
+ {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
+ {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
+ {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
+ {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
{"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
{"min", UNITS, DTK_MINUTE}, /* "minute" relative */
{"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
- {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
- {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
+ {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
+ {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
{"mon", UNITS, DTK_MONTH}, /* "months" relative */
{"mons", UNITS, DTK_MONTH}, /* "months" relative */
{DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
{"mseconds", UNITS, DTK_MILLISEC},
{"msecs", UNITS, DTK_MILLISEC},
{"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
- {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
+ {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
{"s", UNITS, DTK_SECOND},
{"sec", UNITS, DTK_SECOND},
{DSECOND, UNITS, DTK_SECOND},
{"secs", UNITS, DTK_SECOND},
{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 */
+ {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
{"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
- {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
+ {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
{"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
- {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
+ {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
{"w", UNITS, DTK_WEEK}, /* "week" relative */
{DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
{"weeks", UNITS, DTK_WEEK}, /* "weeks" 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;
static const datetkn *abbrevcache[MAXDATEFIELDS] = {NULL};
-/*
- * strtoint --- just like strtol, but returns int not long
- */
-static int
-strtoint(const char *nptr, char **endptr, int base)
-{
- long val;
-
- val = strtol(nptr, endptr, base);
-#ifdef HAVE_LONG_INT_64
- if (val != (long) ((int32) val))
- errno = ERANGE;
-#endif
- return (int) val;
-}
-
-
/*
* Calendar time to Julian date conversions.
* Julian date is commonly used in astronomical applications,
julian += 7834 * m / 256 + d;
return julian;
-} /* date2j() */
+} /* date2j() */
void
j2date(int jd, int *year, int *month, int *day)
*month = (quad + 10) % MONTHS_PER_YEAR + 1;
return;
-} /* j2date() */
+} /* j2date() */
/*
date += 7;
return date;
-} /* j2day() */
+} /* j2day() */
/*
* Get the transaction start time ("now()") broken down as a struct pg_tm.
*/
void
-GetCurrentDateTime(struct pg_tm * tm)
+GetCurrentDateTime(struct pg_tm *tm)
{
int tz;
fsec_t fsec;
* including fractional seconds and timezone offset.
*/
void
-GetCurrentTimeUsec(struct pg_tm * tm, fsec_t *fsec, int *tzp)
+GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
{
int tz;
}
-/* TrimTrailingZeros()
- * ... resulting from printing numbers with full precision.
- *
- * Returns a pointer to the new end of string. No NUL terminator is put
- * there; callers are responsible for NUL terminating str themselves.
- *
- * Before Postgres 8.4, this always left at least 2 fractional digits,
- * but conversations on the lists suggest this isn't desired
- * since showing '0.10' is misleading with values of precision(1).
- */
-#ifndef HAVE_INT64_TIMESTAMP
-static char *
-TrimTrailingZeros(char *str)
-{
- int len = strlen(str);
-
- while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.')
- len--;
- return str + len;
-}
-#endif /* HAVE_INT64_TIMESTAMP */
-
/*
* Append seconds and fractional seconds (if any) at *cp.
*
{
Assert(precision >= 0);
-#ifdef HAVE_INT64_TIMESTAMP
- /* fsec_t is just an int32 */
-
if (fillzeros)
cp = pg_ltostr_zeropad(cp, Abs(sec), 2);
else
cp = pg_ltostr(cp, Abs(sec));
+ /* fsec_t is just an int32 */
if (fsec != 0)
{
int32 value = Abs(fsec);
}
else
return cp;
-#else
- /* fsec_t is a double */
-
- if (fsec == 0)
- {
- if (fillzeros)
- return pg_ltostr_zeropad(cp, Abs(sec), 2);
- else
- return pg_ltostr(cp, Abs(sec));
- }
- else
- {
- if (fillzeros)
- sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
- else
- sprintf(cp, "%.*f", precision, fabs(sec + fsec));
- return TrimTrailingZeros(cp);
- }
-#endif /* HAVE_INT64_TIMESTAMP */
}
* there; callers are responsible for NUL terminating str themselves.
*/
static char *
-AppendTimestampSeconds(char *cp, struct pg_tm * tm, fsec_t fsec)
+AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
{
- /*
- * In float mode, don't print fractional seconds before 1 AD, since it's
- * unlikely there's any precision left ...
- */
-#ifndef HAVE_INT64_TIMESTAMP
- if (tm->tm_year <= 0)
- fsec = 0;
-#endif
return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
}
* We assume the input frac is less than 1 so overflow is not an issue.
*/
static void
-AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec, int scale)
+AdjustFractSeconds(double frac, struct pg_tm *tm, fsec_t *fsec, int scale)
{
int sec;
sec = (int) frac;
tm->tm_sec += sec;
frac -= sec;
-#ifdef HAVE_INT64_TIMESTAMP
*fsec += rint(frac * 1000000);
-#else
- *fsec += frac;
-#endif
}
/* As above, but initial scale produces days */
static void
-AdjustFractDays(double frac, struct pg_tm * tm, fsec_t *fsec, int scale)
+AdjustFractDays(double frac, struct pg_tm *tm, fsec_t *fsec, int scale)
{
int extra_days;
/* check for parse failure */
if (*cp != '\0' || errno != 0)
return DTERR_BAD_FORMAT;
-#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
-#else
- *fsec = frac;
-#endif
return 0;
}
*/
int
DecodeDateTime(char **field, int *ftype, int nf,
- int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
+ int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp)
{
int fmask = 0,
tmask,
int val;
int dterr;
int mer = HR24;
- bool haveTextMonth = FALSE;
- bool isjulian = FALSE;
- bool is2digits = FALSE;
- bool bc = FALSE;
+ bool haveTextMonth = false;
+ bool isjulian = false;
+ bool is2digits = false;
+ bool bc = false;
pg_tz *namedTz = NULL;
pg_tz *abbrevTz = NULL;
pg_tz *valtz;
return DTERR_FIELD_OVERFLOW;
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
- isjulian = TRUE;
+ isjulian = true;
/* Get the time zone from the end of the string */
dterr = DecodeTimezone(cp, tzp);
return DTERR_FIELD_OVERFLOW;
tmask = DTK_DATE_M;
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
- isjulian = TRUE;
+ isjulian = true;
/* fractional Julian Day? */
if (*cp == '.')
time = strtod(cp, &cp);
if (*cp != '\0' || errno != 0)
return DTERR_BAD_FORMAT;
-
-#ifdef HAVE_INT64_TIMESTAMP
time *= USECS_PER_DAY;
-#else
- time *= SECS_PER_DAY;
-#endif
dt2time(time,
&tm->tm_hour, &tm->tm_min,
&tm->tm_sec, fsec);
* 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;
*dtype = DTK_DATE;
GetCurrentDateTime(&cur_tm);
j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
- &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
break;
case DTK_TODAY:
*dtype = DTK_DATE;
GetCurrentDateTime(&cur_tm);
j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
- &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
break;
case DTK_ZULU:
tm->tm_mday = tm->tm_mon;
tmask = DTK_M(DAY);
}
- haveTextMonth = TRUE;
+ haveTextMonth = true;
tm->tm_mon = val;
break;
* though probably some higher-level code will.
*/
int
-DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
+DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
{
pg_time_t t;
* of mktime(), anyway.
*/
static int
-DetermineTimeZoneOffsetInternal(struct pg_tm * tm, pg_tz *tzp, pg_time_t *tp)
+DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, pg_time_t *tp)
{
int date,
sec;
* fall-back transition, prefer "after". (We used to define and implement
* this test as "prefer the standard-time interpretation", but that rule
* does not help to resolve the behavior when both times are reported as
- * standard time; which does happen, eg Europe/Moscow in Oct 2014.)
+ * standard time; which does happen, eg Europe/Moscow in Oct 2014. Also,
+ * in some zones such as Europe/Dublin, there is widespread confusion
+ * about which time offset is "standard" time, so it's fortunate that our
+ * behavior doesn't depend on that.)
*/
if (beforetime > aftertime)
{
* back to doing DetermineTimeZoneOffset().)
*/
int
-DetermineTimeZoneAbbrevOffset(struct pg_tm * tm, const char *abbr, pg_tz *tzp)
+DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp)
{
pg_time_t t;
int zone_offset;
*/
int
DecodeTimeOnly(char **field, int *ftype, int nf,
- int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
+ int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp)
{
int fmask = 0,
tmask,
int i;
int val;
int dterr;
- bool isjulian = FALSE;
- bool is2digits = FALSE;
- bool bc = FALSE;
+ bool isjulian = false;
+ bool is2digits = false;
+ bool bc = false;
int mer = HR24;
pg_tz *namedTz = NULL;
pg_tz *abbrevTz = NULL;
/*
* 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)
{
return DTERR_FIELD_OVERFLOW;
tmask = DTK_DATE_M;
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
- isjulian = TRUE;
+ isjulian = true;
if (*cp == '.')
{
time = strtod(cp, &cp);
if (*cp != '\0' || errno != 0)
return DTERR_BAD_FORMAT;
-
-#ifdef HAVE_INT64_TIMESTAMP
time *= USECS_PER_DAY;
-#else
- time *= SECS_PER_DAY;
-#endif
dt2time(time,
&tm->tm_hour, &tm->tm_min,
&tm->tm_sec, fsec);
else
{
dterr = DecodeNumber(flen, field[i],
- FALSE,
+ false,
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits);
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;
/* test for > 24:00:00 */
(tm->tm_hour == HOURS_PER_DAY &&
(tm->tm_min > 0 || tm->tm_sec > 0 || *fsec > 0)) ||
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec < INT64CONST(0) || *fsec > USECS_PER_SEC
-#else
- *fsec < 0 || *fsec > 1
-#endif
- )
+ *fsec < INT64CONST(0) || *fsec > USECS_PER_SEC)
return DTERR_FIELD_OVERFLOW;
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
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;
* str: field to be parsed
* fmask: bitmask for field types already seen
* *tmask: receives bitmask for fields found here
- * *is2digits: set to TRUE if we find 2-digit year
+ * *is2digits: set to true if we find 2-digit year
* *tm: field values are stored into appropriate members of this struct
*/
static int
DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
- struct pg_tm * tm)
+ struct pg_tm *tm)
{
fsec_t fsec;
int nf = 0;
int i,
len;
int dterr;
- bool haveTextMonth = FALSE;
+ bool haveTextMonth = false;
int type,
val,
dmask = 0;
{
case MONTH:
tm->tm_mon = val;
- haveTextMonth = TRUE;
+ haveTextMonth = true;
break;
default:
*/
int
ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
- struct pg_tm * tm)
+ struct pg_tm *tm)
{
if (fmask & DTK_M(YEAR))
{
*/
static int
DecodeTime(char *str, int fmask, int range,
- int *tmask, struct pg_tm * tm, fsec_t *fsec)
+ int *tmask, struct pg_tm *tm, fsec_t *fsec)
{
char *cp;
int dterr;
return DTERR_BAD_FORMAT;
/* do a sanity check */
-#ifdef HAVE_INT64_TIMESTAMP
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 ||
tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE ||
*fsec < INT64CONST(0) ||
*fsec > USECS_PER_SEC)
return DTERR_FIELD_OVERFLOW;
-#else
- if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 ||
- tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE ||
- *fsec < 0 || *fsec > 1)
- return DTERR_FIELD_OVERFLOW;
-#endif
return 0;
}
*/
static int
DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
- int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits)
+ int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
{
int val;
char *cp;
if (flen >= 3 && *is2digits)
{
/* Guess that first numeric field is day was wrong */
- *tmask = DTK_M(DAY); /* YEAR is already set */
+ *tmask = DTK_M(DAY); /* YEAR is already set */
tm->tm_mday = tm->tm_year;
tm->tm_year = val;
- *is2digits = FALSE;
+ *is2digits = false;
}
else
{
*/
static int
DecodeNumberField(int len, char *str, int fmask,
- int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits)
+ int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
{
char *cp;
frac = strtod(cp, NULL);
if (errno != 0)
return DTERR_BAD_FORMAT;
-#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
-#else
- *fsec = frac;
-#endif
/* Now truncate off the fraction for further processing */
*cp = '\0';
len = strlen(str);
*(str + (len - 4)) = '\0';
tm->tm_year = atoi(str);
if ((len - 4) == 2)
- *is2digits = TRUE;
+ *is2digits = true;
return DTK_DATE;
}
}
-/* ClearPgTM
+/* ClearPgTm
*
* Zero out a pg_tm and associated fsec_t
*/
static inline void
-ClearPgTm(struct pg_tm * tm, fsec_t *fsec)
+ClearPgTm(struct pg_tm *tm, fsec_t *fsec)
{
tm->tm_year = 0;
tm->tm_mon = 0;
*/
int
DecodeInterval(char **field, int *ftype, int nf, int range,
- int *dtype, struct pg_tm * tm, fsec_t *fsec)
+ int *dtype, struct pg_tm *tm, fsec_t *fsec)
{
- bool is_before = FALSE;
+ bool is_before = false;
char *cp;
int fmask = 0,
tmask,
* handle signed float numbers and signed year-month values.
*/
- /* FALL THROUGH */
+ /* FALLTHROUGH */
case DTK_DATE:
case DTK_NUMBER:
switch (type)
{
case DTK_MICROSEC:
-#ifdef HAVE_INT64_TIMESTAMP
*fsec += rint(val + fval);
-#else
- *fsec += (val + fval) * 1e-6;
-#endif
tmask = DTK_M(MICROSECOND);
break;
/* avoid overflowing the fsec field */
tm->tm_sec += val / 1000;
val -= (val / 1000) * 1000;
-#ifdef HAVE_INT64_TIMESTAMP
*fsec += rint((val + fval) * 1000);
-#else
- *fsec += (val + fval) * 1e-3;
-#endif
tmask = DTK_M(MILLISECOND);
break;
case DTK_SECOND:
tm->tm_sec += val;
-#ifdef HAVE_INT64_TIMESTAMP
*fsec += rint(fval * 1000000);
-#else
- *fsec += fval;
-#endif
/*
* If any subseconds were specified, consider this
break;
case AGO:
- is_before = TRUE;
+ is_before = true;
type = val;
break;
{
int sec;
-#ifdef HAVE_INT64_TIMESTAMP
sec = *fsec / USECS_PER_SEC;
*fsec -= sec * USECS_PER_SEC;
-#else
- TMODULO(*fsec, sec, 1.0);
-#endif
tm->tm_sec += sec;
}
*/
int
DecodeISO8601Interval(char *str,
- int *dtype, struct pg_tm * tm, fsec_t *fsec)
+ int *dtype, struct pg_tm *tm, fsec_t *fsec)
{
bool datepart = true;
bool havefield = false;
continue;
}
/* Else fall through to extended alternative format */
+ /* FALLTHROUGH */
case '-': /* ISO 8601 4.4.3.3 Alternative Format,
* Extended */
if (havefield)
return 0;
}
/* Else fall through to extended alternative format */
+ /* FALLTHROUGH */
case ':': /* ISO 8601 4.4.3.3 Alternative Format,
* Extended */
if (havefield)
}
return type;
-} /* DecodeUnits() */
+} /* DecodeUnits() */
/*
* Report an error detected by one of the datetime input processing routines.
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str),
- errhint("Perhaps you need a different \"datestyle\" setting.")));
+ errhint("Perhaps you need a different \"datestyle\" setting.")));
break;
case DTERR_INTERVAL_OVERFLOW:
ereport(ERROR,
* Encode date as local time.
*/
void
-EncodeDateOnly(struct pg_tm * tm, int style, char *str)
+EncodeDateOnly(struct pg_tm *tm, int style, char *str)
{
Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
case USE_XSD_DATES:
/* compatible with ISO date formats */
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
*str++ = '-';
str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
*str++ = '-';
}
*str++ = '/';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
break;
case USE_GERMAN_DATES:
str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
*str++ = '.';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
break;
case USE_POSTGRES_DATES:
}
*str++ = '-';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
break;
}
* output.
*/
void
-EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
+EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
{
str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
*str++ = ':';
* XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
*/
void
-EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
+EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
{
int day;
case USE_XSD_DATES:
/* Compatible with ISO-8601 date formats */
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
*str++ = '-';
str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
*str++ = '-';
}
*str++ = '/';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
*str++ = ' ';
str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
*str++ = ':';
str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
*str++ = '.';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
*str++ = ' ';
str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
*str++ = ':';
str = AppendTimestampSeconds(str, tm, fsec);
*str++ = ' ';
str = pg_ltostr_zeropad(str,
- (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
+ (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
if (print_tz)
{
* tad bizarre but it's how it worked before...
*/
*is_before = (value < 0);
- *is_zero = FALSE;
+ *is_zero = false;
return cp + strlen(cp);
}
else if (*is_before)
value = -value;
sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
- *is_zero = FALSE;
+ *is_zero = false;
return cp + strlen(cp);
}
* "day-time literal"s (that look like ('4 5:6:7')
*/
void
-EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
+EncodeInterval(struct pg_tm *tm, fsec_t fsec, int style, char *str)
{
char *cp = str;
int year = tm->tm_year;
int hour = tm->tm_hour;
int min = tm->tm_min;
int sec = tm->tm_sec;
- bool is_before = FALSE;
- bool is_zero = TRUE;
+ bool is_before = false;
+ bool is_zero = true;
/*
* The sign of year and month are guaranteed to match, since they are
if (sec < 0 || (sec == 0 && fsec < 0))
{
if (is_zero)
- is_before = TRUE;
+ is_before = true;
else if (!is_before)
*cp++ = '-';
}
cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
sprintf(cp, " sec%s",
(abs(sec) != 1 || fsec != 0) ? "s" : "");
- is_zero = FALSE;
+ is_zero = false;
}
/* identically zero? then put in a unitless zero... */
if (is_zero)
}
/*
- * 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 = (FuncExpr *) node;
+ FuncExpr *expr = castNode(FuncExpr, node);
Node *ret = NULL;
Node *typmod;
- Assert(IsA(expr, FuncExpr));
Assert(list_length(expr->args) >= 2);
typmod = (Node *) lsecond(expr->args);
* 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 */