[PATCH 0/5] libstdc++: chrono tzdb correctness fixes
Álvaro Begué
alvaro.begue@gmail.com
Tue Apr 28 13:47:00 GMT 2026
I prepared a "v2" series of patches, and I included the DCO sign-off line.
Will this suffice?
--Á.
On Mon, Apr 13, 2026 at 10:24 AM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Sat, 11 Apr 2026 at 14:33, Alvaro Begue <alvaro.begue@gmail.com> wrote:
> >
> > This series fixes five distinct correctness issues in libstdc++'s
> > <chrono> tzdb implementation that cause time-zone conversions to
> > disagree with zic.c / libc localtime_r for specific real-world zones.
>
> This is really great, thanks. Some of the smaller patches might be
> possible to merge now for GCC 16 but the larger ones will have to wait
> for GCC 17 (and then maybe get backported later for GCC 16.2).
>
> For all significant patches to GCC we require either copyright
> assignment to the FSF or a DCO sign-off, please see
> https://gcc.gnu.org/contribute.html#legal and let us know which route
> you want to take.
>
>
> >
> > The motivation was a brute-force comparison harness that walks every
> > IANA zone hourly across 1850-2150 (~563k transition samples) and
> > compares chrono::time_zone::to_local() against libc's localtime_r().
> > Against current master HEAD that harness reports thousands of
> > mismatches across ~190 zones; with this series applied it reports
> > zero mismatches across all 447 zones. An abbreviation-only sweep
> > (169k samples) likewise reports zero offset and zero abbrev diffs.
> >
> > The series builds on Jonathan Wakely's recent PR 116110 / PR 124513
> > work (commits 663e5ade1, cddf4111c, fbc5d2b1a). Patch 3 in
> > particular resolves the "FIXME: PR 116110" left in operator>>(istream&,
> > ZoneInfo&) for the named-rule wall-UNTIL case.
> >
> > The five commits are deliberately small and independently testable;
> > each adds a hermetic regression test using the same
> > __gnu_cxx::zoneinfo_dir_override pattern as the existing 124513.cc.
> >
> > 1. Fix numeric save offset on Zone lines [PR 124851].
> >
> > ZoneInfo::m_offset had inconsistent semantics: the parser path
> > stored stdoff alone, but the two sys_info-taking constructors
> > stored the total (stdoff + save). ZoneInfo::to() returned
> > m_offset as sys_info::offset, dropping the numeric save for
> > parser-path lines like Africa/Gaborone's "2 1 CAST" middle line.
> > Normalize m_offset to stdoff alone everywhere; to() adds save
> > back when reconstructing.
> >
> > 2. Support ON-format DAY in Zone UNTIL field [PR 124852].
> >
> > The UNTIL parser only accepted a plain integer as the DAY,
> > silently misparsing tzdata.zi entries like Europe/Simferopol's
> > "1997 Mar lastSu 1u". Reuse the on_day machinery and the
> > parse_on_day_body helper.
> >
> > 3. Resolve named-rule UNTIL save adjustment [PR 116110].
> >
> > The remaining FIXME in operator>>(istream&, ZoneInfo&) for
> > wall-time UNTILs on named-rule zone lines. At parse time the
> > active rule cannot be evaluated (rule records aren't all
> > loaded yet), so the parser leaves the SAVE adjustment pending
> > and a new fixup pass in reload_tzdb walks every pending
> > ZoneInfo and applies the adjustment using a new
> > find_pre_until_rule helper with iterative-boundary cascade
> > semantics.
> >
> > A new bit (m_until_save_pending, stolen from m_pos) marks
> > pending entries. The seeding code is also updated to use
> > `t = info.begin + 1s` so a rule firing at exactly info.begin
> > is included.
> >
> > Removes the +11h workaround from test_apia in 116110.cc.
> >
> > 4. Cascade wall-time saves in lazy expansion seeding [PR 124853].
> >
> > The seeding code in _M_get_sys_info interpreted each rule in
> > isolation against ri.offset() (the line's standard offset
> > alone), ignoring the running save accumulated by earlier rules
> > in the same year. For zones whose rule set has wall-time
> > rules whose effective firing time depends on a prior rule's
> > save (Europe/Paris around 1945), this gives a wrong answer.
> >
> > Replace with a chronological cascade walker (matching zic.c's
> > outzone() logic) that maintains a running save and interprets
> > each Wall-time rule's at_time relative to the cascaded state.
> >
> > 5. Implement zic writezone merge optimization [PR 124854].
> >
> > Two distinct fixes that together let lazy expansion match
> > zic.c's writezone output for zones with rule firings near
> > zone-line boundaries:
> >
> > a. Always seed info.offset and info.save from find_active_rule,
> > not just when letters is empty. Previously, partial-
> > expansion re-entry left info.offset/save at their
> > (ri.offset(), 0) init values because the seeding was
> > skipped when letters was already populated from
> > i[-1].next_letters(). This caused zones like Europe/Berlin
> > around 1947-06-29 to emit a 2-hour CEST sys_info with
> > offset=3600 / save=0 — observably wrong.
> >
> > b. Add the writezone merge optimization itself: when adjacent
> > zone lines have different total offsets and the new line's
> > rule set has a rule firing within |jump| of the boundary
> > (where jump is a backward local-time jump), fold that rule
> > into the boundary transition. Canonical cases handled:
> > America/Argentina/Buenos_Aires 1999-10-03 and Europe/Berlin
> > 1945-05-24.
> >
> > Test plan:
> > * make check-target-libstdc++-v3
> RUNTESTFLAGS="conformance.exp=std/time/*"
> > -- all existing chrono tests pass, plus the five new ones added
> > by this series.
> > * The comparison harness reports zero mismatches across all 447
> > zones (563600 samples).
> > * The abbrev sweep reports zero offset and zero abbrev diffs
> > (169080 samples).
> >
> > Alvaro Begue (5):
> > libstdc++: Fix numeric save offset on Zone lines [PR 124851]
> > libstdc++: Support ON-format DAY in Zone UNTIL field [PR 124852]
> > libstdc++: Resolve named-rule UNTIL save adjustment [PR116110]
> > libstdc++: Cascade wall-time saves in lazy expansion seeding [PR
> > 124853]
> > libstdc++: Implement zic writezone merge optimization [PR 124854]
> >
> > libstdc++-v3/src/c++20/tzdb.cc | 464 ++++++++++++++----
> > .../testsuite/std/time/time_zone/116110.cc | 7 +-
> > .../std/time/time_zone/numeric_save.cc | 66 +++
> > .../std/time/time_zone/pr116110_named.cc | 106 ++++
> > .../std/time/time_zone/until_day_on.cc | 177 +++++++
> > .../std/time/time_zone/wall_cascade.cc | 87 ++++
> > .../std/time/time_zone/zone_merge.cc | 101 ++++
> > 7 files changed, 920 insertions(+), 88 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/until_day_on.cc
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/zone_merge.cc
> >
> > --
> > 2.34.1
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260428/b4300674/attachment.htm>
More information about the Libstdc++
mailing list