Skip to content

ZoneInfo evaluates zero-based TZ day-of-year rules one day early #159712

Description

@pganssle

Bug report

import datetime
import io
import struct
import zoneinfo
from zoneinfo import _zoneinfo


def tzif(footer: bytes) -> io.BytesIO:
    # A TZif v3 file with no explicit transitions.
    header = b"TZif3" + b"\0" * 15 + struct.pack(">6l", 0, 0, 0, 0, 1, 4)
    body = struct.pack(">lbb", 0, 0, 0) + b"STD\0"
    return io.BytesIO(header + body + header + body + b"\n" + footer + b"\n")


for implementation in (zoneinfo, _zoneinfo):
    tz = implementation.ZoneInfo.from_file(tzif(b"STD0DST-1,10/0,J100/0"))
    print(implementation.__name__)
    for day in (9, 10, 11):
        local = datetime.datetime(2021, 1, day, 12, tzinfo=tz)
        print(local.date(), local.tzname())

Output:

zoneinfo
2021-01-09 STD
2021-01-10 DST
2021-01-11 DST
zoneinfo._zoneinfo
2021-01-09 STD
2021-01-10 DST
2021-01-11 DST

I would expect January 10 to still be STD, with DST starting at midnight on January 11. The unprefixed n form is a zero-based day of the year, so 0 means January 1 and 10 means January 11. Both implementations instead start DST on January 10. glibc's tzset() gives STD for January 10 with the same TZ string.

The pure-Python calculation uses a year baseline one day before January 1 and adds the day number directly. That works for the one-based Jn form, but leaves the zero-based n form one day early. The C calculation has the same extra day subtracted from the baseline.

This is related to but distinct from #152847, which fixed validation of non-digit day fields. The input here is valid; the resulting transition date is wrong.

Linked PRs

Activity

  1. added 3 commits that reference this issue on Oct 19, 2026
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