]> granicus.if.org Git - postgresql/commitdiff
Fix the various forms of AT TIME ZONE to accept either timezones found
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 Sep 2005 02:31:50 +0000 (02:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 Sep 2005 02:31:50 +0000 (02:31 +0000)
in the zic database or zone names found in the date token table.  This
preserves the old ability to do AT TIME ZONE 'PST' along with the new
ability to do AT TIME ZONE 'PST8PDT'.  Per gripe from Bricklen Anderson.
Also, fix some inconsistencies in usage of TZ_STRLEN_MAX --- the old
code had the potential for one-byte buffer overruns, though given
alignment considerations it's unlikely there was any real risk.

doc/src/sgml/datetime.sgml
doc/src/sgml/func.sgml
src/backend/utils/adt/date.c
src/backend/utils/adt/timestamp.c
src/include/pgtime.h
src/timezone/pgtz.c

index 9610dc36c5cada51e4d6c12f98bb37c2fcf75e24..2cfda15f097491ad195d3be18988e512683fc4e2 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.47 2005/09/09 02:31:48 tgl Exp $
 -->
 
  <appendix id="datetime-appendix">
@@ -990,9 +990,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian
    <para>
     <xref linkend="datetime-timezone-set-table"> shows the time zone
     names recognized by <productname>PostgreSQL</productname> as valid
-    settings for the <xref linkend="guc-timezone"> parameter, and as
-       parameters to the <literal>AT TIME ZONE function</> (see 
-       <xref linkend="functions-datetime-zoneconvert">).  Note that
+    settings for the <xref linkend="guc-timezone"> parameter.  Note that
     these names are conceptually as well as practically different from
     the names shown in <xref linkend="datetime-timezone-input-table">:
     most of these names imply a local daylight-savings time rule, whereas
@@ -1006,7 +1004,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian
    </para>
 
     <table id="datetime-timezone-set-table">
-     <title>Time Zone Names for Setting <varname>timezone</> and <literal>AT TIME ZONE</></title>
+     <title>Time Zone Names for Setting <varname>timezone</></title>
      <tgroup cols="1">
       <thead>
        <row>
index 9bef5e137839cac9d1eb5cb768972f6c6818bed0..41ca4a8cc9b7aa28745c39f8f22e876c3736f1e8 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.283 2005/08/25 01:29:55 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.284 2005/09/09 02:31:48 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -5730,9 +5730,9 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
     In these expressions, the desired time zone <replaceable>zone</> can be
     specified either as a text string (e.g., <literal>'PST'</literal>)
     or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>).
-    In the text case, the available zone names are those shown in
-    <xref linkend="datetime-timezone-set-table">.  The time zone can
-    also be implied using the default time zone for that session.
+    In the text case, the available zone names are those shown in either
+    <xref linkend="datetime-timezone-set-table"> or
+    <xref linkend="datetime-timezone-input-table">.
    </para>
 
    <para>
index 477d7993e6ff048c63d69b6268aaa2f00532d762..b36ee180929b628e0dcf87f216cb9a74bc1e5d45 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.119 2005/07/23 14:25:33 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.120 2005/09/09 02:31:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2484,37 +2484,53 @@ timetz_zone(PG_FUNCTION_ARGS)
        TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
        TimeTzADT  *result;
        int                     tz;
-       char        tzname[TZ_STRLEN_MAX];
+       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;
+       /*
+        * 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;
+       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));
+       result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
        
-       tz = -tm->tm_gmtoff;
 #ifdef HAVE_INT64_TIMESTAMP
        result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
        while (result->time < INT64CONST(0))
index 2b15e64e06172a6c0b21b4bc379b67baab3dd86d..b2d6f774c2fb9f59ab21cd0b01f409a5436c2adc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.151 2005/08/25 05:01:43 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.152 2005/09/09 02:31:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1014,7 +1014,7 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
  *      0 on success
  *     -1 on out of range
  *
- * If attimezone is NULL, the global timezone (including possblly brute forced
+ * If attimezone is NULL, the global timezone (including possibly brute forced
  * timezone) will be used.
  */
 int
