I wrote: > So the idea I'm toying with (again, haven't tried to code this) is to say > that *if* we can match the abbreviation to something in the referenced > zone then we'll use that, but otherwise we fall back to treating the > abbreviation as a macro for the zone name.
This turned out to require only fairly localized code changes. So I now propose that we leave the tznames files as-is and apply the attached patch (plus documentation changes, which I've not done yet). Any objections? regards, tom lane
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 965c3b4..45ba7cd 100644 *** a/src/backend/utils/adt/datetime.c --- b/src/backend/utils/adt/datetime.c *************** static void AdjustFractDays(double frac, *** 56,63 **** int scale); static int DetermineTimeZoneOffsetInternal(struct pg_tm * tm, pg_tz *tzp, pg_time_t *tp); ! static int DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, ! pg_tz *tzp, int *isdst); static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp); --- 56,64 ---- int scale); static int DetermineTimeZoneOffsetInternal(struct pg_tm * tm, pg_tz *tzp, pg_time_t *tp); ! static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, ! const char *abbr, pg_tz *tzp, ! int *offset, int *isdst); static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp); *************** overflow: *** 1689,1707 **** * This differs from the behavior of DetermineTimeZoneOffset() in that a * standard-time or daylight-time abbreviation forces use of the corresponding * GMT offset even when the zone was then in DS or standard time respectively. */ int DetermineTimeZoneAbbrevOffset(struct pg_tm * tm, const char *abbr, pg_tz *tzp) { pg_time_t t; /* * Compute the UTC time we want to probe at. (In event of overflow, we'll * probe at the epoch, which is a bit random but probably doesn't matter.) */ ! (void) DetermineTimeZoneOffsetInternal(tm, tzp, &t); ! return DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, &tm->tm_isdst); } --- 1690,1729 ---- * This differs from the behavior of DetermineTimeZoneOffset() in that a * standard-time or daylight-time abbreviation forces use of the corresponding * GMT offset even when the zone was then in DS or standard time respectively. + * (However, that happens only if we can match the given abbreviation to some + * abbreviation that appears in the IANA timezone data. Otherwise, we fall + * back to doing DetermineTimeZoneOffset().) */ int DetermineTimeZoneAbbrevOffset(struct pg_tm * tm, const char *abbr, pg_tz *tzp) { pg_time_t t; + int zone_offset; + int abbr_offset; + int abbr_isdst; /* * Compute the UTC time we want to probe at. (In event of overflow, we'll * probe at the epoch, which is a bit random but probably doesn't matter.) */ ! zone_offset = DetermineTimeZoneOffsetInternal(tm, tzp, &t); ! /* ! * Try to match the abbreviation to something in the zone definition. ! */ ! if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, ! &abbr_offset, &abbr_isdst)) ! { ! /* Success, so use the abbrev-specific answers. */ ! tm->tm_isdst = abbr_isdst; ! return abbr_offset; ! } ! ! /* ! * No match, so use the answers we already got from ! * DetermineTimeZoneOffsetInternal. ! */ ! return zone_offset; } *************** DetermineTimeZoneAbbrevOffsetTS(Timestam *** 1715,1733 **** pg_tz *tzp, int *isdst) { pg_time_t t = timestamptz_to_time_t(ts); ! return DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, isdst); } /* DetermineTimeZoneAbbrevOffsetInternal() * * Workhorse for above two functions: work from a pg_time_t probe instant. ! * DST status is returned into *isdst. */ ! static int ! DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, ! pg_tz *tzp, int *isdst) { char upabbr[TZ_STRLEN_MAX + 1]; unsigned char *p; --- 1737,1777 ---- pg_tz *tzp, int *isdst) { pg_time_t t = timestamptz_to_time_t(ts); + int zone_offset; + int abbr_offset; + int tz; + struct pg_tm tm; + fsec_t fsec; ! /* ! * If the abbrev matches anything in the zone data, this is pretty easy. ! */ ! if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, ! &abbr_offset, isdst)) ! return abbr_offset; ! ! /* ! * Else, break down the timestamp so we can use DetermineTimeZoneOffset. ! */ ! if (timestamp2tm(ts, &tz, &tm, &fsec, NULL, tzp) != 0) ! ereport(ERROR, ! (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), ! errmsg("timestamp out of range"))); ! ! zone_offset = DetermineTimeZoneOffset(&tm, tzp); ! *isdst = tm.tm_isdst; ! return zone_offset; } /* DetermineTimeZoneAbbrevOffsetInternal() * * Workhorse for above two functions: work from a pg_time_t probe instant. ! * On success, return GMT offset and DST status into *offset and *isdst. */ ! static bool ! DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp, ! int *offset, int *isdst) { char upabbr[TZ_STRLEN_MAX + 1]; unsigned char *p; *************** DetermineTimeZoneAbbrevOffsetInternal(pg *** 1739,1756 **** *p = pg_toupper(*p); /* Look up the abbrev's meaning at this time in this zone */ ! if (!pg_interpret_timezone_abbrev(upabbr, ! &t, ! &gmtoff, ! isdst, ! tzp)) ! ereport(ERROR, ! (errcode(ERRCODE_CONFIG_FILE_ERROR), ! errmsg("time zone abbreviation \"%s\" is not used in time zone \"%s\"", ! abbr, pg_get_timezone_name(tzp)))); ! ! /* Change sign to agree with DetermineTimeZoneOffset() */ ! return (int) -gmtoff; } --- 1783,1799 ---- *p = pg_toupper(*p); /* Look up the abbrev's meaning at this time in this zone */ ! if (pg_interpret_timezone_abbrev(upabbr, ! &t, ! &gmtoff, ! isdst, ! tzp)) ! { ! /* Change sign to agree with DetermineTimeZoneOffset() */ ! *offset = (int) -gmtoff; ! return true; ! } ! return false; } diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 67f26db..2bfc13a 100644 *** a/src/test/regress/expected/timestamptz.out --- b/src/test/regress/expected/timestamptz.out *************** SELECT '2007-12-09 07:30:00 UTC'::timest *** 2603,2605 **** --- 2603,2625 ---- Sun Dec 09 03:00:00 2007 (1 row) + -- + -- Test that the pg_timezone_names and pg_timezone_abbrevs views are + -- more-or-less working. We can't test their contents in any great detail + -- without the outputs changing anytime IANA updates the underlying data, + -- but it seems reasonable to expect at least one entry per major meridian. + -- (At the time of writing, the actual counts are around 38 because of + -- zones using fractional GMT offsets, so this is a pretty loose test.) + -- + select count(distinct utc_offset) >= 24 as ok from pg_timezone_names; + ok + ---- + t + (1 row) + + select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; + ok + ---- + t + (1 row) + diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index c023095..ce9d1c2 100644 *** a/src/test/regress/sql/timestamptz.sql --- b/src/test/regress/sql/timestamptz.sql *************** SELECT '2007-12-09 07:00:00 UTC'::timest *** 468,470 **** --- 468,481 ---- SELECT '2007-12-09 07:00:01 UTC'::timestamptz AT TIME ZONE 'VET'; SELECT '2007-12-09 07:29:59 UTC'::timestamptz AT TIME ZONE 'VET'; SELECT '2007-12-09 07:30:00 UTC'::timestamptz AT TIME ZONE 'VET'; + + -- + -- Test that the pg_timezone_names and pg_timezone_abbrevs views are + -- more-or-less working. We can't test their contents in any great detail + -- without the outputs changing anytime IANA updates the underlying data, + -- but it seems reasonable to expect at least one entry per major meridian. + -- (At the time of writing, the actual counts are around 38 because of + -- zones using fractional GMT offsets, so this is a pretty loose test.) + -- + select count(distinct utc_offset) >= 24 as ok from pg_timezone_names; + select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers