Skip to content

libast/tm: DST offset and contemporaneous time zone fixes.#1008

Open
pghvlaans wants to merge 9 commits into
ksh93:devfrom
pghvlaans:dst_offset
Open

libast/tm: DST offset and contemporaneous time zone fixes.#1008
pghvlaans wants to merge 9 commits into
ksh93:devfrom
pghvlaans:dst_offset

Conversation

@pghvlaans

@pghvlaans pghvlaans commented Jul 16, 2026

Copy link
Copy Markdown

RE #976, 98800e9

Pre-u+m code in tminit.c sets the standard (ST) and daylight savings time (DST) offsets by checking times over the previous year of the time requested to compare DST status and offsets from UTC. If no offset change is detected, the DST offset is set equal to the ST offset.

Although assigning the DST offset in this way is counter-intuitive, it does not result in incorrect times being reported so long as two implicit assumptions are met:

  • If the requested time is DST, a change from ST occurred in the previous year.
  • If the offset changed over the previous year, this was the result of a DST transition.

The first assumption fails on musl Linux systems (which would not appear until well after the code in question was written). musl interprets POSIX timezone specifications with a DST name but without rules as being DST year-round. tminit.c therefore calculates both the DST and ST offsets as equal to the overall UTC offset, which is applied twice when reporting times. Because musl apparently disregards the top zoneinfo directory, even common aliases such as PST8PDT return wildly inaccurate results (rather than the one-hour errors that /bin/date would suggest on musl systems).

The second assumption is broken at all times and places between a historical timezone change and a DST shift or one year later, whichever comes first. Although Pacific/Apia between 2011-12-31 and 2012-04-01 yields the most striking results, there are many other examples.

Fixing these problems involves relatively minor changes to tminit.c and related tests:

  • In tminit.c, save and restore the UTC offset and DST status of the requested time; only break offset calculations for a DST shift.
  • In attributes.sh, use full POSIX timezone specifications in lieu of EST5EDT and PST8PDT. This is necessary because musl applies DST for the entire year and the time of the test is ST.
  • In printf.sh, add several TZ tests to account for different combinations of DST and ST designations across historical timezone changes. Also add an EST5EDT test in DST.

In addition, although 98800e9 introduced a fix for looking up times in zones with historical changes, it applied only to searches using seconds from the epoch. Other lookup formats still fail to change to contemporaneous zones. A convenient reproducer is:

TZ=Europe/London printf "%(%s)T\n" "1970-01-01 01:00:00"

The output should be 0 and not 3600.

  • In tmxdate.c, after running through tmxdate for the first time, compare the values of west, dst and isdst to ensure that a proper zone has been chosen. If the zones differ, this means that a historical change has occurred. Apply an offset and run again, repeating as necessary.
  • In printf.sh, add 'reverse' tests looking up epoch seconds by date.

@pghvlaans

Copy link
Copy Markdown
Author

Tested on Slackware64 -current (glibc), Void (musl), FreeBSD, OpenBSD, omnios (attributes and printf only) and NetBSD. I don't currently have a macOS VM.

@pghvlaans pghvlaans changed the title libast/tm/tminit.c: Fix DST offsets libast/tm/tminit.c: Time fixes. Jul 18, 2026
@pghvlaans pghvlaans changed the title libast/tm/tminit.c: Time fixes. libast/tm: DST offset and contemporaneous time zone fixes. Jul 18, 2026
@pghvlaans

Copy link
Copy Markdown
Author

8b003db broadens the scope a bit. I can revert it and make a different PR after this one is merged if that would be more convenient.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant