]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/date.c
pgindent run for 8.3.
[postgresql] / src / backend / utils / adt / date.c
index 828ca9ec7c9738dd6c95e02260856e5c795a05ba..0f2b44b356979c849b19cc47f83640035f4f552b 100644 (file)
@@ -3,12 +3,12 @@
  * date.c
  *       implements DATE and TIME data types specified in SQL-92 standard
  *
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, 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.115 2005/07/21 04:41:43 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.137 2007/11/15 21:14:38 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <ctype.h>
 #include <limits.h>
 #include <float.h>
-#include <time.h> 
+#include <time.h>
 
 #include "access/hash.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/nabstime.h"
-#include "utils/timestamp.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -44,6 +44,61 @@ 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           typmod;
+       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")));
+
+       if (*tl < 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("TIME(%d)%s precision must not be negative",
+                                               *tl, (istz ? " WITH TIME ZONE" : ""))));
+       if (*tl > MAX_TIME_PRECISION)
+       {
+               ereport(WARNING,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
+                                               *tl, (istz ? " WITH TIME ZONE" : ""),
+                                               MAX_TIME_PRECISION)));
+               typmod = MAX_TIME_PRECISION;
+       }
+       else
+               typmod = *tl;
+
+       return typmod;
+}
+
+/* common code for timetypmodout and timetztypmodout */
+static char *
+anytime_typmodout(bool istz, int32 typmod)
+{
+       char       *res = (char *) palloc(64);
+       const char *tz = istz ? " with time zone" : " without time zone";
+
+       if (typmod >= 0)
+               snprintf(res, 64, "(%d)%s", (int) typmod, tz);
+       else
+               snprintf(res, 64, "%s", tz);
+       return res;
+}
+
+
 /*****************************************************************************
  *      Date ADT
  *****************************************************************************/
@@ -56,7 +111,7 @@ Datum
 date_in(PG_FUNCTION_ARGS)
 {
        char       *str = PG_GETARG_CSTRING(0);
-       DateADT date;
+       DateADT         date;
        fsec_t          fsec;
        struct pg_tm tt,
                           *tm = &tt;
@@ -83,7 +138,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;
@@ -97,6 +152,11 @@ date_in(PG_FUNCTION_ARGS)
                        break;
        }
 
+       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;
 
        PG_RETURN_DATEADT(date);
@@ -108,13 +168,13 @@ date_in(PG_FUNCTION_ARGS)
 Datum
 date_out(PG_FUNCTION_ARGS)
 {
-       DateADT date = PG_GETARG_DATEADT(0);
+       DateADT         date = PG_GETARG_DATEADT(0);
        char       *result;
        struct pg_tm tt,
                           *tm = &tt;
        char            buf[MAXDATELEN + 1];
 
-       j2date(date +POSTGRES_EPOCH_JDATE,
+       j2date(date + POSTGRES_EPOCH_JDATE,
                   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
 
        EncodeDateOnly(tm, DateStyle, buf);
@@ -140,7 +200,7 @@ date_recv(PG_FUNCTION_ARGS)
 Datum
 date_send(PG_FUNCTION_ARGS)
 {
-       DateADT date = PG_GETARG_DATEADT(0);
+       DateADT         date = PG_GETARG_DATEADT(0);
        StringInfoData buf;
 
        pq_begintypsend(&buf);
@@ -277,16 +337,27 @@ date_mii(PG_FUNCTION_ARGS)
  * time zone
  */
 
+static Timestamp
+date2timestamp(DateADT dateVal)
+{
+       Timestamp       result;
+
 #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 must 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)
 {
@@ -301,12 +372,17 @@ date2timestamptz(DateADT dateVal)
        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 * USECS_PER_DAY + tz * USECS_PER_SEC;
+       /* Date's range is wider than timestamp's, so must 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;
@@ -715,7 +791,7 @@ date_timestamp(PG_FUNCTION_ARGS)
 Datum
 timestamp_date(PG_FUNCTION_ARGS)
 {
-       Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
+       Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
        DateADT         result;
        struct pg_tm tt,
                           *tm = &tt;
@@ -797,11 +873,11 @@ abstime_date(PG_FUNCTION_ARGS)
                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")));
 
                        /*
-                        * pretend to drop through to make compiler think that result
-                        * will be set
+                        * pretend to drop through to make compiler think that result will
+                        * be set
                         */
 
                default:
@@ -814,64 +890,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
  *****************************************************************************/
@@ -917,10 +935,10 @@ static int
 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
 {
 #ifdef HAVE_INT64_TIMESTAMP
-       *result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
-                               * USECS_PER_SEC) + fsec;
+       *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
+                          * USECS_PER_SEC) + fsec;
 #else
-       *result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+       *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
 #endif
        return 0;
 }
@@ -944,10 +962,18 @@ time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
 #else
        double          trem;
 
+recalc:
        trem = time;
-       TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
-       TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
+       TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
+       TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
        TMODULO(trem, tm->tm_sec, 1.0);
+       trem = TIMEROUND(trem);
+       /* roundoff may need to propagate to higher-order fields */
+       if (trem >= 1.0)
+       {
+               time = ceil(time);
+               goto recalc;
+       }
        *fsec = trem;
 #endif
 
@@ -981,6 +1007,7 @@ Datum
 time_recv(PG_FUNCTION_ARGS)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
+
 #ifdef NOT_USED
        Oid                     typelem = PG_GETARG_OID(1);
 #endif
@@ -1016,6 +1043,22 @@ time_send(PG_FUNCTION_ARGS)
        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));
+}
+
 
 /* time_scale()
  * Adjust time type for specified scale factor.
@@ -1064,7 +1107,6 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
                INT64CONST(5),
                INT64CONST(0)
        };
-
 #else
        /* note MAX_TIME_PRECISION differs in this case */
        static const double TimeScales[MAX_TIME_PRECISION + 1] = {
@@ -1085,26 +1127,21 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
        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?
+                * 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];
-               }
+                               TimeScales[typmod];
                else
