]> 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 9346f2ab68c997bf6b5914df1aecba7b74f74e57..76ab9496e2ebe3700563679856a1f06e93062853 100644 (file)
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * 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-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.95 2004/02/14 20:16:17 tgl Exp $
+ *       src/backend/utils/adt/date.c
  *
  *-------------------------------------------------------------------------
  */
 
 #include <ctype.h>
 #include <limits.h>
-#include <time.h>
 #include <float.h>
+#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/timestamp.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
- * expressions like timeval / 3600, where timeval is double.
+ * expressions like timeval / SECS_PER_HOUR, where timeval is double.
  */
 #ifdef __FAST_MATH__
 #error -ffast-math is known to break this code
 #endif
 
 
-static int     time2tm(TimeADT time, struct tm * tm, fsec_t *fsec);
-static int     timetz2tm(TimeTzADT *time, struct tm * tm, fsec_t *fsec, int *tzp);
-static int     tm2time(struct tm * tm, fsec_t fsec, TimeADT *result);
-static int     tm2timetz(struct tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
+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 int     tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
 
+
+/* common code for timetypmodin and timetztypmodin */
+static int32
+anytime_typmodin(bool istz, ArrayType *ta)
+{
+       int32      *tl;
+       int                     n;
+
+       tl = ArrayGetIntegerTypmods(ta, &n);
+
+       /*
+        * we're not too tense about good error message here because grammar
+        * shouldn't allow wrong number of modifiers for TIME
+        */
+       if (n != 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("invalid type modifier")));
+
+       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",
+                                               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",
+                                               typmod, (istz ? " WITH TIME ZONE" : ""),
+                                               MAX_TIME_PRECISION)));
+               typmod = MAX_TIME_PRECISION;
+       }
+
+       return typmod;
+}
+
+/* common code for timetypmodout and timetztypmodout */
+static char *
+anytime_typmodout(bool istz, int32 typmod)
+{
+       const char *tz = istz ? " with time zone" : " without time zone";
+
+       if (typmod >= 0)
+               return psprintf("(%d)%s", (int) typmod, tz);
+       else
+               return psprintf("%s", tz);
+}
+
+
 /*****************************************************************************
  *      Date ADT
  *****************************************************************************/
@@ -57,7 +118,7 @@ date_in(PG_FUNCTION_ARGS)
        char       *str = PG_GETARG_CSTRING(0);
        DateADT         date;
        fsec_t          fsec;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tzp;
        int                     dtype;
@@ -65,12 +126,10 @@ date_in(PG_FUNCTION_ARGS)
        int                     dterr;
        char       *field[MAXDATEFIELDS];
        int                     ftype[MAXDATEFIELDS];
-       char            lowstr[MAXDATELEN + 1];
+       char            workbuf[MAXDATELEN + 1];
 
-       if (strlen(str) >= sizeof(lowstr))
-               dterr = DTERR_BAD_FORMAT;
-       else
-               dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+       dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+                                                 field, ftype, MAXDATEFIELDS, &nf);
        if (dterr == 0)
                dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
        if (dterr != 0)
@@ -84,7 +143,7 @@ date_in(PG_FUNCTION_ARGS)
                case DTK_CURRENT:
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("date/time value \"current\" is no longer supported")));
+                         errmsg("date/time value \"current\" is no longer supported")));
 
                        GetCurrentDateTime(tm);
                        break;
@@ -93,13 +152,33 @@ date_in(PG_FUNCTION_ARGS)
                        GetEpochTime(tm);
                        break;
 
+               case DTK_LATE:
+                       DATE_NOEND(date);
+                       PG_RETURN_DATEADT(date);
+
+               case DTK_EARLY:
+                       DATE_NOBEGIN(date);
+                       PG_RETURN_DATEADT(date);
+
                default:
                        DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
                        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),
+                                errmsg("date out of range: \"%s\"", str)));
+
        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);
 }
 
@@ -111,14 +190,18 @@ date_out(PG_FUNCTION_ARGS)
 {
        DateADT         date = PG_GETARG_DATEADT(0);
        char       *result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        char            buf[MAXDATELEN + 1];
 
-       j2date(date + POSTGRES_EPOCH_JDATE,
-                  &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
-
-       EncodeDateOnly(tm, DateStyle, buf);
+       if (DATE_NOT_FINITE(date))
+               EncodeSpecialDate(date, buf);
+       else
+       {
+               j2date(date + POSTGRES_EPOCH_JDATE,
+                          &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
+               EncodeDateOnly(tm, DateStyle, buf);
+       }
 
        result = pstrdup(buf);
        PG_RETURN_CSTRING(result);
@@ -131,8 +214,19 @@ Datum
 date_recv(PG_FUNCTION_ARGS)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
+       DateADT         result;
+
+       result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
+
+       /* Limit to the same range that date_in() accepts. */
+       if (DATE_NOT_FINITE(result))
+                /* ok */ ;
+       else if (!IS_VALID_DATE(result))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("date out of range")));
 
-       PG_RETURN_DATEADT((DateADT) pq_getmsgint(buf, sizeof(DateADT)));
+       PG_RETURN_DATEADT(result);
 }
 
 /*
@@ -149,6 +243,143 @@ 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.
+ */
+void
+EncodeSpecialDate(DateADT dt, char *str)
+{
+       if (DATE_IS_NOBEGIN(dt))
+               strcpy(str, EARLY);
+       else if (DATE_IS_NOEND(dt))
+               strcpy(str, LATE);
+       else    /* shouldn't happen */
+               elog(ERROR, "invalid argument for EncodeSpecialDate");
+}
+
+
+/*
+ * 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
@@ -221,6 +452,36 @@ 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)
+{
+       DateADT         date = PG_GETARG_DATEADT(0);
+
+       PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
+}
+
 Datum
 date_larger(PG_FUNCTION_ARGS)
 {
@@ -247,6 +508,11 @@ date_mi(PG_FUNCTION_ARGS)
        DateADT         dateVal1 = PG_GETARG_DATEADT(0);
        DateADT         dateVal2 = PG_GETARG_DATEADT(1);
 
+       if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("cannot subtract infinite dates")));
+
        PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
 }
 
@@ -258,8 +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))
+               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);
 }
 
 /* Subtract a number of days from a date, giving a new date.
@@ -269,8 +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))
+               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);
 }
 
 /*
@@ -278,50 +570,103 @@ date_mii(PG_FUNCTION_ARGS)
  * time zone
  */
 
