]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/date.c
Update copyright for the year 2010.
[postgresql] / src / backend / utils / adt / date.c
index a66680f4772c50019847c5a09464e7afbda1e8e9..c722e47e352e18ae2bb378b37b122c91461360ed 100644 (file)
@@ -3,12 +3,12 @@
  * date.c
  *       implements DATE and TIME data types specified in SQL-92 standard
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, 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.133 2007/06/15 20:56:50 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.150 2010/01/02 16:57:53 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #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);
@@ -49,9 +50,9 @@ static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
 static int32
 anytime_typmodin(bool istz, ArrayType *ta)
 {
-       int32   typmod;
-       int32   *tl;
-       int             n;
+       int32           typmod;
+       int32      *tl;
+       int                     n;
 
        tl = ArrayGetIntegerTypmods(ta, &n);
 
@@ -74,10 +75,11 @@ anytime_typmodin(bool istz, ArrayType *ta)
                ereport(WARNING,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
-                                               *tl, (istz ? " WITH TIME ZONE" : "" ),
+                                               *tl, (istz ? " WITH TIME ZONE" : ""),
                                                MAX_TIME_PRECISION)));
                typmod = MAX_TIME_PRECISION;
-       } else
+       }
+       else
                typmod = *tl;
 
        return typmod;
@@ -87,7 +89,7 @@ anytime_typmodin(bool istz, ArrayType *ta)
 static char *
 anytime_typmodout(bool istz, int32 typmod)
 {
-       char    *res = (char *) palloc(64);
+       char       *res = (char *) palloc(64);
        const char *tz = istz ? " with time zone" : " without time zone";
 
        if (typmod >= 0)
@@ -146,6 +148,14 @@ 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;
@@ -173,10 +183,14 @@ date_out(PG_FUNCTION_ARGS)
                           *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);
@@ -189,8 +203,18 @@ Datum
 date_recv(PG_FUNCTION_ARGS)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
+       DateADT result;
 
-       PG_RETURN_DATEADT((DateADT) pq_getmsgint(buf, sizeof(DateADT)));
+       result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
+
+       /* Limit to the same range that date_in() accepts. */
+       if (result < -POSTGRES_EPOCH_JDATE ||
+               result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("date out of range")));
+
+       PG_RETURN_DATEADT(result);
 }
 
 /*
@@ -207,6 +231,20 @@ date_send(PG_FUNCTION_ARGS)
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+/*
+ * Convert reserved date values to string.
+ */
+static 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");
+}
+
 
 /*
  * Comparison functions for dates
@@ -279,6 +317,14 @@ date_cmp(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(0);
 }
 
+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)
 {
@@ -305,6 +351,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));
 }
 
@@ -317,6 +368,9 @@ date_pli(PG_FUNCTION_ARGS)
        DateADT         dateVal = PG_GETARG_DATEADT(0);
        int32           days = PG_GETARG_INT32(1);
 
+       if (DATE_NOT_FINITE(dateVal))
+               days = 0;                               /* can't change infinity */
+
        PG_RETURN_DATEADT(dateVal + days);
 }
 
@@ -328,6 +382,9 @@ date_mii(PG_FUNCTION_ARGS)
        DateADT         dateVal = PG_GETARG_DATEADT(0);
        int32           days = PG_GETARG_INT32(1);
 
+       if (DATE_NOT_FINITE(dateVal))
+               days = 0;                               /* can't change infinity */
+
        PG_RETURN_DATEADT(dateVal - days);
 }
 
@@ -336,15 +393,33 @@ date_mii(PG_FUNCTION_ARGS)
  * time zone
  */
 
+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
+       {
 #ifdef HAVE_INT64_TIMESTAMP
-/* date is days since 2000, timestamp is microseconds since same... */
-#define date2timestamp(dateVal) \
-       ((Timestamp) ((dateVal) * USECS_PER_DAY))
+               /* 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)
+                       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... */
-#define date2timestamp(dateVal) \
-       ((Timestamp) ((dateVal) * (double)SECS_PER_DAY))
+               /* date is days since 2000, timestamp is seconds since same... */
+               result = dateVal * (double) SECS_PER_DAY;
 #endif
+       }
+
+       return result;
+}
 
 static TimestampTz
 date2timestamptz(DateADT dateVal)
@@ -354,19 +429,30 @@ date2timestamptz(DateADT dateVal)
                           *tm = &tt;
        int                     tz;
 
-       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 = DetermineTimeZoneOffset(tm, global_timezone);
+       if (DATE_IS_NOBEGIN(dateVal))
+               TIMESTAMP_NOBEGIN(result);
+       else if (DATE_IS_NOEND(dateVal))
+               TIMESTAMP_NOEND(result);
+       else
+       {
+               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 = DetermineTimeZoneOffset(tm, session_timezone);
 
 #ifdef HAVE_INT64_TIMESTAMP
-       result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
+               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)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("date out of range for timestamp")));
 #else
-       result = dateVal * (double) SECS_PER_DAY + tz;
+               result = dateVal * (double) SECS_PER_DAY + tz;
 #endif
+       }
 
        return result;
 }
@@ -780,15 +866,19 @@ timestamp_date(PG_FUNCTION_ARGS)
                           *tm = &tt;
        fsec_t          fsec;
 
-       if (TIMESTAMP_NOT_FINITE(timestamp))
-               PG_RETURN_NULL();
-
-       if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, 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);
 }
@@ -823,15 +913,19 @@ timestamptz_date(PG_FUNCTION_ARGS)
        int                     tz;
        char       *tzn;
 
-       if (TIMESTAMP_NOT_FINITE(timestamp))
-               PG_RETURN_NULL();
-
-       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, 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, &tz, tm, &fsec, &tzn, 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);
 }
@@ -852,16 +946,19 @@ abstime_date(PG_FUNCTION_ARGS)
        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")));
+                       result = 0;                     /* keep compiler quiet */
+                       break;
+
+               case NOSTART_ABSTIME:
+                       DATE_NOBEGIN(result);
+                       break;
 
-                       /*
-                        * pretend to drop through to make compiler think that result will
-                        * be set
-                        */
+               case NOEND_ABSTIME:
+                       DATE_NOEND(result);
+                       break;
 
                default:
                        abstime2tm(abstime, &tz, tm, NULL);
