]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/date.c
Add support for EUI-64 MAC addresses as macaddr8
[postgresql] / src / backend / utils / adt / date.c
index ee0b09f0491b5be863276b7bf2ce75de03f54a44..76ab9496e2ebe3700563679856a1f06e93062853 100644 (file)
@@ -1,9 +1,9 @@
 /*-------------------------------------------------------------------------
  *
  * 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-2010, 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"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
+#include "utils/datetime.h"
 #include "utils/nabstime.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -38,7 +41,6 @@
 #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);
@@ -50,7 +52,6 @@ static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
 static int32
 anytime_typmodin(bool istz, ArrayType *ta)
 {
-       int32           typmod;
        int32      *tl;
        int                     n;
 
@@ -65,22 +66,27 @@ anytime_typmodin(bool istz, ArrayType *ta)
                                (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;
 }
@@ -89,14 +95,12 @@ anytime_typmodin(bool istz, ArrayType *ta)
 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);
 }
 
 
@@ -161,6 +165,7 @@ date_in(PG_FUNCTION_ARGS)
                        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),
@@ -168,6 +173,12 @@ date_in(PG_FUNCTION_ARGS)
 
        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);
 }
 
@@ -210,8 +221,7 @@ date_recv(PG_FUNCTION_ARGS)
        /* 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")));
@@ -233,10 +243,59 @@ date_send(PG_FUNCTION_ARGS)
        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))
@@ -248,6 +307,80 @@ EncodeSpecialDate(DateADT dt, char *str)
 }
 
 
+/*
+ * 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
  */
@@ -319,6 +452,28 @@ date_cmp(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(0);
 }
 
+static int
+date_fastcmp(Datum x, Datum y, SortSupport ssup)
+{
+       DateADT         a = DatumGetDateADT(x);
+       DateADT         b = DatumGetDateADT(y);
+
+       if (a < b)
+               return -1;
+       else if (a > b)
+               return 1;
+       return 0;
+}
+
+Datum
+date_sortsupport(PG_FUNCTION_ARGS)
+{
+       SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+       ssup->comparator = date_fastcmp;
+       PG_RETURN_VOID();
+}
+
 Datum
 date_finite(PG_FUNCTION_ARGS)
 {
@@ -369,11 +524,21 @@ date_pli(PG_FUNCTION_ARGS)
 {
        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 */
+
+       result = dateVal + days;
 
-       PG_RETURN_DATEADT(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.
@@ -383,11 +548,21 @@ date_mii(PG_FUNCTION_ARGS)
 {
        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 */
+
+       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(dateVal - days);
+       PG_RETURN_DATEADT(result);
 }
 
 /*
@@ -406,18 +581,18 @@ date2timestamp(DateADT dateVal)
                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;
@@ -437,6 +612,16 @@ date2timestamptz(DateADT dateVal)
                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;
@@ -444,16 +629,16 @@ date2timestamptz(DateADT dateVal)
                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;
@@ -472,7 +657,7 @@ date2timestamptz(DateADT dateVal)
 double
 date2timestamp_no_overflow(DateADT dateVal)
 {
-       double  result;
+       double          result;
 
        if (DATE_IS_NOBEGIN(dateVal))
                result = -DBL_MAX;
@@ -480,13 +665,8 @@ date2timestamp_no_overflow(DateADT dateVal)
                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;
@@ -888,7 +1068,6 @@ date_timestamp(PG_FUNCTION_ARGS)
        PG_RETURN_TIMESTAMP(result);
 }
 
-
 /* timestamp_date()
  * Convert timestamp to date data type.
  */
@@ -946,7 +1125,6 @@ timestamptz_date(PG_FUNCTION_ARGS)
                           *tm = &tt;
        fsec_t          fsec;
        int                     tz;
-       char       *tzn;
 
        if (TIMESTAMP_IS_NOBEGIN(timestamp))
                DATE_NOBEGIN(result);
@@ -954,7 +1132,7 @@ timestamptz_date(PG_FUNCTION_ARGS)
                DATE_NOEND(result);
        else
        {
-               if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
+               if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
                        ereport(ERROR,
                                        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                                         errmsg("timestamp out of range")));
@@ -997,7 +1175,17 @@ abstime_date(PG_FUNCTION_ARGS)
 
                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;
        }
 
@@ -1049,12 +1237,8 @@ time_in(PG_FUNCTION_ARGS)
 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;
 }
 
@@ -1068,7 +1252,6 @@ tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
 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;
@@ -1076,24 +1259,6 @@ time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
        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;
 }
 
@@ -1108,7 +1273,7 @@ time_out(PG_FUNCTION_ARGS)
        char            buf[MAXDATELEN + 1];
 
        time2tm(time, tm, &fsec);
-       EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
+       EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
 
        result = pstrdup(buf);
        PG_RETURN_CSTRING(result);
