Andrew pointed out that the current fix didn't handle dates that were
authorBruce Momjian
Sat, 23 Jul 2005 14:25:34 +0000 (14:25 +0000)
committerBruce Momjian
Sat, 23 Jul 2005 14:25:34 +0000 (14:25 +0000)
near daylight savings time boudaries.  This handles it properly, e.g.

        test=> select '2005-04-03 04:00:00'::timestamp at time zone
        'America/Los_Angeles';
                timezone
        ------------------------
         2005-04-03 07:00:00-04
        (1 row)

src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/formatting.c
src/backend/utils/adt/nabstime.c
src/backend/utils/adt/timestamp.c
src/include/utils/datetime.h

index fdb3942f4ffaed8526c0352697b303232e1161f8..477d7993e6ff048c63d69b6268aaa2f00532d762 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.118 2005/07/22 05:03:09 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.119 2005/07/23 14:25:33 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -301,7 +301,7 @@ date2timestamptz(DateADT dateVal)
    tm->tm_hour = 0;
    tm->tm_min = 0;
    tm->tm_sec = 0;
-   tz = DetermineLocalTimeZone(tm);
+   tz = DetermineTimeZoneOffset(tm, global_timezone);
 
 #ifdef HAVE_INT64_TIMESTAMP
    result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
@@ -2231,7 +2231,7 @@ time_timetz(PG_FUNCTION_ARGS)
 
    GetCurrentDateTime(tm);
    time2tm(time, tm, &fsec);
-   tz = DetermineLocalTimeZone(tm);
+   tz = DetermineTimeZoneOffset(tm, global_timezone);
 
    result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
index 04ec7cb216ad6b2be5397bd02fc35e86aed962ef..9e5be7d4cff90b3b93881334b954bc7cdfc3f06d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.156 2005/07/22 03:46:33 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.157 2005/07/23 14:25:33 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1612,7 +1612,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
            if (fmask & DTK_M(DTZMOD))
                return DTERR_BAD_FORMAT;
 
-           *tzp = DetermineLocalTimeZone(tm);
+           *tzp = DetermineTimeZoneOffset(tm, global_timezone);
        }
    }
 
@@ -1620,10 +1620,10 @@ DecodeDateTime(char **field, int *ftype, int nf,
 }
 
 
-/* DetermineLocalTimeZone()
+/* DetermineTimeZoneOffset()
  *
  * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
- * tm_sec fields are set, attempt to determine the applicable local zone
+ * tm_sec fields are set, attempt to determine the applicable time zone
  * (ie, regular or daylight-savings time) at that time.  Set the struct pg_tm's
  * tm_isdst field accordingly, and return the actual timezone offset.
  *
@@ -1632,7 +1632,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
  * of mktime(), anyway.
  */
 int
-DetermineLocalTimeZone(struct pg_tm *tm)
+DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
 {
    int         date,
                sec;
@@ -1648,7 +1648,7 @@ DetermineLocalTimeZone(struct pg_tm *tm)
                after_isdst;
    int         res;
 
-   if (HasCTZSet)
+   if (tzp == global_timezone && HasCTZSet)
    {
        tm->tm_isdst = 0;       /* for lack of a better idea */
        return CTimeZone;
@@ -1687,7 +1687,7 @@ DetermineLocalTimeZone(struct pg_tm *tm)
                               &before_gmtoff, &before_isdst,
                               &boundary,
                               &after_gmtoff, &after_isdst,
-                              global_timezone);
+                              tzp);
    if (res < 0)
        goto overflow;          /* failure? */
 
@@ -2282,7 +2282,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
        tmp->tm_hour = tm->tm_hour;
        tmp->tm_min = tm->tm_min;
        tmp->tm_sec = tm->tm_sec;
-       *tzp = DetermineLocalTimeZone(tmp);
+       *tzp = DetermineTimeZoneOffset(tmp, global_timezone);
        tm->tm_isdst = tmp->tm_isdst;
    }
 
index 9f1cc1a476863bdb7a57d385a3311f3c0485fbfc..b473b9b13b4f388c24cb9b9053246b0adfa4c0a4 100644 (file)
@@ -1,7 +1,7 @@
 /* -----------------------------------------------------------------------
  * formatting.c
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.92 2005/07/21 03:56:16 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.93 2005/07/23 14:25:33 momjian Exp $
  *
  *
  *  Portions Copyright (c) 1999-2005, PostgreSQL Global Development Group
@@ -2989,7 +2989,7 @@ to_timestamp(PG_FUNCTION_ARGS)
 
    do_to_timestamp(date_txt, fmt, &tm, &fsec);
 
-   tz = DetermineLocalTimeZone(&tm);
+   tz = DetermineTimeZoneOffset(&tm, global_timezone);
 
    if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
        ereport(ERROR,
index d4d9d25e48d289354125dab0653a2302be2f11e5..739345b7baad50e9827ac7f3ef867574133fe89c 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.141 2005/07/22 19:55:50 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.142 2005/07/23 14:25:33 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -474,7 +474,7 @@ timestamp_abstime(PG_FUNCTION_ARGS)
        result = NOEND_ABSTIME;
    else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
    {
-       tz = DetermineLocalTimeZone(tm);
+       tz = DetermineTimeZoneOffset(tm, global_timezone);
        result = tm2abstime(tm, tz);
    }
    else
index 999f8fbf522ee23e08bddd53edf8b7f6a97820cf..da7a87a1bb89c5cd0049a7a3b728025c2c25ee67 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.143 2005/07/23 02:02:27 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.144 2005/07/23 14:25:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2100,7 +2100,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
            if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
                tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
 
-           tz = DetermineLocalTimeZone(tm);
+           tz = DetermineTimeZoneOffset(tm, global_timezone);
 
            if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0)
                ereport(ERROR,
@@ -2124,7 +2124,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
            julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
            j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
 
-           tz = DetermineLocalTimeZone(tm);
+           tz = DetermineTimeZoneOffset(tm, global_timezone);
 
            if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0)
                ereport(ERROR,
@@ -3104,7 +3104,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
        }
 
        if (redotz)
-           tz = DetermineLocalTimeZone(tm);
+           tz = DetermineTimeZoneOffset(tm, global_timezone);
 
        if (tm2timestamp(tm, fsec, &tz, &result) != 0)
            ereport(ERROR,
@@ -3529,7 +3529,7 @@ timestamp_part(PG_FUNCTION_ARGS)
                           (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                            errmsg("timestamp out of range")));
 
-                   tz = DetermineLocalTimeZone(tm);
+                   tz = DetermineTimeZoneOffset(tm, global_timezone);
 
                    if (tm2timestamp(tm, fsec, &tz, ×tamptz) != 0)
                        ereport(ERROR,
@@ -3924,12 +3924,11 @@ interval_part(PG_FUNCTION_ARGS)
 
 /*     timestamp_zone()
  *     Encode timestamp type with specified time zone.
- *     Returns timestamp with time zone, with the input
- * rotated from local time to the specified zone.
- * This function is tricky because instead of shifting
- * the time _to_ a new time zone, it sets the time to _be_
- * the specified timezone.  This requires trickery
- * of double-subtracting the requested timezone offset.
+ *     This function is just timestamp2timestamptz() except instead of
+ * shifting to the global timezone, we shift to the specified timezone.
+ * This is different from the other AT TIME ZONE cases because instead
+ * of shifting to a _to_ a new time zone, it sets the time to _be_ the
+ * specified timezone.
  */
 Datum
 timestamp_zone(PG_FUNCTION_ARGS)
@@ -3943,11 +3942,12 @@ timestamp_zone(PG_FUNCTION_ARGS)
    int         len;
    struct pg_tm tm;
    fsec_t      fsec;
-
+   bool        fail;
+   
    if (TIMESTAMP_NOT_FINITE(timestamp))
        PG_RETURN_TIMESTAMPTZ(timestamp);
 
-   /* Find the specified timezone? */
+   /* Find the specified timezone */
    len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ?
            TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
    memcpy(tzname, VARDATA(zone), len);
@@ -3963,8 +3963,13 @@ timestamp_zone(PG_FUNCTION_ARGS)
    }
 
    /* Apply the timezone change */
-   if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
-       tm2timestamp(&tm, fsec, &tz, &result) != 0)
+   fail = (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0);
+   if (!fail)
+   {
+       tz = DetermineTimeZoneOffset(&tm, tzp);
+       fail = (tm2timestamp(&tm, fsec, &tz, &result) != 0);
+   }
+   if (fail)
    {
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -3972,8 +3977,6 @@ timestamp_zone(PG_FUNCTION_ARGS)
                        tzname)));
        PG_RETURN_NULL();
    }
-   /* Must double-adjust for timezone */
-   result = dt2local(result, -tz);
 
    PG_RETURN_TIMESTAMPTZ(result);
 }
@@ -4039,7 +4042,7 @@ timestamp2timestamptz(Timestamp timestamp)
                    (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                     errmsg("timestamp out of range")));
 
-       tz = DetermineLocalTimeZone(tm);
+       tz = DetermineTimeZoneOffset(tm, global_timezone);
 
        if (tm2timestamp(tm, fsec, &tz, &result) != 0)
            ereport(ERROR,
index 2e7155d803053e380e19209109f58b5e8393e0bb..572a9852f70ce9ad1f368df3aa2c9d424a1c1567 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.55 2005/07/22 03:46:34 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.56 2005/07/23 14:25:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -291,7 +291,7 @@ extern int DecodeInterval(char **field, int *ftype,
 extern void DateTimeParseError(int dterr, const char *str,
                   const char *datatype);
 
-extern int DetermineLocalTimeZone(struct pg_tm *tm);
+extern int DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp);
 
 extern int EncodeDateOnly(struct pg_tm *tm, int style, char *str);
 extern int EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, int *tzp, int style, char *str);