[PATCH v2 1/5] libstdc++: Fix numeric save offset on Zone lines [PR124851]
Tomasz Kaminski
tkaminsk@redhat.com
Wed Jul 22 15:08:03 GMT 2026
On Mon, May 11, 2026 at 6:40 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Sun, 26 Apr 2026 at 19:51 -0400, Álvaro Begué wrote:
> >When a Zone line specifies a numeric value as its RULES field (the
> >constant DST save value for that zone line, e.g. Africa/Gaborone's
> >"2 1 CAST" line), the parser stored the standard offset alone in
> >ZoneInfo::m_offset. ZoneInfo::to() then returned that as
> >sys_info::offset, dropping the numeric save and reporting a total
> >offset that was wrong by the save amount.
> >
> >This was inconsistent with the two ZoneInfo constructors that take a
> >sys_info, which previously stored the *total* offset (stdoff + save) in
> >m_offset. As a result m_offset's semantics depended on which code path
> >created the ZoneInfo, and only the parser path's lines with non-zero
> >numeric save were observably broken.
> >
> >Fix by giving m_offset a single semantics: always the standard offset
> >only. The two sys_info-taking constructors now subtract the save before
> >storing, and to() adds it back when reconstructing the sys_info.
> >
> >The remaining .offset() callers inside _M_get_sys_info already expect
> >the standard offset (they are computing rule firing times, where the
> >save component is added separately from the active rule's save value),
> >so no other call sites need adjustment.
> >
> >libstdc++-v3/ChangeLog:
> >
> >PR libstdc++/124851
> >* src/c++20/tzdb.cc (ZoneInfo::ZoneInfo(sys_info&&)): Store
> >stdoff only in m_offset (subtract info.save).
> >(ZoneInfo::ZoneInfo(const pair<sys_info, string_view>&)):
> >Likewise.
> >(ZoneInfo::offset()): Document new semantics.
> >(ZoneInfo::to(sys_info&)): Add m_save back to offset() when
> >populating sys_info::offset.
> >* testsuite/std/time/time_zone/numeric_save.cc: New test.
> >
> >Signed-off-by: Álvaro Begué <alvaro.begue@gmail.com>
> >---
> > libstdc++-v3/src/c++20/tzdb.cc | 9 +--
> > .../std/time/time_zone/numeric_save.cc | 58 +++++++++++++++++++
> > 2 files changed, 63 insertions(+), 4 deletions(-)
> > create mode 100644
> >libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc
> >
> >diff --git a/libstdc++-v3/src/c++20/tzdb.cc
> b/libstdc++-v3/src/c++20/tzdb.cc
> >index b0fbfc46a..1e49bb749 100644
> >--- a/libstdc++-v3/src/c++20/tzdb.cc
> >+++ b/libstdc++-v3/src/c++20/tzdb.cc
> >@@ -478,11 +478,12 @@ namespace std::chrono
> >
> > ZoneInfo(sys_info&& info)
> > : m_buf(std::move(info.abbrev)), m_expanded(true),
> m_save(info.save),
> >- m_offset(info.offset), m_until(info.end)
> >+ m_offset(info.offset - seconds(info.save)), m_until(info.end)
> > { }
> >
> > ZoneInfo(const pair<sys_info, string_view>& info)
> >- : m_expanded(true), m_save(info.first.save),
> >m_offset(info.first.offset),
> >+ : m_expanded(true), m_save(info.first.save),
> >+ m_offset(info.first.offset - seconds(info.first.save)),
> > m_until(info.first.end)
> > {
> > if (info.second.size())
> >@@ -494,7 +495,7 @@ namespace std::chrono
> > m_buf += info.first.abbrev;
> > }
> >
> >- // STDOFF: Seconds from UTC during standard time.
> >+ // STDOFF: Seconds from UTC during standard time (without any
> save).
> > seconds
> > offset() const noexcept { return m_offset; }
> >
> >@@ -539,7 +540,7 @@ namespace std::chrono
> > return false;
> >
> > info.end = until();
> >- info.offset = offset();
> >+ info.offset = offset() + seconds(m_save);
> > info.save = minutes(m_save);
> > info.abbrev = format();
> > format_abbrev_str(info); // expand %z
> >diff --git a/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc
> >b/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc
> >new file mode 100644
> >index 000000000..1b5b22263
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc
> >@@ -0,0 +1,58 @@
> >+// { dg-do run { target c++20 } }
> >+// { dg-require-effective-target tzdb }
> >+// { dg-require-effective-target cxx11_abi }
> >+// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } }
> >+
> >+// When a Zone line specifies a numeric value as its RULES field, that
> >+// value is the constant DST save value for that zone line. Per
> >+// [time.zone.info.sys] sys_info::offset is the total UTC offset
> >+// (stdoff + save).
> >+
> >+#include <chrono>
> >+#include <fstream>
> >+#include <testsuite_hooks.h>
> >+
> >+static bool override_used = false;
> >+
> >+namespace __gnu_cxx
> >+{
> >+ const char* zoneinfo_dir_override() {
> >+ override_used = true;
> >+ return "./";
> >+ }
> >+}
> >+
> >+int
> >+main()
> >+{
> >+ using namespace std::chrono;
> >+
> >+ std::ofstream("tzdata.zi") << R"(# version test_numeric_save
> >+Z Test/Gaborone 2 - CAT 1943 Sep 19 2
> >+ 2 1 CAST 1944 Mar 19 2
> >+ 2 - CAT
> >+)";
> >+
> >+ const auto& db = reload_tzdb();
> >+ VERIFY( override_used );
>
> Please add a comment on the line above:
>
> VERIFY( override_used ); // If this fails then XFAIL for the target.
>
> OK for trunk with that change, thanks.
>
This patch is on turnk for some time, OK for backport to GCC 16 for 16.2?
>
>
> >+ VERIFY( db.version == "test_numeric_save" );
> >+
> >+ auto* tz = locate_zone("Test/Gaborone");
> >+
> >+ // Sample well inside the CAST (numeric-save) zone line.
> >+ auto info = tz->get_info(sys_days(1943y/December/15));
> >+ VERIFY( info.offset == 3h ); // stdoff +2h + save +1h
> >+ VERIFY( info.save == 60min );
> >+ VERIFY( info.abbrev == "CAST" );
> >+
> >+ // Bordering zone lines should report the standard offset with save 0.
> >+ auto before = tz->get_info(sys_days(1943y/September/1));
> >+ VERIFY( before.offset == 2h );
> >+ VERIFY( before.save == 0min );
> >+ VERIFY( before.abbrev == "CAT" );
> >+
> >+ auto after = tz->get_info(sys_days(1944y/April/15));
> >+ VERIFY( after.offset == 2h );
> >+ VERIFY( after.save == 0min );
> >+ VERIFY( after.abbrev == "CAT" );
> >+}
> >--
> >2.34.1
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260722/77de1294/attachment.htm>
More information about the Libstdc++
mailing list