Skip to content

ZoneInfo.utcoffset() gives the wrong offset near the last explicit TZif transition #159708

Description

@pganssle

Bug report

import datetime
import zoneinfo
from zoneinfo import _zoneinfo

# Requires tzdata==2026.5; use its slim TZif files.
zoneinfo.reset_tzpath([])
for implementation in (zoneinfo, _zoneinfo):
    tz = implementation.ZoneInfo.no_cache("America/Nuuk")
    local = datetime.datetime(2023, 10, 28, 23, 30, tzinfo=tz)
    print(implementation.__name__)
    print(local)
    print(local.astimezone(datetime.UTC).astimezone(tz))

Output:

zoneinfo
2023-10-28 23:30:00-01:00
2023-10-28 22:30:00-02:00
zoneinfo._zoneinfo
2023-10-28 23:30:00-01:00
2023-10-28 22:30:00-02:00

I would expect both lines to show 2023-10-28 23:30:00-02:00. Nuuk stayed on UTC−02 through the end of 2023, so this is an ordinary, existing local time, but both implementations give it the wrong offset and change the wall time when it is converted to UTC and back.

The last explicit transition in this file is at 2023-10-29 01:00 UTC, and the footer is <-02>2<-01>,M3.5.0/-1,M10.5.0/0. Extrapolating those recurring rules backwards creates a fold from 23:00 to 00:00 on October 28, with UTC−01 as the offset for fold=0. However, 23:30 with that offset corresponds to 00:30 UTC, which is before the last explicit transition, where the file specifies UTC−02.

Both the pure-Python lookup and the C lookup select the footer based on the local transition boundary without checking whether its answer implies a UTC instant before the footer applies. I think the lookup needs to account for that boundary rather than applying the footer's apparent fold to these local times.

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