-#ifdef HAVE_INT64_TIMESTAMP
-/* date is days since 2000, timestamp is microseconds since same... */
-#define date2timestamp(dateVal) \
-       ((Timestamp) ((dateVal) * INT64CONST(86400000000)))
-#else
-/* date is days since 2000, timestamp is seconds since same... */
-#define date2timestamp(dateVal) \
-       ((Timestamp) ((dateVal) * 86400.0))
-#endif
+static Timestamp
+date2timestamp(DateADT dateVal)
+{
+       Timestamp       result;
+
+       if (DATE_IS_NOBEGIN(dateVal))
+               TIMESTAMP_NOBEGIN(result);
+       else if (DATE_IS_NOEND(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")));
+
+               /* date is days since 2000, timestamp is microseconds since same... */
+               result = dateVal * USECS_PER_DAY;
+       }
+
+       return result;
+}
 
 static TimestampTz
 date2timestamptz(DateADT dateVal)
 {
        TimestampTz result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
+       int                     tz;
 
-       j2date(dateVal + POSTGRES_EPOCH_JDATE,
-                  &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
-
-       if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+       if (DATE_IS_NOBEGIN(dateVal))
+               TIMESTAMP_NOBEGIN(result);
+       else if (DATE_IS_NOEND(dateVal))
+               TIMESTAMP_NOEND(result);
+       else
        {
-               int                     tz;
+               /*
+                * 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_min = 0;
                tm->tm_sec = 0;
-               tz = DetermineLocalTimeZone(tm);
+               tz = DetermineTimeZoneOffset(tm, session_timezone);
 
-#ifdef HAVE_INT64_TIMESTAMP
-               result = (dateVal * INT64CONST(86400000000))
-                       + (tz * INT64CONST(1000000));
-#else
-               result = dateVal * 86400.0 + tz;
-#endif
+               result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
+
+               /*
+                * 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")));
        }
+
+       return result;
+}
+
+/*
+ * date2timestamp_no_overflow
+ *
+ * This is chartered to produce a double value that is numerically
+ * equivalent to the corresponding Timestamp value, if the date is in the
+ * valid range of Timestamps, but in any case not throw an overflow error.
+ * We can do this since the numerical range of double is greater than
+ * that of non-erroneous timestamps.  The results are currently only
+ * used for statistical estimation purposes.
+ */
+double
+date2timestamp_no_overflow(DateADT dateVal)
+{
+       double          result;
+
+       if (DATE_IS_NOBEGIN(dateVal))
+               result = -DBL_MAX;
+       else if (DATE_IS_NOEND(dateVal))
+               result = DBL_MAX;
        else
        {
-               /* Outside of range for timezone support, so assume UTC */
-#ifdef HAVE_INT64_TIMESTAMP
-               result = (dateVal * INT64CONST(86400000000));
-#else
-               result = dateVal * 86400.0;
-#endif
+               /* date is days since 2000, timestamp is microseconds since same... */
+               result = dateVal * (double) USECS_PER_DAY;
        }
 
        return result;
@@ -420,8 +765,8 @@ Datum
 date_eq_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -432,8 +777,8 @@ Datum
 date_ne_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -444,8 +789,8 @@ Datum
 date_lt_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -456,8 +801,8 @@ Datum
 date_gt_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -468,8 +813,8 @@ Datum
 date_le_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -480,8 +825,8 @@ Datum
 date_ge_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -492,8 +837,8 @@ Datum
 date_cmp_timestamptz(PG_FUNCTION_ARGS)
 {
        DateADT         dateVal = PG_GETARG_DATEADT(0);
-       TimestampTz     dt2 = PG_GETARG_TIMESTAMPTZ(1);
-       TimestampTz     dt1;
+       TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+       TimestampTz dt1;
 
        dt1 = date2timestamptz(dateVal);
 
@@ -587,9 +932,9 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_eq_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -599,9 +944,9 @@ timestamptz_eq_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_ne_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -611,9 +956,9 @@ timestamptz_ne_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_lt_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -623,9 +968,9 @@ timestamptz_lt_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_gt_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -635,9 +980,9 @@ timestamptz_gt_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_le_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -647,9 +992,9 @@ timestamptz_le_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_ge_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -659,9 +1004,9 @@ timestamptz_ge_date(PG_FUNCTION_ARGS)
 Datum
 timestamptz_cmp_date(PG_FUNCTION_ARGS)
 {
-       TimestampTz     dt1 = PG_GETARG_TIMESTAMPTZ(0);
+       TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
        DateADT         dateVal = PG_GETARG_DATEADT(1);
-       TimestampTz     dt2;
+       TimestampTz dt2;
 
        dt2 = date2timestamptz(dateVal);
 
@@ -723,7 +1068,6 @@ date_timestamp(PG_FUNCTION_ARGS)
        PG_RETURN_TIMESTAMP(result);
 }
 
-
 /* timestamp_date()
  * Convert timestamp to date data type.
  */
@@ -732,19 +1076,23 @@ timestamp_date(PG_FUNCTION_ARGS)
 {
        Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
        DateADT         result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
 
-       if (TIMESTAMP_NOT_FINITE(timestamp))
-               PG_RETURN_NULL();
-
-       if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-                                errmsg("timestamp out of range")));
+       if (TIMESTAMP_IS_NOBEGIN(timestamp))
+               DATE_NOBEGIN(result);
+       else if (TIMESTAMP_IS_NOEND(timestamp))
+               DATE_NOEND(result);
+       else
+       {
+               if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("timestamp out of range")));
 
-       result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+               result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+       }
 
        PG_RETURN_DATEADT(result);
 }
@@ -773,21 +1121,24 @@ timestamptz_date(PG_FUNCTION_ARGS)
 {
        TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
        DateADT         result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
        int                     tz;
-       char       *tzn;
 
-       if (TIMESTAMP_NOT_FINITE(timestamp))
-               PG_RETURN_NULL();
-
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-                                errmsg("timestamp out of range")));
+       if (TIMESTAMP_IS_NOBEGIN(timestamp))
+               DATE_NOBEGIN(result);
+       else if (TIMESTAMP_IS_NOEND(timestamp))
+               DATE_NOEND(result);
+       else
+       {
+               if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("timestamp out of range")));
 
-       result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+               result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+       }
 
        PG_RETURN_DATEADT(result);
 }
@@ -801,27 +1152,40 @@ abstime_date(PG_FUNCTION_ARGS)
 {
        AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
        DateADT         result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tz;
 
        switch (abstime)
        {
                case INVALID_ABSTIME:
-               case NOSTART_ABSTIME:
-               case NOEND_ABSTIME:
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                          errmsg("cannot convert reserved abstime value to date")));
+                                  errmsg("cannot convert reserved abstime value to date")));
+                       result = 0;                     /* keep compiler quiet */
+                       break;
 