@@ -928,8 +1025,10 @@ tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
 
 /* 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 pg_tm * tm, fsec_t *fsec)
@@ -999,8 +1098,18 @@ time_recv(PG_FUNCTION_ARGS)
 
 #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);
@@ -1029,7 +1138,7 @@ time_send(PG_FUNCTION_ARGS)
 Datum
 timetypmodin(PG_FUNCTION_ARGS)
 {
-       ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+       ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
 
        PG_RETURN_INT32(anytime_typmodin(false, ta));
 }
@@ -1037,7 +1146,7 @@ timetypmodin(PG_FUNCTION_ARGS)
 Datum
 timetypmodout(PG_FUNCTION_ARGS)
 {
-       int32 typmod = PG_GETARG_INT32(0);
+       int32           typmod = PG_GETARG_INT32(0);
 
        PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
 }
@@ -1197,6 +1306,17 @@ time_cmp(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(0);
 }
 
+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
 time_larger(PG_FUNCTION_ARGS)
 {
@@ -1422,9 +1542,9 @@ 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;
 
        PG_RETURN_TIMESTAMP(result);
 }
@@ -1564,15 +1684,15 @@ time_mi_interval(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;
        char       *lowunits;
 
-       lowunits = downcase_truncate_identifier(VARDATA(units),
-                                                                                       VARSIZE(units) - VARHDRSZ,
+       lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+                                                                                       VARSIZE_ANY_EXHDR(units),
                                                                                        false);
 
        type = DecodeUnits(0, lowunits, &val);
@@ -1591,7 +1711,7 @@ time_part(PG_FUNCTION_ARGS)
                {
                        case DTK_MICROSEC:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec * USECS_PER_SEC + fsec;
+                               result = tm->tm_sec * 1000000.0 + fsec;
 #else
                                result = (tm->tm_sec + fsec) * 1000000;
 #endif
@@ -1599,7 +1719,7 @@ time_part(PG_FUNCTION_ARGS)
 
                        case DTK_MILLISEC:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
+                               result = tm->tm_sec * 1000.0 + fsec / 1000.0;
 #else
                                result = (tm->tm_sec + fsec) * 1000;
 #endif
@@ -1607,7 +1727,7 @@ time_part(PG_FUNCTION_ARGS)
 
                        case DTK_SECOND:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec + fsec / USECS_PER_SEC;
+                               result = tm->tm_sec + fsec / 1000000.0;
 #else
                                result = tm->tm_sec + fsec;
 #endif
@@ -1636,9 +1756,7 @@ time_part(PG_FUNCTION_ARGS)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("\"time\" units \"%s\" not recognized",
-                                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                PointerGetDatum(units))))));
-
+                                                               lowunits)));
                                result = 0;
                }
        }
@@ -1655,8 +1773,7 @@ time_part(PG_FUNCTION_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"time\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                PointerGetDatum(units))))));
+                                               lowunits)));
                result = 0;
        }
 
@@ -1756,11 +1873,29 @@ timetz_recv(PG_FUNCTION_ARGS)
 
 #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)
+               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);
@@ -1788,7 +1923,7 @@ timetz_send(PG_FUNCTION_ARGS)
 Datum
 timetztypmodin(PG_FUNCTION_ARGS)
 {
-       ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+       ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
 
        PG_RETURN_INT32(anytime_typmodin(true, ta));
 }
@@ -1796,7 +1931,7 @@ timetztypmodin(PG_FUNCTION_ARGS)
 Datum
 timetztypmodout(PG_FUNCTION_ARGS)
 {
-       int32 typmod = PG_GETARG_INT32(0);
+       int32           typmod = PG_GETARG_INT32(0);
 
        PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
 }
@@ -1808,9 +1943,9 @@ timetztypmodout(PG_FUNCTION_ARGS)
 static int
 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
 {
-#ifdef HAVE_INT64_TIMESTAMP
-       int64           trem = time->time;
+       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;
@@ -1818,8 +1953,6 @@ timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
        tm->tm_sec = trem / USECS_PER_SEC;
        *fsec = trem - tm->tm_sec * USECS_PER_SEC;
 #else
-       double          trem = time->time;
-
 recalc:
        TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
        TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
@@ -1865,17 +1998,14 @@ timetz_scale(PG_FUNCTION_ARGS)
 static int
 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
 {
-       /* Primary sort is by true (GMT-equivalent) time */
-#ifdef HAVE_INT64_TIMESTAMP
-       int64           t1,
+       TimeOffset      t1,
                                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
-       double          t1,
-                               t2;
-
        t1 = time1->time + time1->zone;
        t2 = time2->time + time2->zone;
 #endif
@@ -1960,20 +2090,27 @@ 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.  This also provides a convenient
+        * framework for dealing with the fact that the time field might be either
+        * double or int64.
         */
-       return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->zone));
+#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);
 }
 
 Datum
@@ -2221,7 +2358,7 @@ time_timetz(PG_FUNCTION_ARGS)
 
        GetCurrentDateTime(tm);
        time2tm(time, tm, &fsec);
-       tz = DetermineTimeZoneOffset(tm, global_timezone);
+       tz = DetermineTimeZoneOffset(tm, session_timezone);
 
        result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
@@ -2275,11 +2412,18 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
        TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
        TimestampTz result;
 
+       if (DATE_IS_NOBEGIN(date))
+               TIMESTAMP_NOBEGIN(result);
+       else if (DATE_IS_NOEND(date))
+               TIMESTAMP_NOEND(result);
+       else
+       {
 #ifdef HAVE_INT64_TIMESTAMP
-       result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
+               result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
 #else
-       result = date * (double) SECS_PER_DAY + time->time + time->zone;
+               result = date * (double) SECS_PER_DAY + time->time + time->zone;
 #endif
+       }
 
        PG_RETURN_TIMESTAMP(result);
 }
@@ -2291,15 +2435,15 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
 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;
        char       *lowunits;
 