-               {
                        *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
-                                       TimeScales[typmod]);
-               }
+                                         TimeScales[typmod]);
 #else
-               *time = rint((double)*time * TimeScales[typmod])
-                                / TimeScales[typmod];
+               *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
 #endif
        }
 }
@@ -1177,6 +1214,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)
 {
@@ -1205,8 +1253,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);
@@ -1223,9 +1271,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)
        {
@@ -1273,8 +1321,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();
@@ -1284,8 +1332,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);
        }
@@ -1300,8 +1348,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);
        }
@@ -1309,8 +1357,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();
@@ -1327,7 +1374,7 @@ overlaps_time(PG_FUNCTION_ARGS)
 Datum
 timestamp_time(PG_FUNCTION_ARGS)
 {
-       Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
+       Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
        TimeADT         result;
        struct pg_tm tt,
                           *tm = &tt;
@@ -1347,10 +1394,10 @@ timestamp_time(PG_FUNCTION_ARGS)
         * Could also do this with time = (timestamp / USECS_PER_DAY *
         * USECS_PER_DAY) - timestamp;
         */
-       result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
-                               USECS_PER_SEC) + fsec;
+       result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+                         USECS_PER_SEC) + fsec;
 #else
-       result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+       result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
 #endif
 
        PG_RETURN_TIMEADT(result);
@@ -1384,10 +1431,10 @@ timestamptz_time(PG_FUNCTION_ARGS)
         * Could also do this with time = (timestamp / USECS_PER_DAY *
         * USECS_PER_DAY) - timestamp;
         */
-       result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
-                               USECS_PER_SEC) + fsec;
+       result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+                         USECS_PER_SEC) + fsec;
 #else
-       result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+       result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
 #endif
 
        PG_RETURN_TIMEADT(result);
@@ -1399,12 +1446,12 @@ timestamptz_time(PG_FUNCTION_ARGS)
 Datum
 datetime_timestamp(PG_FUNCTION_ARGS)
 {
-       DateADT date = PG_GETARG_DATEADT(0);
+       DateADT         date = PG_GETARG_DATEADT(0);
        TimeADT         time = PG_GETARG_TIMEADT(1);
        Timestamp       result;
 
        result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
-                                                                                                DateADTGetDatum(date)));
+                                                                                                  DateADTGetDatum(date)));
        result += time;
 
        PG_RETURN_TIMESTAMP(result);
@@ -1458,8 +1505,8 @@ interval_time(PG_FUNCTION_ARGS)
        }
 #else
        result = span->time;
