diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index e647ac05ff..b6af6e7e25 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -327,6 +327,7 @@ check_timezone(char **newval, void **extra, GucSource source) #else myextra.CTimeZone = -interval->time; #endif + myextra.session_timezone = pg_tzset_offset(myextra.CTimeZone); myextra.HasCTZSet = true; pfree(interval); @@ -341,6 +342,7 @@ check_timezone(char **newval, void **extra, GucSource source) { /* Here we change from SQL to Unix sign convention */ myextra.CTimeZone = -hours * SECS_PER_HOUR; + myextra.session_timezone = pg_tzset_offset(myextra.CTimeZone); myextra.HasCTZSet = true; } else diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 48bf3db9b2..1b8f109992 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -1465,12 +1465,6 @@ DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp) after_isdst; int res; - if (tzp == session_timezone && HasCTZSet) - { - tm->tm_isdst = 0; /* for lack of a better idea */ - return CTimeZone; - } - /* * First, generate the pg_time_t value corresponding to the given * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the diff --git a/src/include/pgtime.h b/src/include/pgtime.h index 57af51e6bd..73a0ed2db7 100644 --- a/src/include/pgtime.h +++ b/src/include/pgtime.h @@ -68,6 +68,7 @@ extern pg_tz *log_timezone; extern void pg_timezone_initialize(void); extern pg_tz *pg_tzset(const char *tzname); +extern pg_tz *pg_tzset_offset(long gmtoffset); extern pg_tzenum *pg_tzenumerate_start(void); extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir); diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index 553a158e2c..2666deea88 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -2935,3 +2935,33 @@ DETAIL: Value must be an integer. SELECT to_timestamp('10000000000', 'FMYYYY'); ERROR: value for "YYYY" in source string is out of range DETAIL: Value must be in the range -2147483648 to 2147483647. +-- +-- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) +-- +SET TIME ZONE 'America/New_York'; +SET TIME ZONE '-1.5'; +SHOW TIME ZONE; + TimeZone +---------------------- + @ 1 hour 30 mins ago +(1 row) + +SELECT '2012-12-12 12:00'::timestamptz; + timestamptz +--------------------------------- + Wed Dec 12 12:00:00 2012 -01:30 +(1 row) + +SELECT '2012-12-12 12:00 America/New_York'::timestamptz; + timestamptz +--------------------------------- + Wed Dec 12 15:30:00 2012 -01:30 +(1 row) + +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); + to_char +---------------------- + 2012-12-12 12:00:00 +(1 row) + +RESET TIME ZONE; diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql index ea794ecdd9..fe9a520cb9 100644 --- a/src/test/regress/sql/horology.sql +++ b/src/test/regress/sql/horology.sql @@ -461,3 +461,19 @@ SELECT to_timestamp('199711xy', 'YYYYMMDD'); -- Input that doesn't fit in an int: SELECT to_timestamp('10000000000', 'FMYYYY'); + +-- +-- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) +-- + +SET TIME ZONE 'America/New_York'; +SET TIME ZONE '-1.5'; + +SHOW TIME ZONE; + +SELECT '2012-12-12 12:00'::timestamptz; +SELECT '2012-12-12 12:00 America/New_York'::timestamptz; + +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); + +RESET TIME ZONE; diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 203bda88c4..1130de9a96 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -288,6 +288,46 @@ pg_tzset(const char *name) return &tzp->tz; } +/* + * Load a fixed-GMT-offset timezone. + * This is used for SQL-spec SET TIME ZONE INTERVAL 'foo' cases. + * It's otherwise equivalent to pg_tzset(). + * + * The GMT offset is specified in seconds, positive values meaning west of + * Greenwich (ie, POSIX not ISO sign convention). However, we use ISO + * sign convention in the displayable abbreviation for the zone. + */ +pg_tz * +pg_tzset_offset(long gmtoffset) +{ + long absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset; + char offsetstr[64]; + char tzname[128]; + + snprintf(offsetstr, sizeof(offsetstr), + "%02ld", absoffset / SECSPERHOUR); + absoffset %= SECSPERHOUR; + if (absoffset != 0) + { + snprintf(offsetstr + strlen(offsetstr), + sizeof(offsetstr) - strlen(offsetstr), + ":%02ld", absoffset / SECSPERMIN); + absoffset %= SECSPERMIN; + if (absoffset != 0) + snprintf(offsetstr + strlen(offsetstr), + sizeof(offsetstr) - strlen(offsetstr), + ":%02ld", absoffset); + } + if (gmtoffset > 0) + snprintf(tzname, sizeof(tzname), "<-%s>+%s", + offsetstr, offsetstr); + else + snprintf(tzname, sizeof(tzname), "<+%s>-%s", + offsetstr, offsetstr); + + return pg_tzset(tzname); +} + /* * Initialize timezone library