-       lowunits = downcase_truncate_identifier(VARDATA(units),
-                                                                                       VARSIZE(units) - VARHDRSZ,
+       lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
+                                                                                       VARSIZE_ANY_EXHDR(units),
                                                                                        false);
 
        type = DecodeUnits(0, lowunits, &val);
@@ -2335,7 +2479,7 @@ timetz_part(PG_FUNCTION_ARGS)
 
                        case DTK_MICROSEC:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec * USECS_PER_SEC + fsec;
+                               result = tm->tm_sec * 1000000.0 + fsec;
 #else
                                result = (tm->tm_sec + fsec) * 1000000;
 #endif
@@ -2343,7 +2487,7 @@ timetz_part(PG_FUNCTION_ARGS)
 
                        case DTK_MILLISEC:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
+                               result = tm->tm_sec * 1000.0 + fsec / 1000.0;
 #else
                                result = (tm->tm_sec + fsec) * 1000;
 #endif
@@ -2351,7 +2495,7 @@ timetz_part(PG_FUNCTION_ARGS)
 
                        case DTK_SECOND:
 #ifdef HAVE_INT64_TIMESTAMP
-                               result = tm->tm_sec + fsec / USECS_PER_SEC;
+                               result = tm->tm_sec + fsec / 1000000.0;
 #else
                                result = tm->tm_sec + fsec;
 #endif
@@ -2376,9 +2520,7 @@ timetz_part(PG_FUNCTION_ARGS)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                errmsg("\"time with time zone\" units \"%s\" not recognized",
-                                          DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                PointerGetDatum(units))))));
-
+                                          lowunits)));
                                result = 0;
                }
        }
@@ -2395,9 +2537,7 @@ timetz_part(PG_FUNCTION_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"time with time zone\" units \"%s\" not recognized",
-                                               DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                PointerGetDatum(units))))));
-
+                                               lowunits)));
                result = 0;
        }
 
@@ -2411,46 +2551,45 @@ timetz_part(PG_FUNCTION_ARGS)
 Datum
 timetz_zone(PG_FUNCTION_ARGS)
 {
-       text       *zone = PG_GETARG_TEXT_P(0);
+       text       *zone = PG_GETARG_TEXT_PP(0);
        TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
        TimeTzADT  *result;
        int                     tz;
        char            tzname[TZ_STRLEN_MAX + 1];
-       int                     len;
+       char       *lowzone;
+       int                     type,
+                               val;
        pg_tz      *tzp;
 
        /*
-        * Look up the requested timezone.      First we look in the timezone database
-        * (to handle cases like "America/New_York"), and if that fails, we look
-        * in the date token table (to handle cases like "EST").
+        * 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.)
         */
-       len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
-       memcpy(tzname, VARDATA(zone), len);
-       tzname[len] = '\0';
-       tzp = pg_tzset(tzname);
-       if (tzp)
-       {
-               /* Get the offset-from-GMT that is valid today for the selected zone */
-               pg_time_t       now;
-               struct pg_tm *tm;
+       text_to_cstring_buffer(zone, tzname, sizeof(tzname));
+       lowzone = downcase_truncate_identifier(tzname,
+                                                                                  strlen(tzname),
+                                                                                  false);
 
-               now = time(NULL);
-               tm = pg_localtime(&now, tzp);
-               tz = -tm->tm_gmtoff;
-       }
+       type = DecodeSpecial(0, lowzone, &val);
+
+       if (type == TZ || type == DTZ)
+               tz = val * 60;
        else
        {
-               char       *lowzone;
-               int                     type,
-                                       val;
-
-               lowzone = downcase_truncate_identifier(VARDATA(zone),
-                                                                                          VARSIZE(zone) - VARHDRSZ,
-                                                                                          false);
-               type = DecodeSpecial(0, lowzone, &val);
+               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;
 
-               if (type == TZ || type == DTZ)
-                       tz = val * 60;
+                       tm = pg_localtime(&now, tzp);
+                       tz = -tm->tm_gmtoff;
+               }
                else
                {
                        ereport(ERROR,