@@ -1116,9 +1281,6 @@ time_out(PG_FUNCTION_ARGS)
 
 /*
  *             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)
@@ -1131,21 +1293,12 @@ 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);
 
@@ -1162,11 +1315,7 @@ time_send(PG_FUNCTION_ARGS)
        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));
 }
 
@@ -1186,6 +1335,46 @@ timetypmodout(PG_FUNCTION_ARGS)
        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
+ * increases in allowed precision.
+ */
+Datum
+time_transform(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION,
+                                                                               (Node *) PG_GETARG_POINTER(0)));
+}
 
 /* time_scale()
  * Adjust time type for specified scale factor.
@@ -1214,7 +1403,6 @@ time_scale(PG_FUNCTION_ARGS)
 static void
 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
 {
-#ifdef HAVE_INT64_TIMESTAMP
        static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
                INT64CONST(1000000),
                INT64CONST(100000),
@@ -1234,42 +1422,15 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
                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
        }
 }
 
@@ -1344,12 +1505,7 @@ time_cmp(PG_FUNCTION_ARGS)
 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
@@ -1370,9 +1526,9 @@ time_smaller(PG_FUNCTION_ARGS)
        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.
  */
@@ -1515,17 +1671,12 @@ timestamp_time(PG_FUNCTION_ARGS)
                                (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);
 }
@@ -1542,27 +1693,21 @@ timestamptz_time(PG_FUNCTION_ARGS)
                           *tm = &tt;
        int                     tz;
        fsec_t          fsec;
-       char       *tzn;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_NULL();
 
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
+       if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
                ereport(ERROR,
                                (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);
 }
@@ -1579,7 +1724,13 @@ datetime_timestamp(PG_FUNCTION_ARGS)
 
        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);
 }
@@ -1606,7 +1757,7 @@ time_interval(PG_FUNCTION_ARGS)
  * 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'.
  */
@@ -1615,8 +1766,6 @@ interval_time(PG_FUNCTION_ARGS)
 {
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        TimeADT         result;
-
-#ifdef HAVE_INT64_TIMESTAMP
        int64           days;
 
        result = span->time;
@@ -1630,11 +1779,6 @@ interval_time(PG_FUNCTION_ARGS)
                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);
 }
@@ -1668,19 +1812,10 @@ time_pl_interval(PG_FUNCTION_ARGS)
        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);
 }
@@ -1695,19 +1830,10 @@ time_mi_interval(PG_FUNCTION_ARGS)
        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);
 }
@@ -1745,27 +1871,15 @@ time_part(PG_FUNCTION_ARGS)
                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:
@@ -1797,11 +1911,7 @@ time_part(PG_FUNCTION_ARGS)
        }
        else if (type == RESERV && val == DTK_EPOCH)
        {
-#ifdef HAVE_INT64_TIMESTAMP
                result = time / 1000000.0;
-#else
-               result = time;
-#endif
        }
        else
        {
@@ -1826,12 +1936,8 @@ time_part(PG_FUNCTION_ARGS)
 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;
@@ -1884,7 +1990,7 @@ timetz_out(PG_FUNCTION_ARGS)
        char            buf[MAXDATELEN + 1];
 
        timetz2tm(time, tm, &fsec, &tz);
-       EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
+       EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
 
        result = pstrdup(buf);
        PG_RETURN_CSTRING(result);
@@ -1906,27 +2012,17 @@ timetz_recv(PG_FUNCTION_ARGS)
 
        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));
 
-       /* we allow GMT displacements up to 14:59:59, cf DecodeTimezone() */
-       if (result->zone <= -15 * SECS_PER_HOUR ||
-               result->zone >= 15 * SECS_PER_HOUR)
+       /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
+       if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
                                 errmsg("time zone displacement out of range")));
@@ -1946,11 +2042,7 @@ timetz_send(PG_FUNCTION_ARGS)
        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));
 }
@@ -1980,27 +2072,12 @@ timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
 {
        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;
@@ -2037,13 +2114,8 @@ timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
                                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;
@@ -2133,17 +2205,10 @@ timetz_hash(PG_FUNCTION_ARGS)
 
        /*
         * 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);
 }
@@ -2186,23 +2251,12 @@ timetz_pl_interval(PG_FUNCTION_ARGS)
        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;
 
@@ -2219,32 +2273,21 @@ timetz_mi_interval(PG_FUNCTION_ARGS)
        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.
  */
@@ -2416,12 +2459,11 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
                           *tm = &tt;
        int                     tz;
        fsec_t          fsec;
-       char       *tzn;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_NULL();
 
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
+       if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
                ereport(ERROR,
                                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                                 errmsg("timestamp out of range")));
@@ -2453,11 +2495,25 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
                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);
@@ -2513,27 +2569,15 @@ timetz_part(PG_FUNCTION_ARGS)
                                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:
@@ -2561,11 +2605,7 @@ timetz_part(PG_FUNCTION_ARGS)
        }
        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
        {
@@ -2597,24 +2637,39 @@ timetz_zone(PG_FUNCTION_ARGS)
        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 * 60;
+       {
+               /* 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)
                {
@@ -2636,19 +2691,11 @@ timetz_zone(PG_FUNCTION_ARGS)
 
        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;
 
@@ -2666,34 +2713,22 @@ timetz_izone(PG_FUNCTION_ARGS)
        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;