Andrew pointed out that the current fix didn't handle dates that were

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)
This commit is contained in:
Bruce Momjian 2005-07-23 14:25:34 +00:00
parent 6c61b0d93c
commit 3dbbbbf8e9
6 changed files with 38 additions and 35 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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_hour = 0;
tm->tm_min = 0; tm->tm_min = 0;
tm->tm_sec = 0; tm->tm_sec = 0;
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC; result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
@ -2231,7 +2231,7 @@ time_timetz(PG_FUNCTION_ARGS)
GetCurrentDateTime(tm); GetCurrentDateTime(tm);
time2tm(time, tm, &fsec); time2tm(time, tm, &fsec);
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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)) if (fmask & DTK_M(DTZMOD))
return DTERR_BAD_FORMAT; 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 * 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 * (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. * tm_isdst field accordingly, and return the actual timezone offset.
* *
@ -1632,7 +1632,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
* of mktime(), anyway. * of mktime(), anyway.
*/ */
int int
DetermineLocalTimeZone(struct pg_tm *tm) DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
{ {
int date, int date,
sec; sec;
@ -1648,7 +1648,7 @@ DetermineLocalTimeZone(struct pg_tm *tm)
after_isdst; after_isdst;
int res; int res;
if (HasCTZSet) if (tzp == global_timezone && HasCTZSet)
{ {
tm->tm_isdst = 0; /* for lack of a better idea */ tm->tm_isdst = 0; /* for lack of a better idea */
return CTimeZone; return CTimeZone;
@ -1687,7 +1687,7 @@ DetermineLocalTimeZone(struct pg_tm *tm)
&before_gmtoff, &before_isdst, &before_gmtoff, &before_isdst,
&boundary, &boundary,
&after_gmtoff, &after_isdst, &after_gmtoff, &after_isdst,
global_timezone); tzp);
if (res < 0) if (res < 0)
goto overflow; /* failure? */ goto overflow; /* failure? */
@ -2282,7 +2282,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tmp->tm_hour = tm->tm_hour; tmp->tm_hour = tm->tm_hour;
tmp->tm_min = tm->tm_min; tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec; tmp->tm_sec = tm->tm_sec;
*tzp = DetermineLocalTimeZone(tmp); *tzp = DetermineTimeZoneOffset(tmp, global_timezone);
tm->tm_isdst = tmp->tm_isdst; tm->tm_isdst = tmp->tm_isdst;
} }

View File

@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * 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 * 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); do_to_timestamp(date_txt, fmt, &tm, &fsec);
tz = DetermineLocalTimeZone(&tm); tz = DetermineTimeZoneOffset(&tm, global_timezone);
if (tm2timestamp(&tm, fsec, &tz, &result) != 0) if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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; result = NOEND_ABSTIME;
else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
{ {
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
result = tm2abstime(tm, tz); result = tm2abstime(tm, tz);
} }
else else

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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]) 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]); 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, &timestamp) != 0) if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
ereport(ERROR, 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; julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0) if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
ereport(ERROR, ereport(ERROR,
@ -3104,7 +3104,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
} }
if (redotz) if (redotz)
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
if (tm2timestamp(tm, fsec, &tz, &result) != 0) if (tm2timestamp(tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,
@ -3529,7 +3529,7 @@ timestamp_part(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))); errmsg("timestamp out of range")));
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0) if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
ereport(ERROR, ereport(ERROR,
@ -3924,12 +3924,11 @@ interval_part(PG_FUNCTION_ARGS)
/* timestamp_zone() /* timestamp_zone()
* Encode timestamp type with specified time zone. * Encode timestamp type with specified time zone.
* Returns timestamp with time zone, with the input * This function is just timestamp2timestamptz() except instead of
* rotated from local time to the specified zone. * shifting to the global timezone, we shift to the specified timezone.
* This function is tricky because instead of shifting * This is different from the other AT TIME ZONE cases because instead
* the time _to_ a new time zone, it sets the time to _be_ * of shifting to a _to_ a new time zone, it sets the time to _be_ the
* the specified timezone. This requires trickery * specified timezone.
* of double-subtracting the requested timezone offset.
*/ */
Datum Datum
timestamp_zone(PG_FUNCTION_ARGS) timestamp_zone(PG_FUNCTION_ARGS)
@ -3943,11 +3942,12 @@ timestamp_zone(PG_FUNCTION_ARGS)
int len; int len;
struct pg_tm tm; struct pg_tm tm;
fsec_t fsec; fsec_t fsec;
bool fail;
if (TIMESTAMP_NOT_FINITE(timestamp)) if (TIMESTAMP_NOT_FINITE(timestamp))
PG_RETURN_TIMESTAMPTZ(timestamp); PG_RETURN_TIMESTAMPTZ(timestamp);
/* Find the specified timezone? */ /* Find the specified timezone */
len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ? len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ?
TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ; TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
memcpy(tzname, VARDATA(zone), len); memcpy(tzname, VARDATA(zone), len);
@ -3963,8 +3963,13 @@ timestamp_zone(PG_FUNCTION_ARGS)
} }
/* Apply the timezone change */ /* Apply the timezone change */
if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 || fail = (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0);
tm2timestamp(&tm, fsec, &tz, &result) != 0) if (!fail)
{
tz = DetermineTimeZoneOffset(&tm, tzp);
fail = (tm2timestamp(&tm, fsec, &tz, &result) != 0);
}
if (fail)
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -3972,8 +3977,6 @@ timestamp_zone(PG_FUNCTION_ARGS)
tzname))); tzname)));
PG_RETURN_NULL(); PG_RETURN_NULL();
} }
/* Must double-adjust for timezone */
result = dt2local(result, -tz);
PG_RETURN_TIMESTAMPTZ(result); PG_RETURN_TIMESTAMPTZ(result);
} }
@ -4039,7 +4042,7 @@ timestamp2timestamptz(Timestamp timestamp)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))); errmsg("timestamp out of range")));
tz = DetermineLocalTimeZone(tm); tz = DetermineTimeZoneOffset(tm, global_timezone);
if (tm2timestamp(tm, fsec, &tz, &result) != 0) if (tm2timestamp(tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, extern void DateTimeParseError(int dterr, const char *str,
const char *datatype); 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 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); extern int EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, int *tzp, int style, char *str);