-                       /*
-                        * pretend to drop through to make compiler think that result
-                        * will be set
-                        */
+               case NOSTART_ABSTIME:
+                       DATE_NOBEGIN(result);
+                       break;
+
+               case NOEND_ABSTIME:
+                       DATE_NOEND(result);
+                       break;
 
                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;
        }
 
@@ -829,64 +1193,6 @@ abstime_date(PG_FUNCTION_ARGS)
 }
 
 
-/* date_text()
- * Convert date to text data type.
- */
-Datum
-date_text(PG_FUNCTION_ARGS)
-{
-       /* Input is a Date, but may as well leave it in Datum form */
-       Datum           date = PG_GETARG_DATUM(0);
-       text       *result;
-       char       *str;
-       int                     len;
-
-       str = DatumGetCString(DirectFunctionCall1(date_out, date));
-
-       len = (strlen(str) + VARHDRSZ);
-
-       result = palloc(len);
-
-       VARATT_SIZEP(result) = len;
-       memmove(VARDATA(result), str, (len - VARHDRSZ));
-
-       pfree(str);
-
-       PG_RETURN_TEXT_P(result);
-}
-
-
-/* text_date()
- * Convert text string to date.
- * Text type is not null terminated, so use temporary string
- *     then call the standard input routine.
- */
-Datum
-text_date(PG_FUNCTION_ARGS)
-{
-       text       *str = PG_GETARG_TEXT_P(0);
-       int                     i;
-       char       *sp,
-                          *dp,
-                               dstr[MAXDATELEN + 1];
-
-       if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-                                errmsg("invalid input syntax for type date: \"%s\"",
-                                               VARDATA(str))));
-
-       sp = VARDATA(str);
-       dp = dstr;
-       for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
-               *dp++ = *sp++;
-       *dp = '\0';
-
-       return DirectFunctionCall1(date_in,
-                                                          CStringGetDatum(dstr));
-}
-
-
 /*****************************************************************************
  *      Time ADT
  *****************************************************************************/
@@ -902,20 +1208,18 @@ time_in(PG_FUNCTION_ARGS)
        int32           typmod = PG_GETARG_INT32(2);
        TimeADT         result;
        fsec_t          fsec;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tz;
        int                     nf;
        int                     dterr;
-       char            lowstr[MAXDATELEN + 1];
+       char            workbuf[MAXDATELEN + 1];
        char       *field[MAXDATEFIELDS];
        int                     dtype;
        int                     ftype[MAXDATEFIELDS];
 
-       if (strlen(str) >= sizeof(lowstr))
-               dterr = DTERR_BAD_FORMAT;
-       else
-               dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+       dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+                                                 field, ftype, MAXDATEFIELDS, &nf);
        if (dterr == 0)
                dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
        if (dterr != 0)
@@ -931,43 +1235,30 @@ time_in(PG_FUNCTION_ARGS)
  * Convert a tm structure to a time data type.
  */
 static int
-tm2time(struct tm * tm, fsec_t fsec, TimeADT *result)
+tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
 {
-#ifdef HAVE_INT64_TIMESTAMP
-       *result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
-                               * INT64CONST(1000000)) + fsec);
-#else
-       *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
-#endif
+       *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
+                          * USECS_PER_SEC) + fsec;
        return 0;
 }
 
 /* time2tm()
  * Convert time data type to POSIX time structure.
- * For dates within the system-supported time_t range, convert to the
- *     local time zone. If out of this range, leave as GMT. - tgl 97/05/27
+ *
+ * For dates within the range of pg_time_t, convert to the local time zone.
+ * If out of this range, leave as UTC (in practice that could only happen
+ * if pg_time_t is just 32 bits) - thomas 97/05/27
  */
 static int
-time2tm(TimeADT time, struct tm * tm, fsec_t *fsec)
-{
-#ifdef HAVE_INT64_TIMESTAMP
-       tm->tm_hour = (time / INT64CONST(3600000000));
-       time -= (tm->tm_hour * INT64CONST(3600000000));
-       tm->tm_min = (time / INT64CONST(60000000));
-       time -= (tm->tm_min * INT64CONST(60000000));
-       tm->tm_sec = (time / INT64CONST(1000000));
-       time -= (tm->tm_sec * INT64CONST(1000000));
+time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
+{
+       tm->tm_hour = time / USECS_PER_HOUR;
+       time -= tm->tm_hour * USECS_PER_HOUR;
+       tm->tm_min = time / USECS_PER_MINUTE;
+       time -= tm->tm_min * USECS_PER_MINUTE;
+       tm->tm_sec = time / USECS_PER_SEC;
+       time -= tm->tm_sec * USECS_PER_SEC;
        *fsec = time;
-#else
-       double          trem;
-
-       trem = time;
-       TMODULO(trem, tm->tm_hour, 3600e0);
-       TMODULO(trem, tm->tm_min, 60e0);
-       TMODULO(trem, tm->tm_sec, 1e0);
-       *fsec = trem;
-#endif
-
        return 0;
 }
 
@@ -976,13 +1267,13 @@ time_out(PG_FUNCTION_ARGS)
 {
        TimeADT         time = PG_GETARG_TIMEADT(0);
        char       *result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
        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);
@@ -990,20 +1281,28 @@ 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)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
 
-#ifdef HAVE_INT64_TIMESTAMP
-       PG_RETURN_TIMEADT((TimeADT) pq_getmsgint64(buf));
-#else
-       PG_RETURN_TIMEADT((TimeADT) pq_getmsgfloat8(buf));
+#ifdef NOT_USED
+       Oid                     typelem = PG_GETARG_OID(1);
 #endif
+       int32           typmod = PG_GETARG_INT32(2);
+       TimeADT         result;
+
+       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")));
+
+       AdjustTimeForTypmod(&result, typmod);
+
+       PG_RETURN_TIMEADT(result);
 }
 
 /*
@@ -1016,14 +1315,66 @@ 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));
 }
 
+Datum
+timetypmodin(PG_FUNCTION_ARGS)
+{
+       ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+       PG_RETURN_INT32(anytime_typmodin(false, ta));
+}
+
+Datum
+timetypmodout(PG_FUNCTION_ARGS)
+{
+       int32           typmod = PG_GETARG_INT32(0);
+
+       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.
@@ -1052,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),
@@ -1073,47 +1423,14 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
                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))
+       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]);
-               }
+                       *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
+                       *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
+                                         TimeScales[typmod]);
        }
 }
 
@@ -1185,6 +1502,12 @@ time_cmp(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(0);
 }
 
+Datum
+time_hash(PG_FUNCTION_ARGS)
+{
+       return hashint8(fcinfo);
+}
+
 Datum
 time_larger(PG_FUNCTION_ARGS)
 {
@@ -1203,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.
  */
@@ -1213,8 +1536,8 @@ Datum
 overlaps_time(PG_FUNCTION_ARGS)
 {
        /*
-        * The arguments are TimeADT, but we leave them as generic Datums to
-        * avoid dereferencing nulls (TimeADT is pass-by-reference!)
+        * The arguments are TimeADT, but we leave them as generic Datums to avoid
+        * dereferencing nulls (TimeADT is pass-by-reference!)
         */
        Datum           ts1 = PG_GETARG_DATUM(0);
        Datum           te1 = PG_GETARG_DATUM(1);
@@ -1231,9 +1554,9 @@ overlaps_time(PG_FUNCTION_ARGS)
        (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
 
        /*
-        * If both endpoints of interval 1 are null, the result is null
-        * (unknown). If just one endpoint is null, take ts1 as the non-null
-        * one. Otherwise, take ts1 as the lesser endpoint.
+        * If both endpoints of interval 1 are null, the result is null (unknown).
+        * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
+        * take ts1 as the lesser endpoint.
         */
        if (ts1IsNull)
        {
@@ -1281,8 +1604,8 @@ overlaps_time(PG_FUNCTION_ARGS)
        if (TIMEADT_GT(ts1, ts2))
        {
                /*
-                * This case is ts1 < te2 OR te1 < te2, which may look redundant
-                * but in the presence of nulls it's not quite completely so.
+                * This case is ts1 < te2 OR te1 < te2, which may look redundant but
+                * in the presence of nulls it's not quite completely so.
                 */
                if (te2IsNull)
                        PG_RETURN_NULL();
@@ -1292,8 +1615,8 @@ overlaps_time(PG_FUNCTION_ARGS)
                        PG_RETURN_NULL();
 
                /*
-                * If te1 is not null then we had ts1 <= te1 above, and we just
-                * found ts1 >= te2, hence te1 >= te2.
+                * If te1 is not null then we had ts1 <= te1 above, and we just found
+                * ts1 >= te2, hence te1 >= te2.
                 */
                PG_RETURN_BOOL(false);
        }
@@ -1308,8 +1631,8 @@ overlaps_time(PG_FUNCTION_ARGS)
                        PG_RETURN_NULL();
 
                /*
-                * If te2 is not null then we had ts2 <= te2 above, and we just
-                * found ts2 >= te1, hence te2 >= te1.
+                * If te2 is not null then we had ts2 <= te2 above, and we just found
+                * ts2 >= te1, hence te2 >= te1.
                 */
                PG_RETURN_BOOL(false);
        }
@@ -1317,8 +1640,7 @@ overlaps_time(PG_FUNCTION_ARGS)
        {
                /*
                 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
-                * rather silly way of saying "true if both are nonnull, else
-                * null".
+                * rather silly way of saying "true if both are nonnull, else null".
                 */
                if (te1IsNull || te2IsNull)
                        PG_RETURN_NULL();
@@ -1337,29 +1659,24 @@ timestamp_time(PG_FUNCTION_ARGS)
 {
        Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
        TimeADT         result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_NULL();
 
-       if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
+       if (timestamp2tm(timestamp, NULL, 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 / 86400000000 *
-        * 86400000000) - timestamp;
+        * Could also do this with time = (timestamp / USECS_PER_DAY *
+        * USECS_PER_DAY) - timestamp;
         */
-       result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
-                          * INT64CONST(1000000)) + fsec);
-#else
-       result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
-#endif
+       result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+                         USECS_PER_SEC) + fsec;
 
        PG_RETURN_TIMEADT(result);
 }
@@ -1372,31 +1689,25 @@ timestamptz_time(PG_FUNCTION_ARGS)
 {
        TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
        TimeADT         result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tz;
        fsec_t          fsec;
-       char       *tzn;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_NULL();
 
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 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 / 86400000000 *
-        * 86400000000) - timestamp;
+        * Could also do this with time = (timestamp / USECS_PER_DAY *
+        * USECS_PER_DAY) - timestamp;
         */
-       result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
-                          * INT64CONST(1000000)) + fsec);
-#else
-       result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
-#endif
+       result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+                         USECS_PER_SEC) + fsec;
 
        PG_RETURN_TIMEADT(result);
 }
@@ -1411,9 +1722,15 @@ datetime_timestamp(PG_FUNCTION_ARGS)
        TimeADT         time = PG_GETARG_TIMEADT(1);
        Timestamp       result;
 
-       result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
-                                                                                                DateADTGetDatum(date)));
-       result += time;
+       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);
 }
@@ -1430,6 +1747,7 @@ time_interval(PG_FUNCTION_ARGS)
        result = (Interval *) palloc(sizeof(Interval));
 
        result->time = time;
+       result->day = 0;
        result->month = 0;
 
        PG_RETURN_INTERVAL_P(result);
@@ -1439,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'.
  */
@@ -1448,26 +1766,19 @@ interval_time(PG_FUNCTION_ARGS)
 {
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        TimeADT         result;
-
-#ifdef HAVE_INT64_TIMESTAMP
        int64           days;
 
        result = span->time;
-       if (result >= INT64CONST(86400000000))
+       if (result >= USECS_PER_DAY)
        {
-               days = result / INT64CONST(86400000000);
-               result -= days * INT64CONST(86400000000);
+               days = result / USECS_PER_DAY;
+               result -= days * USECS_PER_DAY;
        }
        else if (result < 0)
        {
-               days = (-result + INT64CONST(86400000000 - 1)) / INT64CONST(86400000000);
-               result += days * INT64CONST(86400000000);
+               days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
+               result += days * USECS_PER_DAY;
        }
-#else
-       result = span->time;
-       if (result >= 86400e0 || result < 0)
-               result -= floor(result / 86400e0) * 86400e0;
-#endif
 
        PG_RETURN_TIMEADT(result);
 }
@@ -1484,8 +1795,9 @@ time_mi_time(PG_FUNCTION_ARGS)
 
        result = (Interval *) palloc(sizeof(Interval));
 
-       result->time = (time1 - time2);
        result->month = 0;
+       result->day = 0;
+       result->time = time1 - time2;
 
        PG_RETURN_INTERVAL_P(result);
 }
@@ -1500,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 / INT64CONST(86400000000) * INT64CONST(86400000000));
+       result = time + span->time;
+       result -= result / USECS_PER_DAY * USECS_PER_DAY;
        if (result < INT64CONST(0))
-               result += INT64CONST(86400000000);
-#else
-       TimeADT         time1;
-
-       result = (time + span->time);
-       TMODULO(result, time1, 86400e0);
-       if (result < 0)
-               result += 86400;
-#endif
+               result += USECS_PER_DAY;
 
        PG_RETURN_TIMEADT(result);
 }
@@ -1527,94 +1830,14 @@ 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 / INT64CONST(86400000000) * INT64CONST(86400000000));
+       result = time - span->time;
+       result -= result / USECS_PER_DAY * USECS_PER_DAY;
        if (result < INT64CONST(0))
-               result += INT64CONST(86400000000);
-#else
-       TimeADT         time1;
-
-       result = (time - span->time);
-       TMODULO(result, time1, 86400e0);
-       if (result < 0)
-               result += 86400;
-#endif
+               result += USECS_PER_DAY;
 
        PG_RETURN_TIMEADT(result);
 }
 
-/* interval_pl_time()
- * Add time to interval.
- */
-Datum
-interval_pl_time(PG_FUNCTION_ARGS)
-{
-       Datum           span = PG_GETARG_DATUM(0);
-       Datum           time = PG_GETARG_DATUM(1);
-
-       return DirectFunctionCall2(time_pl_interval, time, span);
-}
-
-
-/* time_text()
- * Convert time to text data type.
- */
-Datum
-time_text(PG_FUNCTION_ARGS)
-{
-       /* Input is a Time, but may as well leave it in Datum form */
-       Datum           time = PG_GETARG_DATUM(0);
-       text       *result;
-       char       *str;
-       int                     len;
-
-       str = DatumGetCString(DirectFunctionCall1(time_out, time));
-
-       len = (strlen(str) + VARHDRSZ);
-
-       result = palloc(len);
-
-       VARATT_SIZEP(result) = len;
-       memmove(VARDATA(result), str, (len - VARHDRSZ));
-
-       pfree(str);
-
-       PG_RETURN_TEXT_P(result);
-}
-
-
-/* text_time()
- * Convert text string to time.
- * Text type is not null terminated, so use temporary string
- *     then call the standard input routine.
- */
-Datum
-text_time(PG_FUNCTION_ARGS)
-{
-       text       *str = PG_GETARG_TEXT_P(0);
-       int                     i;
-       char       *sp,
-                          *dp,
-                               dstr[MAXDATELEN + 1];
-
-       if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-                                errmsg("invalid input syntax for type time: \"%s\"",
-                                               VARDATA(str))));
-
-       sp = VARDATA(str);
-       dp = dstr;
-       for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
-               *dp++ = *sp++;
-       *dp = '\0';
-
-       return DirectFunctionCall3(time_in,
-                                                          CStringGetDatum(dstr),
-                                                          ObjectIdGetDatum(InvalidOid),
-                                                          Int32GetDatum(-1));
-}
 
 /* time_part()
  * Extract specified field from time type.
@@ -1622,28 +1845,16 @@ text_time(PG_FUNCTION_ARGS)
 Datum
 time_part(PG_FUNCTION_ARGS)
 {
-       text       *units = PG_GETARG_TEXT_P(0);
+       text       *units = PG_GETARG_TEXT_PP(0);
        TimeADT         time = PG_GETARG_TIMEADT(1);
        float8          result;
        int                     type,
                                val;
-       int                     i;
-       char       *up,
-                          *lp,
-                               lowunits[MAXDATELEN + 1];
-
-       if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("\"time\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
+       char       *lowunits;
 
-       up = VARDATA(units);
-       lp = lowunits;
-       for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
-               *lp++ = tolower((unsigned char) *up++);
-       *lp = '\0';
+       lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+                                                                                       VARSIZE_ANY_EXHDR(units),
+                                                                                       false);
 
        type = DecodeUnits(0, lowunits, &val);
        if (type == UNKNOWN_FIELD)
@@ -1652,7 +1863,7 @@ time_part(PG_FUNCTION_ARGS)
        if (type == UNITS)
        {
                fsec_t          fsec;
-               struct tm       tt,
+               struct pg_tm tt,
                                   *tm = &tt;
 
                time2tm(time, tm, &fsec);
@@ -1660,28 +1871,15 @@ time_part(PG_FUNCTION_ARGS)
                switch (val)
                {
                        case DTK_MICROSEC:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = ((tm->tm_sec * INT64CONST(1000000)) + fsec);
-#else
-                               result = ((tm->tm_sec + fsec) * 1000000);
-#endif
+                               result = tm->tm_sec * 1000000.0 + fsec;
                                break;
 
                        case DTK_MILLISEC:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = ((tm->tm_sec * INT64CONST(1000))
-                                                 + (fsec / INT64CONST(1000)));
-#else
-                               result = ((tm->tm_sec + fsec) * 1000);
-#endif
+                               result = tm->tm_sec * 1000.0 + fsec / 1000.0;
                                break;
 
                        case DTK_SECOND:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = (tm->tm_sec + (fsec / INT64CONST(1000000)));
-#else
-                               result = (tm->tm_sec + fsec);
-#endif
+                               result = tm->tm_sec + fsec / 1000000.0;
                                break;
 
                        case DTK_MINUTE:
@@ -1702,31 +1900,25 @@ time_part(PG_FUNCTION_ARGS)
                        case DTK_DECADE:
                        case DTK_CENTURY:
                        case DTK_MILLENNIUM:
+                       case DTK_ISOYEAR:
                        default:
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("\"time\" units \"%s\" not recognized",
-                                                        DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
-
+                                                               lowunits)));
                                result = 0;
                }
        }
-       else if ((type == RESERV) && (val == DTK_EPOCH))
+       else if (type == RESERV && val == DTK_EPOCH)
        {
-#ifdef HAVE_INT64_TIMESTAMP
-               result = (time / 1000000e0);
-#else
-               result = time;
-#endif
+               result = time / 1000000.0;
        }
        else
        {
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"time\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
+                                               lowunits)));
                result = 0;
        }
 
@@ -1742,14 +1934,10 @@ time_part(PG_FUNCTION_ARGS)
  * Convert a tm structure to a time data type.
  */
 static int
-tm2timetz(struct tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
+tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
 {
-#ifdef HAVE_INT64_TIMESTAMP
-       result->time = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
-                                        * INT64CONST(1000000)) + fsec);
-#else
-       result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
-#endif
+       result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+                                       USECS_PER_SEC) + fsec;
        result->zone = tz;
 
        return 0;
@@ -1766,20 +1954,18 @@ timetz_in(PG_FUNCTION_ARGS)
        int32           typmod = PG_GETARG_INT32(2);
        TimeTzADT  *result;
        fsec_t          fsec;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tz;
        int                     nf;
        int                     dterr;
