Skip to content

time.timezone, altzone and tzname are stale after a zone's rules change mid-year #161194

Open

Description

@nascheme

Bug report

On Linux, the time module constants can disagree with both libc and time.localtime() when a zone's rules change during the current year. This now happens for America/Vancouver, which moved to permanent MST on 2026-11-01 (tzdata 2026f).

Reproduced on current main with glibc 2.41 and tzdata 2026f, on 2026-11-02:

$ TZ=America/Vancouver ./python -c "
import time
print(time.tzname, time.timezone, time.altzone, time.daylight)
lt = time.localtime()
print(lt.tm_zone, lt.tm_gmtoff, lt.tm_isdst)
time.strptime(time.strftime('%Z'), '%Z')
"
('PST', 'PDT') 28800 25200 1
MST -25200 0
Traceback (most recent call last):
  ...
ValueError: time data 'MST' does not match format '%Z'

The system data and libc are correct. A small C program calling tzset() reports:

tzname=MST/PDT timezone=25200 daylight=1
now zone=MST off=-25200 isdst=0

time.localtime() is also correct. Only the module constants are stale.

Cause

HAVE_DECL_TZNAME is defined only in PC/pyconfig.h, so on Linux init_timezone() in Modules/timemodule.c does not use libc's tzname and timezone. Its fallback instead samples the local time near January 1 and July 1 of the current year, and treats the larger offset as standard time.

Both samples are from before the change, so it reports PST and PDT.

Test failures

With the system zone set to America/Vancouver:

  • test_time: test_localtime_timezone fails with AssertionError: -25200 != -28800
  • test_strptime: StrptimeTests.test_timezone, LocaleTime_Tests.test_timezone and TimeRETests.test_compile fail because MST is not in time.tzname

test_strptime and test_time pass with TZ=America/Los_Angeles.

Because the fallback samples the current year, this should resolve itself around the start of 2027, when both samples become MST. That was checked by evaluating the sample dates, not by running with a changed clock. It will recur for any zone with a similar mid-year rule change.

Related

gh-83985 is in the same code but has a different trigger. There, the offset-based ordering gives the wrong result for negative DST: Europe/Dublin gets ('GMT', 'IST'), while glibc gives IST/GMT, which agrees with tm_isdst.

Possible fix

  1. Call tzset() on all platforms and take timezone and tzname from libc. This should address both this issue and timezone constants in time module inaccurate with negative DST (e.g. Ireland) #83985.
  2. Derive altzone from a sample with tm_isdst=1, because glibc does not provide altzone. timezone - 3600 would give 21600 here, but PDT is 25200.
  3. If no sample has DST, fall back to timezone.

glibc was excluded from the libc path historically, and a related 2018 simplification was reverted (bpo-34715), so the reason for that revert should be checked first. Any change also needs testing on musl, macOS and the BSDs.

Separately, test_localtime_timezone compares the constants against the live system zone. A single set of constants cannot describe every zone during a transition, so that check may be better done with a fixed TZ.

Note: this bug report was assisted by an LLM

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions