Repository navigation
Conversation
Previously the libc path in init_timezone() was gated on HAVE_DECL_TZNAME, which configure only defines when struct tm lacks tm_zone. On Linux and macOS this meant timezone, altzone, daylight and tzname were guessed by sampling localtime() near January 1 and July 1 of the current year. That gives stale values when a zone's rules change mid-year (e.g. America/Vancouver moving to permanent MST on 2026-11-01) and wrong standard/DST names for negative DST zones. Add a configure check, HAVE_TIMEZONE_GLOBALS, that verifies timezone, daylight and tzname are usable as variables, and use libc's values whenever it is defined. FreeBSD, where timezone is a function, keeps the sampling fallback. When libc has no altzone, use timezone - 3600 only if daylight is set, otherwise timezone.
| ADD_INT("altzone", _Py_timezone-3600); | ||
| /* libc does not provide the DST offset. Assume a one hour shift when | ||
| the zone has DST, otherwise use the standard offset. */ | ||
| ADD_INT("altzone", _Py_daylight ? _Py_timezone - 3600 : _Py_timezone); |
There was a problem hiding this comment.
In glibc daylight does not mean "this zone has DST now", it means DST appears somewhere in the zone's transition history. The glibc manual says:
This variable is nonzero if daylight saving time rules apply. A nonzero value does not necessarily mean that daylight saving time is now in effect; it means only that daylight saving time is sometimes in effect. This variable has little or no practical use; it is present for POSIX compatibility.
There was a problem hiding this comment.
Oh, that's too bad. I pushed a change that uses a sampling approach (look at dates this year looking for DST offset). I'm not thrilled about that solution but I can't think of a better one.
We can't use the _Py_daylight flag to determine the DST offset, it only says if the zone has DST rules. Loop through the current year months, looking for DST offset.
Documentation build overview
11 files changed ·
|
Rewrite test_strptime.test_timezone to check each tzname entry and treat UTC/GMT as standard time, so zones with negative DST such as Europe/Dublin pass. Add a round-trip test with fixed EST5EDT rules. Document the glibc tzset() early-return caveat in init_timezone(). In get_dst_offset(), skip a normalized sample date instead of abandoning the remaining months, and drop a dead tm_mon range check. Expand the NEWS entry to cover the negative DST classification change, daylight being nonzero for historical DST, and altzone sampling on glibc and Windows.
The libc path in init_timezone() was gated on HAVE_DECL_TZNAME, which configure only checks when struct tm lacks tm_zone. On Linux and macOS, this caused timezone constants to be estimated from January and July samples, producing stale values after mid-year rule changes and incorrect standard/DST classification for negative DST zones.
Add _Py_HAVE_TIMEZONE_GLOBALS to detect and use libc’s timezone, daylight, and tzname variables. Platforms without these globals retain the existing fallback.
When libc lacks altzone, estimate it using calendar-month samples rather than assuming a one-hour DST shift. Prefer the latest sampled DST offset at or before now, looking ahead only if none was found. Sampling is best-effort: failures retain the last valid offset, or the standard offset if none was found.
Note: creation of this PR was assisted by an LLM.