[PATCH 0/5] libstdc++: chrono tzdb correctness fixes

Jonathan Wakely jwakely@redhat.com
Mon Apr 13 14:23:59 GMT 2026


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
>



More information about the Libstdc++ mailing list