-       if (result >= (double)SECS_PER_DAY || result < 0)
-               result -= floor(result / (double)SECS_PER_DAY) * (double)SECS_PER_DAY;
+       if (result >= (double) SECS_PER_DAY || result < 0)
+               result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
 #endif
 
        PG_RETURN_TIMEADT(result);
@@ -1503,7 +1550,7 @@ time_pl_interval(PG_FUNCTION_ARGS)
        TimeADT         time1;
 
        result = time + span->time;
-       TMODULO(result, time1, (double)SECS_PER_DAY);
+       TMODULO(result, time1, (double) SECS_PER_DAY);
        if (result < 0)
                result += SECS_PER_DAY;
 #endif
@@ -1530,7 +1577,7 @@ time_mi_interval(PG_FUNCTION_ARGS)
        TimeADT         time1;
 
        result = time - span->time;
-       TMODULO(result, time1, (double)SECS_PER_DAY);
+       TMODULO(result, time1, (double) SECS_PER_DAY);
        if (result < 0)
                result += SECS_PER_DAY;
 #endif
@@ -1539,65 +1586,6 @@ time_mi_interval(PG_FUNCTION_ARGS)
 }
 
 
-/* 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.
  */
@@ -1671,12 +1659,13 @@ 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))))));
+                                                               DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                PointerGetDatum(units))))));
 
                                result = 0;
                }
@@ -1695,7 +1684,7 @@ time_part(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"time\" units \"%s\" not recognized",
                                                DatumGetCString(DirectFunctionCall1(textout,
-                                                                                        PointerGetDatum(units))))));
+                                                                                                PointerGetDatum(units))))));
                result = 0;
        }
 
@@ -1714,10 +1703,10 @@ static int
 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
 {
 #ifdef HAVE_INT64_TIMESTAMP
-       result->time = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+       result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
                                        USECS_PER_SEC) + fsec;
 #else
-       result->time = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+       result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
 #endif
        result->zone = tz;
 
@@ -1784,6 +1773,7 @@ Datum
 timetz_recv(PG_FUNCTION_ARGS)
 {
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
+
 #ifdef NOT_USED
        Oid                     typelem = PG_GETARG_OID(1);
 #endif
@@ -1823,6 +1813,22 @@ timetz_send(PG_FUNCTION_ARGS)
        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.
@@ -1842,9 +1848,17 @@ timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
 #else
        double          trem = time->time;
 
-       TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
-       TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
+recalc:
+       TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
+       TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
        TMODULO(trem, tm->tm_sec, 1.0);
+       trem = TIMEROUND(trem);
+       /* roundoff may need to propagate to higher-order fields */
+       if (trem >= 1.0)
+       {
+               trem = ceil(time->time);
+               goto recalc;
+       }
        *fsec = trem;
 #endif
 
@@ -1974,20 +1988,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
@@ -2041,7 +2062,7 @@ timetz_pl_interval(PG_FUNCTION_ARGS)
                result->time += USECS_PER_DAY;
 #else
        result->time = time->time + span->time;
-       TMODULO(result->time, time1.time, (double)SECS_PER_DAY);
+       TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
        if (result->time < 0)
                result->time += SECS_PER_DAY;
 #endif
@@ -2074,7 +2095,7 @@ timetz_mi_interval(PG_FUNCTION_ARGS)
                result->time += USECS_PER_DAY;
 #else
        result->time = time->time - span->time;
-       TMODULO(result->time, time1.time, (double)SECS_PER_DAY);
+       TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
        if (result->time < 0)
                result->time += SECS_PER_DAY;
 #endif
@@ -2094,8 +2115,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);
@@ -2112,9 +2133,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)
        {
@@ -2162,8 +2183,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();
@@ -2173,8 +2194,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);
        }
@@ -2189,8 +2210,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);
        }
@@ -2198,8 +2219,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();
@@ -2236,7 +2256,7 @@ time_timetz(PG_FUNCTION_ARGS)
 
        GetCurrentDateTime(tm);
        time2tm(time, tm, &fsec);
-       tz = DetermineLocalTimeZone(tm);
+       tz = DetermineTimeZoneOffset(tm, session_timezone);
 
        result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
@@ -2286,79 +2306,20 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
 Datum
 datetimetz_timestamptz(PG_FUNCTION_ARGS)
 {
-       DateADT date = PG_GETARG_DATEADT(0);
+       DateADT         date = PG_GETARG_DATEADT(0);
        TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
        TimestampTz result;
 
 #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);
 }
 
 
-/* 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));
-
-       pfree(str);
-
-       PG_RETURN_TEXT_P(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.
  */
@@ -2399,12 +2360,12 @@ timetz_part(PG_FUNCTION_ARGS)
                        case DTK_TZ_MINUTE:
                                result = -tz;
                                result /= SECS_PER_MINUTE;
-                               FMODULO(result, dummy, (double)SECS_PER_MINUTE);
+                               FMODULO(result, dummy, (double) SECS_PER_MINUTE);
                                break;
 
                        case DTK_TZ_HOUR:
                                dummy = -tz;
-                               FMODULO(dummy, result, (double)SECS_PER_HOUR);
+                               FMODULO(dummy, result, (double) SECS_PER_HOUR);
                                break;
 
                        case DTK_MICROSEC:
@@ -2449,9 +2410,9 @@ 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",
+                                          DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                PointerGetDatum(units))))));
 
                                result = 0;
                }
@@ -2468,9 +2429,9 @@ 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))))));
+                                errmsg("\"time with time zone\" units \"%s\" not recognized",
+                                               DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                PointerGetDatum(units))))));
 
                result = 0;
        }
@@ -2489,37 +2450,53 @@ timetz_zone(PG_FUNCTION_ARGS)
        TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
        TimeTzADT  *result;
        int                     tz;
-       char        tzname[TZ_STRLEN_MAX];
-       int         len;
+       char            tzname[TZ_STRLEN_MAX + 1];
+       int                     len;
        pg_tz      *tzp;
-       struct pg_tm *tm;
-       pg_time_t   now;
-
-       /* Find the specified timezone */ 
-       len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX) ?
-                                       TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
-       memcpy(tzname,VARDATA(zone),len);
-       tzname[len]=0;
+
+       /*
+        * 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").
+        */
+       len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
+       memcpy(tzname, VARDATA(zone), len);
+       tzname[len] = '\0';
        tzp = pg_tzset(tzname);
-       if (!tzp) {
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("time zone \"%s\" not recognized", tzname)));
-               PG_RETURN_NULL();
+       if (tzp)
+       {
+               /* Get the offset-from-GMT that is valid today for the selected zone */
+               pg_time_t       now;
+               struct pg_tm *tm;
+
+               now = time(NULL);
+               tm = pg_localtime(&now, tzp);
+               tz = -tm->tm_gmtoff;
        }
+       else
+       {
+               char       *lowzone;
+               int                     type,
+                                       val;
 
-       /* Get the offset-from-GMT that is valid today for the selected zone */
-       if ((now = time(NULL)) < 0 ||
-           (tm = pg_localtime(&now, tzp)) == NULL) {
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("could not determine current time")));
-               PG_RETURN_NULL();
+               lowzone = downcase_truncate_identifier(VARDATA(zone),
+                                                                                          VARSIZE(zone) - VARHDRSZ,
+                                                                                          false);
+               type = DecodeSpecial(0, lowzone, &val);
+
+               if (type == TZ || type == DTZ)
+                       tz = val * 60;
+               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));
-       
-       tz = -tm->tm_gmtoff;
+       result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+
 #ifdef HAVE_INT64_TIMESTAMP
        result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
        while (result->time < INT64CONST(0))
@@ -2537,7 +2514,7 @@ timetz_zone(PG_FUNCTION_ARGS)
        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.
@@ -2555,7 +2532,7 @@ timetz_izone(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("\"interval\" time zone \"%s\" not valid",
                                                DatumGetCString(DirectFunctionCall1(interval_out,
-                                                                                         PointerGetDatum(zone))))));
+                                                                                                 PointerGetDatum(zone))))));
 
 #ifdef HAVE_INT64_TIMESTAMP
        tz = -(zone->time / USECS_PER_SEC);
@@ -2582,4 +2559,4 @@ timetz_izone(PG_FUNCTION_ARGS)
        result->zone = tz;
 
        PG_RETURN_TIMETZADT_P(result);
-}      /* timetz_izone() */
+}