-       char            lowstr[MAXDATELEN + 1];
+       char            workbuf[MAXDATELEN + 1];
        char       *field[MAXDATEFIELDS];
        int                     dtype;
        int                     ftype[MAXDATEFIELDS];
 
-       if (strlen(str) >= sizeof(lowstr))
-               dterr = DTERR_BAD_FORMAT;
-       else
-               dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+       dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+                                                 field, ftype, MAXDATEFIELDS, &nf);
        if (dterr == 0)
                dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
        if (dterr != 0)
@@ -1797,14 +1983,14 @@ timetz_out(PG_FUNCTION_ARGS)
 {
        TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
        char       *result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
        int                     tz;
        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);
@@ -1817,18 +2003,33 @@ Datum
 timetz_recv(PG_FUNCTION_ARGS)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
-       TimeTzADT  *time;
-
-       time = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
-#ifdef HAVE_INT64_TIMESTAMP
-       time->time = pq_getmsgint64(buf);
-#else
-       time->time = pq_getmsgfloat8(buf);
+#ifdef NOT_USED
+       Oid                     typelem = PG_GETARG_OID(1);
 #endif
-       time->zone = pq_getmsgint(buf, sizeof(time->zone));
+       int32           typmod = PG_GETARG_INT32(2);
+       TimeTzADT  *result;
+
+       result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+
+       result->time = pq_getmsgint64(buf);
 
-       PG_RETURN_TIMETZADT_P(time);
+       if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("time out of range")));
+
+       result->zone = pq_getmsgint(buf, sizeof(result->zone));
+
+       /* 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")));
+
+       AdjustTimeForTypmod(&(result->time), typmod);
+
+       PG_RETURN_TIMETZADT_P(result);
 }
 
 /*
@@ -1841,39 +2042,42 @@ 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));
 }
 
+Datum
+timetztypmodin(PG_FUNCTION_ARGS)
+{
+       ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+       PG_RETURN_INT32(anytime_typmodin(true, ta));
+}
+
+Datum
+timetztypmodout(PG_FUNCTION_ARGS)
+{
+       int32           typmod = PG_GETARG_INT32(0);
+
+       PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
+}
+
 
 /* timetz2tm()
  * Convert TIME WITH TIME ZONE data type to POSIX time structure.
  */
 static int
-timetz2tm(TimeTzADT *time, struct tm * tm, fsec_t *fsec, int *tzp)
-{
-#ifdef HAVE_INT64_TIMESTAMP
-       int64           trem = time->time;
-
-       tm->tm_hour = (trem / INT64CONST(3600000000));
-       trem -= (tm->tm_hour * INT64CONST(3600000000));
-       tm->tm_min = (trem / INT64CONST(60000000));
-       trem -= (tm->tm_min * INT64CONST(60000000));
-       tm->tm_sec = (trem / INT64CONST(1000000));
-       *fsec = (trem - (tm->tm_sec * INT64CONST(1000000)));
-#else
-       double          trem = time->time;
-
-       TMODULO(trem, tm->tm_hour, 3600e0);
-       TMODULO(trem, tm->tm_min, 60e0);
-       TMODULO(trem, tm->tm_sec, 1e0);
-       *fsec = trem;
-#endif
+timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
+{
+       TimeOffset      trem = time->time;
+
+       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;
 
        if (tzp != NULL)
                *tzp = time->zone;
@@ -1906,12 +2110,12 @@ timetz_scale(PG_FUNCTION_ARGS)
 static int
 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
 {
-       double          t1,
+       TimeOffset      t1,
                                t2;
 
        /* Primary sort is by true (GMT-equivalent) time */
-       t1 = time1->time + time1->zone;
-       t2 = time2->time + time2->zone;
+       t1 = time1->time + (time1->zone * USECS_PER_SEC);
+       t2 = time2->time + (time2->zone * USECS_PER_SEC);
 
        if (t1 > t2)
                return 1;
@@ -1993,20 +2197,20 @@ timetz_cmp(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
 }
 
-/*
- * timetz, being an unusual size, needs a specialized hash function.
- */
 Datum
 timetz_hash(PG_FUNCTION_ARGS)
 {
        TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
+       uint32          thash;
 
        /*
-        * Specify hash length as sizeof(double) + sizeof(int4), not as
-        * sizeof(TimeTzADT), so that any garbage pad bytes in the structure
-        * won't be included in the hash!
+        * To avoid any problems with padding bytes in the struct, we figure the
+        * field hashes separately and XOR them.
         */
-       return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->zone));
+       thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
+                                                                                          Int64GetDatumFast(key->time)));
+       thash ^= DatumGetUInt32(hash_uint32(key->zone));
+       PG_RETURN_UINT32(thash);
 }
 
 Datum
@@ -2047,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 / INT64CONST(86400000000) * INT64CONST(86400000000));
+       result->time = time->time + span->time;
+       result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
        if (result->time < INT64CONST(0))
-               result->time += INT64CONST(86400000000);
-#else
-       result->time = (time->time + span->time);
-       TMODULO(result->time, time1.time, 86400e0);
-       if (result->time < 0)
-               result->time += 86400;
-#endif
+               result->time += USECS_PER_DAY;
 
        result->zone = time->zone;
 
@@ -2080,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 / INT64CONST(86400000000) * INT64CONST(86400000000));
+       result->time = time->time - span->time;
+       result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
        if (result->time < INT64CONST(0))
-               result->time += INT64CONST(86400000000);
-#else
-       result->time = (time->time - span->time);
-       TMODULO(result->time, time1.time, 86400e0);
-       if (result->time < 0)
-               result->time += 86400;
-#endif
+               result->time += USECS_PER_DAY;
 
        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.
  */
