]> granicus.if.org Git - postgresql/commitdiff
Fix AT TIME ZONE (in all three variants) so that we first try to interpret
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 7 Jul 2008 18:10:12 +0000 (18:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 7 Jul 2008 18:10:12 +0000 (18:10 +0000)
the timezone argument as a timezone abbreviation, and only try it as a full
timezone name if that fails.  The zic database has four zones (CET, EET, MET,
WET) that are full daylight-savings zones and yet have names that are the
same as their abbreviations for standard time, resulting in ambiguity.
In the timestamp input functions we resolve the ambiguity by preferring the
abbreviation, and AT TIME ZONE should work the same way.  (No functionality
is lost because the zic database also has other names for these zones, eg
Europe/Zurich.)  Per gripe from Jaromir Talir.

Backpatch to 8.1.  Older releases did not have the issue because AT TIME ZONE
only accepted abbreviations not zone names.  (Thus, this patch also arguably
fixes a compatibility botch introduced at 8.1: in ambiguous cases we now
behave the same as 8.0 did.)

src/backend/utils/adt/date.c
src/backend/utils/adt/timestamp.c

index 6daeff9da7f0e33ee8df31b2ef10e45f0e23aac5..d7624a188e24238eac70d7216f852645997f1a13 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.122.2.2 2007/06/02 16:41:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.122.2.3 2008/07/07 18:10:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2509,40 +2509,42 @@ timetz_zone(PG_FUNCTION_ARGS)
        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;
+       lowzone = downcase_truncate_identifier(VARDATA(zone),
+                                                                                  VARSIZE(zone) - VARHDRSZ,
+                                                                                  false);
+       type = DecodeSpecial(0, lowzone, &val);
 
-               now = time(NULL);
-               tm = pg_localtime(&now, tzp);
-               tz = -tm->tm_gmtoff;
-       }
+       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);
+               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 zone */
+                       pg_time_t       now;
+                       struct pg_tm *tm;
 
-               if (type == TZ || type == DTZ)
-                       tz = val * 60;
+                       now = time(NULL);
+                       tm = pg_localtime(&now, tzp);
+                       tz = -tm->tm_gmtoff;
+               }
                else
                {
                        ereport(ERROR,
index b00caeee93c256ff73481a284990c4a7eab79faf..79e106c83bf7c3189ba4bc5c604df18b1fdbaa91 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157.2.2 2007/09/16 15:56:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157.2.3 2008/07/07 18:10:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4012,61 +4012,64 @@ timestamp_zone(PG_FUNCTION_ARGS)
        Timestamp       timestamp = PG_GETARG_TIMESTAMP(1);
        TimestampTz result;
        int                     tz;
-       pg_tz      *tzp;
        char            tzname[TZ_STRLEN_MAX + 1];
        int                     len;
+       char       *lowzone;
+       int                     type,
+                               val;
+       pg_tz      *tzp;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_TIMESTAMPTZ(timestamp);
 
        /*
-        * 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)
-       {
-               /* Apply the timezone change */
-               struct pg_tm tm;
-               fsec_t          fsec;
+       lowzone = downcase_truncate_identifier(VARDATA(zone),
+                                                                                  VARSIZE(zone) - VARHDRSZ,
+                                                                                  false);
+       type = DecodeSpecial(0, lowzone, &val);
 
-               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);
-               if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("could not convert to time zone \"%s\"",
-                                                       tzname)));
+       if (type == TZ || type == DTZ)
+       {
+               tz = -(val * 60);
+               result = dt2local(timestamp, tz);
        }
        else
        {
-               char       *lowzone;
-               int                     type,
-                                       val;
-
-               lowzone = downcase_truncate_identifier(VARDATA(zone),
-                                                                                          VARSIZE(zone) - VARHDRSZ,
-                                                                                          false);
-               type = DecodeSpecial(0, lowzone, &val);
+               len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
+               memcpy(tzname, VARDATA(zone), len);
+               tzname[len] = '\0';
+               tzp = pg_tzset(tzname);
+               if (tzp)
+               {
+                       /* Apply the timezone change */
+                       struct pg_tm tm;
+                       fsec_t          fsec;
 
-               if (type == TZ || type == DTZ)
-                       tz = -(val * 60);
+                       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);
+                       if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("could not convert to time zone \"%s\"",
+                                                               tzname)));
+               }
                else
                {
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("time zone \"%s\" not recognized", tzname)));
-                       tz = 0;                         /* keep compiler quiet */
+                       result = 0;                             /* keep compiler quiet */
                }
-
-               result = dt2local(timestamp, tz);
        }
 
        PG_RETURN_TIMESTAMPTZ(result);
@@ -4185,60 +4188,63 @@ timestamptz_zone(PG_FUNCTION_ARGS)
        TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
        Timestamp       result;
        int                     tz;
-       pg_tz      *tzp;
        char            tzname[TZ_STRLEN_MAX + 1];
        int                     len;
+       char       *lowzone;
+       int                     type,
+                               val;
+       pg_tz      *tzp;
 
        if (TIMESTAMP_NOT_FINITE(timestamp))
                PG_RETURN_TIMESTAMP(timestamp);
 
        /*
-        * 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)
-       {
-               /* Apply the timezone change */
-               struct pg_tm tm;
-               fsec_t          fsec;
+       lowzone = downcase_truncate_identifier(VARDATA(zone),
+                                                                                  VARSIZE(zone) - VARHDRSZ,
+                                                                                  false);
+       type = DecodeSpecial(0, lowzone, &val);
 
-               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)));
+       if (type == TZ || type == DTZ)
+       {
+               tz = val * 60;
+               result = dt2local(timestamp, tz);
        }
        else
        {
-               char       *lowzone;
-               int                     type,
-                                       val;
-
-               lowzone = downcase_truncate_identifier(VARDATA(zone),
-                                                                                          VARSIZE(zone) - VARHDRSZ,
-                                                                                          false);
-               type = DecodeSpecial(0, lowzone, &val);
+               len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
+               memcpy(tzname, VARDATA(zone), len);
+               tzname[len] = '\0';
+               tzp = pg_tzset(tzname);
+               if (tzp)
+               {
+                       /* Apply the timezone change */
+                       struct pg_tm tm;
+                       fsec_t          fsec;
 
-               if (type == TZ || type == DTZ)
-                       tz = val * 60;
+                       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
                {
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("time zone \"%s\" not recognized", tzname)));
-                       tz = 0;                         /* keep compiler quiet */
+                       result = 0;                             /* keep compiler quiet */
                }
-
-               result = dt2local(timestamp, tz);
        }
 
        PG_RETURN_TIMESTAMP(result);