@@ -1113,8 +1113,8 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn,
        utime = (pg_time_t) dt;
        if ((Timestamp) utime == dt)
        {
-               struct pg_tm *tx = pg_localtime(&utime, (attimezone != NULL) ?
-                                                                               attimezone : global_timezone);
+               struct pg_tm *tx = pg_localtime(&utime,
+                                                                               attimezone ? attimezone : global_timezone);
 
                tm->tm_year = tx->tm_year + 1900;
                tm->tm_mon = tx->tm_mon + 1;
@@ -3948,48 +3948,64 @@ Datum
 timestamp_zone(PG_FUNCTION_ARGS)
 {
        text       *zone = PG_GETARG_TEXT_P(0);
-       Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
+       Timestamp       timestamp = PG_GETARG_TIMESTAMP(1);
        TimestampTz     result;
        int                     tz;
        pg_tz      *tzp;
-       char        tzname[TZ_STRLEN_MAX+1];
+       char        tzname[TZ_STRLEN_MAX + 1];
        int         len;
-       struct pg_tm tm;
-       fsec_t      fsec;
-       bool            fail;
        
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_TIMESTAMPTZ(timestamp);
 
-       /* Find the specified timezone */
-       len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ?
-                       TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
+       /*
+        * 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;
+       tzname[len] = '\0';
        tzp = pg_tzset(tzname);
-       if (!tzp)
+       if (tzp)
        {
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone \"%s\" not recognised",
-                                       tzname)));
-               PG_RETURN_NULL();
-       }
+               /* Apply the timezone change */
+               struct pg_tm tm;
+               fsec_t      fsec;
 
-       /* Apply the timezone change */
-       fail = (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0);
-       if (!fail)
-       {
+               if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("timestamp out of range")));
                tz = DetermineTimeZoneOffset(&tm, tzp);
-               fail = (tm2timestamp(&tm, fsec, &tz, &result) != 0);
+               if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("could not convert to time zone \"%s\"",
+                                                       tzname)));
        }
-       if (fail)
+       else
        {
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("could not convert to time zone \"%s\"",
-                                       tzname)));
-               PG_RETURN_NULL();
+               char       *lowzone;
+               int                     type,
+                                       val;
+
+               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 = dt2local(timestamp, tz);
        }
 
        PG_RETURN_TIMESTAMPTZ(result);
@@ -4109,37 +4125,59 @@ timestamptz_zone(PG_FUNCTION_ARGS)
        Timestamp       result;
        int                     tz;
        pg_tz      *tzp;
-       char        tzname[TZ_STRLEN_MAX];
+       char        tzname[TZ_STRLEN_MAX + 1];
        int         len;
-       struct pg_tm tm;
-       fsec_t      fsec = 0;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
-               PG_RETURN_NULL();
+               PG_RETURN_TIMESTAMP(timestamp);
 
-       /* Find the specified zone */
-       len = (VARSIZE(zone) - VARHDRSZ > TZ_STRLEN_MAX) ?
-                       TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
+       /*
+        * 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;
+       tzname[len] = '\0';
        tzp = pg_tzset(tzname);
-
-       if (!tzp)
+       if (tzp)
        {
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("time zone \"%s\" not recognized", tzname)));
+               /* Apply the timezone change */
+               struct pg_tm tm;
+               fsec_t      fsec;
 
-               PG_RETURN_NULL();
+               if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("timestamp out of range")));
+               if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("could not convert to time zone \"%s\"",
+                                                       tzname)));
        }
+       else
+       {
+               char       *lowzone;
+               int                     type,
+                                       val;
 
-       if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
-           tm2timestamp(&tm, fsec, NULL, &result))
-       { 
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                       errmsg("could not to convert to time zone \"%s\"", tzname)));
-               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 = dt2local(timestamp, tz);
        }
 
        PG_RETURN_TIMESTAMP(result);
index ab4bdef1f6813eb892b3ce4ad7097ee18ef7ccc5..95f21393939e0373c9db4b574257abb955139c73 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/include/pgtime.h,v 1.9 2005/07/22 03:46:34 momjian Exp $
+ *       $PostgreSQL: pgsql/src/include/pgtime.h,v 1.10 2005/09/09 02:31:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,7 +58,7 @@ extern const char *pg_get_timezone_name(pg_tz *tz);
 
 extern pg_tz *global_timezone;
 
-/* Maximum length of a timezone name */
+/* Maximum length of a timezone name (not including trailing null) */
 #define TZ_STRLEN_MAX 255
 
 #endif   /* _PGTIME_H */
index 305bea2e5ebda486f0b4a8bdf68c6ae7a583412b..2512061222accb1704ccced1fa852364a8843106 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.36 2005/06/26 23:32:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.37 2005/09/09 02:31:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -974,7 +974,7 @@ init_timezone_hashtable(void)
 
        MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 
-       hash_ctl.keysize = TZ_STRLEN_MAX;
+       hash_ctl.keysize = TZ_STRLEN_MAX + 1;
        hash_ctl.entrysize = sizeof(pg_tz);
 
        timezone_cache = hash_create("Timezones",
@@ -997,7 +997,7 @@ pg_tzset(const char *name)
        pg_tz *tzp;
        pg_tz tz;
        
-       if (strlen(name) >= TZ_STRLEN_MAX)
+       if (strlen(name) > TZ_STRLEN_MAX)
                return NULL;                    /* not going to fit */
 
        if (!timezone_cache)