/*-------------------------------------------------------------------------
*
* date.c
- * implements DATE and TIME data types specified in SQL-92 standard
+ * implements DATE and TIME data types specified in SQL standard
*
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
*
#include <time.h>
#include "access/hash.h"
+#include "access/xact.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#endif
-static void EncodeSpecialDate(DateADT dt, char *str);
static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
static int32
anytime_typmodin(bool istz, ArrayType *ta)
{
- int32 typmod;
int32 *tl;
int n;
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid type modifier")));
- if (*tl < 0)
+ return anytime_typmod_check(istz, tl[0]);
+}
+
+/* exported so parse_expr.c can use it */
+int32
+anytime_typmod_check(bool istz, int32 typmod)
+{
+ if (typmod < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("TIME(%d)%s precision must not be negative",
- *tl, (istz ? " WITH TIME ZONE" : ""))));
- if (*tl > MAX_TIME_PRECISION)
+ typmod, (istz ? " WITH TIME ZONE" : ""))));
+ if (typmod > MAX_TIME_PRECISION)
{
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
- *tl, (istz ? " WITH TIME ZONE" : ""),
+ typmod, (istz ? " WITH TIME ZONE" : ""),
MAX_TIME_PRECISION)));
typmod = MAX_TIME_PRECISION;
}
- else
- typmod = *tl;
return typmod;
}
static char *
anytime_typmodout(bool istz, int32 typmod)
{
- char *res = (char *) palloc(64);
const char *tz = istz ? " with time zone" : " without time zone";
if (typmod >= 0)
- snprintf(res, 64, "(%d)%s", (int) typmod, tz);
+ return psprintf("(%d)%s", (int) typmod, tz);
else
- snprintf(res, 64, "%s", tz);
- return res;
+ return psprintf("%s", tz);
}
break;
}
+ /* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+ /* Now check for just-out-of-range dates */
+ if (!IS_VALID_DATE(date))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: \"%s\"", str)));
+
PG_RETURN_DATEADT(date);
}
/* Limit to the same range that date_in() accepts. */
if (DATE_NOT_FINITE(result))
/* ok */ ;
- else if (result < -POSTGRES_EPOCH_JDATE ||
- result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE)
+ else if (!IS_VALID_DATE(result))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range")));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+/*
+ * make_date - date constructor
+ */
+Datum
+make_date(PG_FUNCTION_ARGS)
+{
+ struct pg_tm tm;
+ DateADT date;
+ int dterr;
+ bool bc = false;
+
+ tm.tm_year = PG_GETARG_INT32(0);
+ tm.tm_mon = PG_GETARG_INT32(1);
+ tm.tm_mday = PG_GETARG_INT32(2);
+
+ /* Handle negative years as BC */
+ if (tm.tm_year < 0)
+ {
+ bc = true;
+ tm.tm_year = -tm.tm_year;
+ }
+
+ dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm);
+
+ if (dterr != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+ errmsg("date field value out of range: %d-%02d-%02d",
+ tm.tm_year, tm.tm_mon, tm.tm_mday)));
+
+ /* Prevent overflow in Julian-day routines */
+ if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: %d-%02d-%02d",
+ tm.tm_year, tm.tm_mon, tm.tm_mday)));
+
+ date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
+
+ /* Now check for just-out-of-range dates */
+ if (!IS_VALID_DATE(date))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: %d-%02d-%02d",
+ tm.tm_year, tm.tm_mon, tm.tm_mday)));
+
+ PG_RETURN_DATEADT(date);
+}
+
/*
* Convert reserved date values to string.
*/
-static void
+void
EncodeSpecialDate(DateADT dt, char *str)
{
if (DATE_IS_NOBEGIN(dt))
}
+/*
+ * GetSQLCurrentDate -- implements CURRENT_DATE
+ */
+DateADT
+GetSQLCurrentDate(void)
+{
+ TimestampTz ts;
+ struct pg_tm tt,
+ *tm = &tt;
+ fsec_t fsec;
+ int tz;
+
+ ts = GetCurrentTransactionStartTimestamp();
+
+ if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+}
+
+/*
+ * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n)
+ */
+TimeTzADT *
+GetSQLCurrentTime(int32 typmod)
+{
+ TimeTzADT *result;
+ TimestampTz ts;
+ struct pg_tm tt,
+ *tm = &tt;
+ fsec_t fsec;
+ int tz;
+
+ ts = GetCurrentTransactionStartTimestamp();
+
+ if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+ tm2timetz(tm, fsec, tz, result);
+ AdjustTimeForTypmod(&(result->time), typmod);
+ return result;
+}
+
+/*
+ * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n)
+ */
+TimeADT
+GetSQLLocalTime(int32 typmod)
+{
+ TimeADT result;
+ TimestampTz ts;
+ struct pg_tm tt,
+ *tm = &tt;
+ fsec_t fsec;
+ int tz;
+
+ ts = GetCurrentTransactionStartTimestamp();
+
+ if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ tm2time(tm, fsec, &result);
+ AdjustTimeForTypmod(&result, typmod);
+ return result;
+}
+
+
/*
* Comparison functions for dates
*/
{
DateADT dateVal = PG_GETARG_DATEADT(0);
int32 days = PG_GETARG_INT32(1);
+ DateADT result;
if (DATE_NOT_FINITE(dateVal))
- days = 0; /* can't change infinity */
+ PG_RETURN_DATEADT(dateVal); /* can't change infinity */
- PG_RETURN_DATEADT(dateVal + days);
+ result = dateVal + days;
+
+ /* Check for integer overflow and out-of-allowed-range */
+ if ((days >= 0 ? (result < dateVal) : (result > dateVal)) ||
+ !IS_VALID_DATE(result))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range")));
+
+ PG_RETURN_DATEADT(result);
}
/* Subtract a number of days from a date, giving a new date.
{
DateADT dateVal = PG_GETARG_DATEADT(0);
int32 days = PG_GETARG_INT32(1);
+ DateADT result;
if (DATE_NOT_FINITE(dateVal))
- days = 0; /* can't change infinity */
+ PG_RETURN_DATEADT(dateVal); /* can't change infinity */
- PG_RETURN_DATEADT(dateVal - days);
+ result = dateVal - days;
+
+ /* Check for integer overflow and out-of-allowed-range */
+ if ((days >= 0 ? (result > dateVal) : (result < dateVal)) ||
+ !IS_VALID_DATE(result))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range")));
+
+ PG_RETURN_DATEADT(result);
}
/*
TIMESTAMP_NOEND(result);
else
{
-#ifdef HAVE_INT64_TIMESTAMP
- /* date is days since 2000, timestamp is microseconds since same... */
- result = dateVal * USECS_PER_DAY;
- /* Date's range is wider than timestamp's, so check for overflow */
- if (result / USECS_PER_DAY != dateVal)
+ /*
+ * Date's range is wider than timestamp's, so check for boundaries.
+ * Since dates have the same minimum values as timestamps, only upper
+ * boundary need be checked for overflow.
+ */
+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
-#else
- /* date is days since 2000, timestamp is seconds since same... */
- result = dateVal * (double) SECS_PER_DAY;
-#endif
+
+ /* date is days since 2000, timestamp is microseconds since same... */
+ result = dateVal * USECS_PER_DAY;
}
return result;
TIMESTAMP_NOEND(result);
else
{
+ /*
+ * Date's range is wider than timestamp's, so check for boundaries.
+ * Since dates have the same minimum values as timestamps, only upper
+ * boundary need be checked for overflow.
+ */
+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range for timestamp")));
+
j2date(dateVal + POSTGRES_EPOCH_JDATE,
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
tm->tm_hour = 0;
tm->tm_sec = 0;
tz = DetermineTimeZoneOffset(tm, session_timezone);
-#ifdef HAVE_INT64_TIMESTAMP
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
- /* Date's range is wider than timestamp's, so check for overflow */
- if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
+
+ /*
+ * Since it is possible to go beyond allowed timestamptz range because
+ * of time zone, check for allowed timestamp range after adding tz.
+ */
+ if (!IS_VALID_TIMESTAMP(result))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
-#else
- result = dateVal * (double) SECS_PER_DAY + tz;
-#endif
}
return result;
result = DBL_MAX;
else
{
-#ifdef HAVE_INT64_TIMESTAMP
/* date is days since 2000, timestamp is microseconds since same... */
result = dateVal * (double) USECS_PER_DAY;
-#else
- /* date is days since 2000, timestamp is seconds since same... */
- result = dateVal * (double) SECS_PER_DAY;
-#endif
}
return result;
default:
abstime2tm(abstime, &tz, tm, NULL);
+ /* Prevent overflow in Julian-day routines */
+ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("abstime out of range for date")));
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+ /* Now check for just-out-of-range dates */
+ if (!IS_VALID_DATE(result))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("abstime out of range for date")));
break;
}
static int
tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
{
-#ifdef HAVE_INT64_TIMESTAMP
*result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
* USECS_PER_SEC) + fsec;
-#else
- *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
-#endif
return 0;
}
static int
time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
{
-#ifdef HAVE_INT64_TIMESTAMP
tm->tm_hour = time / USECS_PER_HOUR;
time -= tm->tm_hour * USECS_PER_HOUR;
tm->tm_min = time / USECS_PER_MINUTE;
tm->tm_sec = time / USECS_PER_SEC;
time -= tm->tm_sec * USECS_PER_SEC;
*fsec = time;
-#else
- double trem;
-
-recalc:
- trem = time;
- TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
- TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
- TMODULO(trem, tm->tm_sec, 1.0);
- trem = TIMEROUND(trem);
- /* roundoff may need to propagate to higher-order fields */
- if (trem >= 1.0)
- {
- time = ceil(time);
- goto recalc;
- }
- *fsec = trem;
-#endif
-
return 0;
}
/*
* time_recv - converts external binary format to time
- *
- * We make no attempt to provide compatibility between int and float
- * time representations ...
*/
Datum
time_recv(PG_FUNCTION_ARGS)
int32 typmod = PG_GETARG_INT32(2);
TimeADT result;
-#ifdef HAVE_INT64_TIMESTAMP
result = pq_getmsgint64(buf);
if (result < INT64CONST(0) || result > USECS_PER_DAY)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("time out of range")));
-#else
- result = pq_getmsgfloat8(buf);
-
- if (result < 0 || result > (double) SECS_PER_DAY)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("time out of range")));
-#endif
AdjustTimeForTypmod(&result, typmod);
StringInfoData buf;
pq_begintypsend(&buf);
-#ifdef HAVE_INT64_TIMESTAMP
pq_sendint64(&buf, time);
-#else
- pq_sendfloat8(&buf, time);
-#endif
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
}
+/*
+ * make_time - time constructor
+ */
+Datum
+make_time(PG_FUNCTION_ARGS)
+{
+ int tm_hour = PG_GETARG_INT32(0);
+ int tm_min = PG_GETARG_INT32(1);
+ double sec = PG_GETARG_FLOAT8(2);
+ TimeADT time;
+
+ /* This should match the checks in DecodeTimeOnly */
+ if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 ||
+ sec < 0 || sec > SECS_PER_MINUTE ||
+ tm_hour > HOURS_PER_DAY ||
+ /* test for > 24:00:00 */
+ (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+ errmsg("time field value out of range: %d:%02d:%02g",
+ tm_hour, tm_min, sec)));
+
+ /* This should match tm2time */
+ time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
+ * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
+
+ PG_RETURN_TIMEADT(time);
+}
+
/* time_transform()
* Flatten calls to time_scale() and timetz_scale() that solely represent
static void
AdjustTimeForTypmod(TimeADT *time, int32 typmod)
{
-#ifdef HAVE_INT64_TIMESTAMP
static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
INT64CONST(1000000),
INT64CONST(100000),
INT64CONST(5),
INT64CONST(0)
};
-#else
- /* note MAX_TIME_PRECISION differs in this case */
- static const double TimeScales[MAX_TIME_PRECISION + 1] = {
- 1.0,
- 10.0,
- 100.0,
- 1000.0,
- 10000.0,
- 100000.0,
- 1000000.0,
- 10000000.0,
- 100000000.0,
- 1000000000.0,
- 10000000000.0
- };
-#endif
if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
{
- /*
- * Note: this round-to-nearest code is not completely consistent about
- * rounding values that are exactly halfway between integral values.
- * On most platforms, rint() will implement round-to-nearest-even, but
- * the integer code always rounds up (away from zero). Is it worth
- * trying to be consistent?
- */
-#ifdef HAVE_INT64_TIMESTAMP
if (*time >= INT64CONST(0))
*time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
TimeScales[typmod];
else
*time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
TimeScales[typmod]);
-#else
- *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
-#endif
}
}
Datum
time_hash(PG_FUNCTION_ARGS)
{
- /* We can use either hashint8 or hashfloat8 directly */
-#ifdef HAVE_INT64_TIMESTAMP
return hashint8(fcinfo);
-#else
- return hashfloat8(fcinfo);
-#endif
}
Datum
PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
}
-/* overlaps_time() --- implements the SQL92 OVERLAPS operator.
+/* overlaps_time() --- implements the SQL OVERLAPS operator.
*
- * Algorithm is per SQL92 spec. This is much harder than you'd think
+ * Algorithm is per SQL spec. This is much harder than you'd think
* because the spec requires us to deliver a non-null answer in some cases
* where some of the inputs are null.
*/
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
-#ifdef HAVE_INT64_TIMESTAMP
-
/*
* Could also do this with time = (timestamp / USECS_PER_DAY *
* USECS_PER_DAY) - timestamp;
*/
result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
-#else
- result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
-#endif
PG_RETURN_TIMEADT(result);
}
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
-#ifdef HAVE_INT64_TIMESTAMP
-
/*
* Could also do this with time = (timestamp / USECS_PER_DAY *
* USECS_PER_DAY) - timestamp;
*/
result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
-#else
- result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
-#endif
PG_RETURN_TIMEADT(result);
}
result = date2timestamp(date);
if (!TIMESTAMP_NOT_FINITE(result))
+ {
result += time;
+ if (!IS_VALID_TIMESTAMP(result))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+ }
PG_RETURN_TIMESTAMP(result);
}
* Convert interval to time data type.
*
* This is defined as producing the fractional-day portion of the interval.
- * Therefore, we can just ignore the months field. It is not real clear
+ * Therefore, we can just ignore the months field. It is not real clear
* what to do with negative intervals, but we choose to subtract the floor,
* so that, say, '-2 hours' becomes '22:00:00'.
*/
{
Interval *span = PG_GETARG_INTERVAL_P(0);
TimeADT result;
-
-#ifdef HAVE_INT64_TIMESTAMP
int64 days;
result = span->time;
days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
result += days * USECS_PER_DAY;
}
-#else
- result = span->time;
- if (result >= (double) SECS_PER_DAY || result < 0)
- result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
-#endif
PG_RETURN_TIMEADT(result);
}
Interval *span = PG_GETARG_INTERVAL_P(1);
TimeADT result;
-#ifdef HAVE_INT64_TIMESTAMP
result = time + span->time;
result -= result / USECS_PER_DAY * USECS_PER_DAY;
if (result < INT64CONST(0))
result += USECS_PER_DAY;
-#else
- TimeADT time1;
-
- result = time + span->time;
- TMODULO(result, time1, (double) SECS_PER_DAY);
- if (result < 0)
- result += SECS_PER_DAY;
-#endif
PG_RETURN_TIMEADT(result);
}
Interval *span = PG_GETARG_INTERVAL_P(1);
TimeADT result;
-#ifdef HAVE_INT64_TIMESTAMP
result = time - span->time;
result -= result / USECS_PER_DAY * USECS_PER_DAY;
if (result < INT64CONST(0))
result += USECS_PER_DAY;
-#else
- TimeADT time1;
-
- result = time - span->time;
- TMODULO(result, time1, (double) SECS_PER_DAY);
- if (result < 0)
- result += SECS_PER_DAY;
-#endif
PG_RETURN_TIMEADT(result);
}
switch (val)
{
case DTK_MICROSEC:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec * 1000000.0 + fsec;
-#else
- result = (tm->tm_sec + fsec) * 1000000;
-#endif
break;
case DTK_MILLISEC:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec * 1000.0 + fsec / 1000.0;
-#else
- result = (tm->tm_sec + fsec) * 1000;
-#endif
break;
case DTK_SECOND:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec + fsec / 1000000.0;
-#else
- result = tm->tm_sec + fsec;
-#endif
break;
case DTK_MINUTE:
}
else if (type == RESERV && val == DTK_EPOCH)
{
-#ifdef HAVE_INT64_TIMESTAMP
result = time / 1000000.0;
-#else
- result = time;
-#endif
}
else
{
static int
tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
{
-#ifdef HAVE_INT64_TIMESTAMP
result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
-#else
- result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
-#endif
result->zone = tz;
return 0;
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
-#ifdef HAVE_INT64_TIMESTAMP
result->time = pq_getmsgint64(buf);
if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("time out of range")));
-#else
- result->time = pq_getmsgfloat8(buf);
-
- if (result->time < 0 || result->time > (double) SECS_PER_DAY)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("time out of range")));
-#endif
result->zone = pq_getmsgint(buf, sizeof(result->zone));
StringInfoData buf;
pq_begintypsend(&buf);
-#ifdef HAVE_INT64_TIMESTAMP
pq_sendint64(&buf, time->time);
-#else
- pq_sendfloat8(&buf, time->time);
-#endif
pq_sendint(&buf, time->zone, sizeof(time->zone));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
{
TimeOffset trem = time->time;
-#ifdef HAVE_INT64_TIMESTAMP
tm->tm_hour = trem / USECS_PER_HOUR;
trem -= tm->tm_hour * USECS_PER_HOUR;
tm->tm_min = trem / USECS_PER_MINUTE;
trem -= tm->tm_min * USECS_PER_MINUTE;
tm->tm_sec = trem / USECS_PER_SEC;
*fsec = trem - tm->tm_sec * USECS_PER_SEC;
-#else
-recalc:
- TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
- TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
- TMODULO(trem, tm->tm_sec, 1.0);
- trem = TIMEROUND(trem);
- /* roundoff may need to propagate to higher-order fields */
- if (trem >= 1.0)
- {
- trem = ceil(time->time);
- goto recalc;
- }
- *fsec = trem;
-#endif
if (tzp != NULL)
*tzp = time->zone;
t2;
/* Primary sort is by true (GMT-equivalent) time */
-#ifdef HAVE_INT64_TIMESTAMP
t1 = time1->time + (time1->zone * USECS_PER_SEC);
t2 = time2->time + (time2->zone * USECS_PER_SEC);
-#else
- t1 = time1->time + time1->zone;
- t2 = time2->time + time2->zone;
-#endif
if (t1 > t2)
return 1;
/*
* To avoid any problems with padding bytes in the struct, we figure the
- * field hashes separately and XOR them. This also provides a convenient
- * framework for dealing with the fact that the time field might be either
- * double or int64.
+ * field hashes separately and XOR them.
*/
-#ifdef HAVE_INT64_TIMESTAMP
thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
Int64GetDatumFast(key->time)));
-#else
- thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
- Float8GetDatumFast(key->time)));
-#endif
thash ^= DatumGetUInt32(hash_uint32(key->zone));
PG_RETURN_UINT32(thash);
}
Interval *span = PG_GETARG_INTERVAL_P(1);
TimeTzADT *result;
-#ifndef HAVE_INT64_TIMESTAMP
- TimeTzADT time1;
-#endif
-
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
-#ifdef HAVE_INT64_TIMESTAMP
result->time = time->time + span->time;
result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
if (result->time < INT64CONST(0))
result->time += USECS_PER_DAY;
-#else
- result->time = time->time + span->time;
- TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
- if (result->time < 0)
- result->time += SECS_PER_DAY;
-#endif
result->zone = time->zone;
Interval *span = PG_GETARG_INTERVAL_P(1);
TimeTzADT *result;
-#ifndef HAVE_INT64_TIMESTAMP
- TimeTzADT time1;
-#endif
-
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
-#ifdef HAVE_INT64_TIMESTAMP
result->time = time->time - span->time;
result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
if (result->time < INT64CONST(0))
result->time += USECS_PER_DAY;
-#else
- result->time = time->time - span->time;
- TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
- if (result->time < 0)
- result->time += SECS_PER_DAY;
-#endif
result->zone = time->zone;
PG_RETURN_TIMETZADT_P(result);
}
-/* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
+/* overlaps_timetz() --- implements the SQL OVERLAPS operator.
*
- * Algorithm is per SQL92 spec. This is much harder than you'd think
+ * Algorithm is per SQL spec. This is much harder than you'd think
* because the spec requires us to deliver a non-null answer in some cases
* where some of the inputs are null.
*/
TIMESTAMP_NOEND(result);
else
{
-#ifdef HAVE_INT64_TIMESTAMP
+ /*
+ * Date's range is wider than timestamp's, so check for boundaries.
+ * Since dates have the same minimum values as timestamps, only upper
+ * boundary need be checked for overflow.
+ */
+ if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range for timestamp")));
result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
-#else
- result = date * (double) SECS_PER_DAY + time->time + time->zone;
-#endif
+
+ /*
+ * Since it is possible to go beyond allowed timestamptz range because
+ * of time zone, check for allowed timestamp range after adding tz.
+ */
+ if (!IS_VALID_TIMESTAMP(result))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range for timestamp")));
}
PG_RETURN_TIMESTAMP(result);
break;
case DTK_MICROSEC:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec * 1000000.0 + fsec;
-#else
- result = (tm->tm_sec + fsec) * 1000000;
-#endif
break;
case DTK_MILLISEC:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec * 1000.0 + fsec / 1000.0;
-#else
- result = (tm->tm_sec + fsec) * 1000;
-#endif
break;
case DTK_SECOND:
-#ifdef HAVE_INT64_TIMESTAMP
result = tm->tm_sec + fsec / 1000000.0;
-#else
- result = tm->tm_sec + fsec;
-#endif
break;
case DTK_MINUTE:
}
else if (type == RESERV && val == DTK_EPOCH)
{
-#ifdef HAVE_INT64_TIMESTAMP
result = time->time / 1000000.0 + time->zone;
-#else
- result = time->time + time->zone;
-#endif
}
else
{
pg_tz *tzp;
/*
- * Look up the requested timezone. First we look in the date token table
- * (to handle cases like "EST"), and if that fails, we look in the
- * timezone database (to handle cases like "America/New_York"). (This
- * matches the order in which timestamp input checks the cases; it's
- * important because the timezone database unwisely uses a few zone names
- * that are identical to offset abbreviations.)
+ * Look up the requested timezone. First we look in the timezone
+ * abbreviation table (to handle cases like "EST"), and if that fails, we
+ * look in the timezone database (to handle cases like
+ * "America/New_York"). (This matches the order in which timestamp input
+ * checks the cases; it's important because the timezone database unwisely
+ * uses a few zone names that are identical to offset abbreviations.)
*/
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
+
+ /* DecodeTimezoneAbbrev requires lowercase input */
lowzone = downcase_truncate_identifier(tzname,
strlen(tzname),
false);
- type = DecodeSpecial(0, lowzone, &val);
+ type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
if (type == TZ || type == DTZ)
- tz = val * MINS_PER_HOUR;
+ {
+ /* fixed-offset abbreviation */
+ tz = -val;
+ }
+ else if (type == DYNTZ)
+ {
+ /* dynamic-offset abbreviation, resolve using current time */
+ pg_time_t now = (pg_time_t) time(NULL);
+ struct pg_tm *tm;
+
+ tm = pg_localtime(&now, tzp);
+ tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
+ }
else
{
+ /* try it as a full zone name */
tzp = pg_tzset(tzname);
if (tzp)
{
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
-#ifdef HAVE_INT64_TIMESTAMP
result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
while (result->time < INT64CONST(0))
result->time += USECS_PER_DAY;
while (result->time >= USECS_PER_DAY)
result->time -= USECS_PER_DAY;
-#else
- result->time = t->time + (t->zone - tz);
- while (result->time < 0)
- result->time += SECS_PER_DAY;
- while (result->time >= SECS_PER_DAY)
- result->time -= SECS_PER_DAY;
-#endif
result->zone = tz;
TimeTzADT *result;
int tz;
- if (zone->month != 0)
+ if (zone->month != 0 || zone->day != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("\"interval\" time zone \"%s\" not valid",
- DatumGetCString(DirectFunctionCall1(interval_out,
+ errmsg("interval time zone \"%s\" must not include months or days",
+ DatumGetCString(DirectFunctionCall1(interval_out,
PointerGetDatum(zone))))));
-#ifdef HAVE_INT64_TIMESTAMP
tz = -(zone->time / USECS_PER_SEC);
-#else
- tz = -(zone->time);
-#endif
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
-#ifdef HAVE_INT64_TIMESTAMP
result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
while (result->time < INT64CONST(0))
result->time += USECS_PER_DAY;
while (result->time >= USECS_PER_DAY)
result->time -= USECS_PER_DAY;
-#else
- result->time = time->time + (time->zone - tz);
- while (result->time < 0)
- result->time += SECS_PER_DAY;
- while (result->time >= SECS_PER_DAY)
- result->time -= SECS_PER_DAY;
-#endif
result->zone = tz;