@@ -2113,8 +2295,8 @@ Datum
 overlaps_timetz(PG_FUNCTION_ARGS)
 {
        /*
-        * The arguments are TimeTzADT *, but we leave them as generic Datums
-        * for convenience of notation --- and to avoid dereferencing nulls.
+        * The arguments are TimeTzADT *, but we leave them as generic Datums for
+        * convenience of notation --- and to avoid dereferencing nulls.
         */
        Datum           ts1 = PG_GETARG_DATUM(0);
        Datum           te1 = PG_GETARG_DATUM(1);
@@ -2131,9 +2313,9 @@ overlaps_timetz(PG_FUNCTION_ARGS)
        DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
 
        /*
-        * If both endpoints of interval 1 are null, the result is null
-        * (unknown). If just one endpoint is null, take ts1 as the non-null
-        * one. Otherwise, take ts1 as the lesser endpoint.
+        * If both endpoints of interval 1 are null, the result is null (unknown).
+        * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
+        * take ts1 as the lesser endpoint.
         */
        if (ts1IsNull)
        {
@@ -2181,8 +2363,8 @@ overlaps_timetz(PG_FUNCTION_ARGS)
        if (TIMETZ_GT(ts1, ts2))
        {
                /*
-                * This case is ts1 < te2 OR te1 < te2, which may look redundant
-                * but in the presence of nulls it's not quite completely so.
+                * This case is ts1 < te2 OR te1 < te2, which may look redundant but
+                * in the presence of nulls it's not quite completely so.
                 */
                if (te2IsNull)
                        PG_RETURN_NULL();
@@ -2192,8 +2374,8 @@ overlaps_timetz(PG_FUNCTION_ARGS)
                        PG_RETURN_NULL();
 
                /*
-                * If te1 is not null then we had ts1 <= te1 above, and we just
-                * found ts1 >= te2, hence te1 >= te2.
+                * If te1 is not null then we had ts1 <= te1 above, and we just found
+                * ts1 >= te2, hence te1 >= te2.
                 */
                PG_RETURN_BOOL(false);
        }
@@ -2208,8 +2390,8 @@ overlaps_timetz(PG_FUNCTION_ARGS)
                        PG_RETURN_NULL();
 
                /*
-                * If te2 is not null then we had ts2 <= te2 above, and we just
-                * found ts2 >= te1, hence te2 >= te1.
+                * If te2 is not null then we had ts2 <= te2 above, and we just found
+                * ts2 >= te1, hence te2 >= te1.
                 */
                PG_RETURN_BOOL(false);
        }
@@ -2217,8 +2399,7 @@ overlaps_timetz(PG_FUNCTION_ARGS)
        {
                /*
                 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
-                * rather silly way of saying "true if both are nonnull, else
-                * null".
+                * rather silly way of saying "true if both are nonnull, else null".
                 */
                if (te1IsNull || te2IsNull)
                        PG_RETURN_NULL();
@@ -2248,14 +2429,14 @@ time_timetz(PG_FUNCTION_ARGS)
 {
        TimeADT         time = PG_GETARG_TIMEADT(0);
        TimeTzADT  *result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        fsec_t          fsec;
        int                     tz;
 
        GetCurrentDateTime(tm);
        time2tm(time, tm, &fsec);
-       tz = DetermineLocalTimeZone(tm);
+       tz = DetermineTimeZoneOffset(tm, session_timezone);
 
        result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
@@ -2274,16 +2455,15 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
 {
        TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
        TimeTzADT  *result;
-       struct tm       tt,
+       struct pg_tm tt,
                           *tm = &tt;
        int                     tz;
        fsec_t          fsec;
-       char       *tzn;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_NULL();
 
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
+       if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
                ereport(ERROR,
                                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                                 errmsg("timestamp out of range")));
@@ -2309,104 +2489,53 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
        TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
        TimestampTz result;
 
-#ifdef HAVE_INT64_TIMESTAMP
-       result = (((date * INT64CONST(86400000000)) + time->time)
-                         + (time->zone * INT64CONST(1000000)));
-#else
-       result = (((date * 86400.0) + time->time) + time->zone);
-#endif
-
-       PG_RETURN_TIMESTAMP(result);
-}
-
-
-/* timetz_text()
- * Convert timetz to text data type.
- */
-Datum
-timetz_text(PG_FUNCTION_ARGS)
-{
-       /* Input is a Timetz, but may as well leave it in Datum form */
-       Datum           timetz = PG_GETARG_DATUM(0);
-       text       *result;
-       char       *str;
-       int                     len;
-
-       str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
-
-       len = (strlen(str) + VARHDRSZ);
-
-       result = palloc(len);
-
-       VARATT_SIZEP(result) = len;
-       memmove(VARDATA(result), str, (len - VARHDRSZ));
+       if (DATE_IS_NOBEGIN(date))
+               TIMESTAMP_NOBEGIN(result);
+       else if (DATE_IS_NOEND(date))
+               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 (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;
 
-       pfree(str);
+               /*
+                * 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_TEXT_P(result);
+       PG_RETURN_TIMESTAMP(result);
 }
 
 
-/* text_timetz()
- * Convert text string to timetz.
- * Text type is not null terminated, so use temporary string
- *     then call the standard input routine.
- */
-Datum
-text_timetz(PG_FUNCTION_ARGS)
-{
-       text       *str = PG_GETARG_TEXT_P(0);
-       int                     i;
-       char       *sp,
-                          *dp,
-                               dstr[MAXDATELEN + 1];
-
-       if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-                  errmsg("invalid input syntax for type time with time zone: \"%s\"",
-                                 VARDATA(str))));
-
-       sp = VARDATA(str);
-       dp = dstr;
-       for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
-               *dp++ = *sp++;
-       *dp = '\0';
-
-       return DirectFunctionCall3(timetz_in,
-                                                          CStringGetDatum(dstr),
-                                                          ObjectIdGetDatum(InvalidOid),
-                                                          Int32GetDatum(-1));
-}
-
 /* timetz_part()
  * Extract specified field from time type.
  */
 Datum
 timetz_part(PG_FUNCTION_ARGS)
 {
-       text       *units = PG_GETARG_TEXT_P(0);
+       text       *units = PG_GETARG_TEXT_PP(0);
        TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
        float8          result;
        int                     type,
                                val;
-       int                     i;
-       char       *up,
-                          *lp,
-                               lowunits[MAXDATELEN + 1];
-
-       if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("\"time with time zone\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
+       char       *lowunits;
 
-       up = VARDATA(units);
-       lp = lowunits;
-       for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
-               *lp++ = tolower((unsigned char) *up++);
-       *lp = '\0';
+       lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+                                                                                       VARSIZE_ANY_EXHDR(units),
+                                                                                       false);
 
        type = DecodeUnits(0, lowunits, &val);
        if (type == UNKNOWN_FIELD)
@@ -2417,7 +2546,7 @@ timetz_part(PG_FUNCTION_ARGS)
                double          dummy;
                int                     tz;
                fsec_t          fsec;
-               struct tm       tt,
+               struct pg_tm tt,
                                   *tm = &tt;
 
                timetz2tm(time, tm, &fsec, &tz);
@@ -2430,38 +2559,25 @@ timetz_part(PG_FUNCTION_ARGS)
 
                        case DTK_TZ_MINUTE:
                                result = -tz;
-                               result /= 60;
-                               FMODULO(result, dummy, 60e0);
+                               result /= SECS_PER_MINUTE;
+                               FMODULO(result, dummy, (double) SECS_PER_MINUTE);
                                break;
 
                        case DTK_TZ_HOUR:
                                dummy = -tz;
-                               FMODULO(dummy, result, 3600e0);
+                               FMODULO(dummy, result, (double) SECS_PER_HOUR);
                                break;
 
                        case DTK_MICROSEC:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = ((tm->tm_sec * INT64CONST(1000000)) + fsec);
-#else
-                               result = ((tm->tm_sec + fsec) * 1000000);
-#endif
+                               result = tm->tm_sec * 1000000.0 + fsec;
                                break;
 
                        case DTK_MILLISEC:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = ((tm->tm_sec * INT64CONST(1000))
-                                                 + (fsec / INT64CONST(1000)));
-#else
-                               result = ((tm->tm_sec + fsec) * 1000);
-#endif
+                               result = tm->tm_sec * 1000.0 + fsec / 1000.0;
                                break;
 
                        case DTK_SECOND:
-#ifdef HAVE_INT64_TIMESTAMP
-                               result = (tm->tm_sec + (fsec / INT64CONST(1000000)));
-#else
-                               result = (tm->tm_sec + fsec);
-#endif
+                               result = tm->tm_sec + fsec / 1000000.0;
                                break;
 
                        case DTK_MINUTE:
@@ -2482,29 +2598,21 @@ timetz_part(PG_FUNCTION_ARGS)
                        default:
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("\"time with time zone\" units \"%s\" not recognized",
-                                                        DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
-
+                               errmsg("\"time with time zone\" units \"%s\" not recognized",
+                                          lowunits)));
                                result = 0;
                }
        }
-       else if ((type == RESERV) && (val == DTK_EPOCH))
+       else if (type == RESERV && val == DTK_EPOCH)
        {
-#ifdef HAVE_INT64_TIMESTAMP
-               result = ((time->time / 1000000e0) - time->zone);
-#else
-               result = (time->time - time->zone);
-#endif
+               result = time->time / 1000000.0 + time->zone;
        }
        else
        {
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"time with time zone\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
-
+                                               lowunits)));
                result = 0;
        }
 
@@ -2513,68 +2621,86 @@ timetz_part(PG_FUNCTION_ARGS)
 
 /* timetz_zone()
  * Encode time with time zone type with specified time zone.
+ * Applies DST rules as of the current date.
  */
 Datum
 timetz_zone(PG_FUNCTION_ARGS)
 {
-       text       *zone = PG_GETARG_TEXT_P(0);
-       TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
+       text       *zone = PG_GETARG_TEXT_PP(0);
+       TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
        TimeTzADT  *result;
        int                     tz;
+       char            tzname[TZ_STRLEN_MAX + 1];
+       char       *lowzone;
        int                     type,
                                val;
-       int                     i;
-       char       *up,
-                          *lp,
-                               lowzone[MAXDATELEN + 1];
+       pg_tz      *tzp;
 
-       if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                         PointerGetDatum(zone))))));
-
-       up = VARDATA(zone);
-       lp = lowzone;
-       for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
-               *lp++ = tolower((unsigned char) *up++);
-       *lp = '\0';
+       /*
+        * 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));
 
-       type = DecodeSpecial(0, lowzone, &val);
+       /* DecodeTimezoneAbbrev requires lowercase input */
+       lowzone = downcase_truncate_identifier(tzname,
+                                                                                  strlen(tzname),
+                                                                                  false);
 
-       result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+       type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
 
-       if ((type == TZ) || (type == DTZ))
+       if (type == TZ || type == DTZ)
        {
-               tz = val * 60;
-#ifdef HAVE_INT64_TIMESTAMP
-               result->time = time->time + ((time->zone - tz) * INT64CONST(1000000));
-               while (result->time < INT64CONST(0))
-                       result->time += INT64CONST(86400000000);
-               while (result->time >= INT64CONST(86400000000))
-                       result->time -= INT64CONST(86400000000);
-#else
-               result->time = time->time + (time->zone - tz);
-               while (result->time < 0)
-                       result->time += 86400;
-               while (result->time >= 86400)
-                       result->time -= 86400;
-#endif
+               /* 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;
 
-               result->zone = tz;
+               tm = pg_localtime(&now, tzp);
+               tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
        }
        else
        {
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone \"%s\" not recognized", lowzone)));
+               /* try it as a full zone name */
+               tzp = pg_tzset(tzname);
+               if (tzp)
+               {
+                       /* Get the offset-from-GMT that is valid today for the zone */
+                       pg_time_t       now = (pg_time_t) time(NULL);
+                       struct pg_tm *tm;
 
-               PG_RETURN_NULL();
+                       tm = pg_localtime(&now, tzp);
+                       tz = -tm->tm_gmtoff;
+               }
+               else
+               {
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("time zone \"%s\" not recognized", tzname)));
+                       tz = 0;                         /* keep compiler quiet */
+               }
        }
 
+       result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+
+       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;
+
+       result->zone = tz;
+
        PG_RETURN_TIMETZADT_P(result);
-}      /* timetz_zone() */
+}
 
 /* timetz_izone()
  * Encode time with time zone type with specified time interval as time zone.
@@ -2587,36 +2713,24 @@ 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,
-                                                                                         PointerGetDatum(zone))))));
-
-#ifdef HAVE_INT64_TIMESTAMP
-       tz = -(zone->time / INT64CONST(1000000));
-#else
-       tz = -(zone->time);
-#endif
+                 errmsg("interval time zone \"%s\" must not include months or days",
+                                DatumGetCString(DirectFunctionCall1(interval_out,
+                                                                                                 PointerGetDatum(zone))))));
+
+       tz = -(zone->time / USECS_PER_SEC);
 
        result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
-#ifdef HAVE_INT64_TIMESTAMP
-       result->time = time->time + ((time->zone - tz) * INT64CONST(1000000));
+       result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
        while (result->time < INT64CONST(0))
-               result->time += INT64CONST(86400000000);
-       while (result->time >= INT64CONST(86400000000))
-               result->time -= INT64CONST(86400000000);
-#else
-       result->time = time->time + (time->zone - tz);
-       while (result->time < 0)
-               result->time += 86400;
-       while (result->time >= 86400)
-               result->time -= 86400;
-#endif
+               result->time += USECS_PER_DAY;
+       while (result->time >= USECS_PER_DAY)
+               result->time -= USECS_PER_DAY;
 
        result->zone = tz;
 
        PG_RETURN_TIMETZADT_P(result);
-}      /* timetz_izone() */
+}