{"id":2222415,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2222415/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/patch/a08fa4509d28c856eb6063edf8285c00797b415b.1775912642.git.alvaro.begue@gmail.com/","project":{"id":17,"url":"http://patchwork.ozlabs.org/api/1.1/projects/17/?format=json","name":"GNU Compiler Collection","link_name":"gcc","list_id":"gcc-patches.gcc.gnu.org","list_email":"gcc-patches@gcc.gnu.org","web_url":null,"scm_url":null,"webscm_url":null},"msgid":"<a08fa4509d28c856eb6063edf8285c00797b415b.1775912642.git.alvaro.begue@gmail.com>","date":"2026-04-11T13:33:05","name":"[1/5] libstdc++: Fix numeric save offset on Zone lines [PR 124851]","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"b87c0c92e56aea963b56af6588098a7625c8a618","submitter":{"id":93119,"url":"http://patchwork.ozlabs.org/api/1.1/people/93119/?format=json","name":"Alvaro Begue","email":"alvaro.begue@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/gcc/patch/a08fa4509d28c856eb6063edf8285c00797b415b.1775912642.git.alvaro.begue@gmail.com/mbox/","series":[{"id":499557,"url":"http://patchwork.ozlabs.org/api/1.1/series/499557/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/list/?series=499557","date":"2026-04-11T13:33:04","name":"libstdc++: chrono tzdb correctness fixes","version":1,"mbox":"http://patchwork.ozlabs.org/series/499557/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2222415/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2222415/checks/","tags":{},"headers":{"Return-Path":"<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>","X-Original-To":["incoming@patchwork.ozlabs.org","gcc-patches@gcc.gnu.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","gcc-patches@gcc.gnu.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=BVwuaKT9;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=BVwuaKT9","sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com","sourceware.org; spf=pass smtp.mailfrom=gmail.com","server2.sourceware.org;\n arc=none smtp.remote-ip=209.85.219.45"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4ftF6h5Ny7z1yGb\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 11 Apr 2026 23:35:40 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id E9A1A4BA9032\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 11 Apr 2026 13:35:38 +0000 (GMT)","from mail-qv1-f45.google.com (mail-qv1-f45.google.com\n [209.85.219.45])\n by sourceware.org (Postfix) with ESMTPS id 45F184BA23D9\n for <gcc-patches@gcc.gnu.org>; Sat, 11 Apr 2026 13:33:29 +0000 (GMT)","by mail-qv1-f45.google.com with SMTP id\n 6a1803df08f44-8a48deebe95so24866016d6.0\n for <gcc-patches@gcc.gnu.org>; Sat, 11 Apr 2026 06:33:29 -0700 (PDT)","from alvaro-MS-7D37.verizon.net\n ([2600:4041:592d:7300:b8f7:c631:1ab:88b0])\n by smtp.gmail.com with ESMTPSA id\n 6a1803df08f44-8ac84caace4sm47629306d6.39.2026.04.11.06.33.28\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Sat, 11 Apr 2026 06:33:28 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org E9A1A4BA9032","OpenDKIM Filter v2.11.0 sourceware.org 45F184BA23D9"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 45F184BA23D9","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 45F184BA23D9","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775914409; cv=none;\n b=A/doFYXEPTS0Q2E645AAnTExVNOw2axpoFYOM51sTY587UOLb6dAFRoX4cOeIRC7QM2GwGqFF2AqZ1pqtd2zrZPz2QIiqu05SuLJFzzJwbj2GLbEVm+ZZ7QrwlJPl5k9EqQaagWmdqQfiMUbWDylty0g0AlyX3l//R3eSzB1HOc=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1775914409; c=relaxed/simple;\n bh=hrwWnrm8ue7inTh+w0Amj6RxPHZR5Ku4D3ttuCGGbx8=;\n h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version;\n b=OhFxS0/ja+shEHqmtbFZ37x0dQD/q2jEQVAzNH6wUS+jXKRqdjFLH/RpwAm8DGl7sQAhRghOTanECIGDPQElAxy8MttyQvd/sZ5n0U1O0oJC1/BKc6hlpqnJHfwC37/2YDthNTRtLJYju5mHkXHcbCAew5fRV4QRuJ6b7UgUwBU=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1775914408; x=1776519208; darn=gcc.gnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=Y3bARdSd1XA9sRfElWN5eZEv4uVEqD3vLt+CiIj/VIo=;\n b=BVwuaKT99xG0vW1sKwmGKMzI7f3HCsfC/dpJarz6L0fX3f9dylQqjNAIb84uNoNDQP\n iLCSgdSTr4DYGwh79XvCxefJq2TX+FEHPkq+XmSeliRAQmioYjWWi4wC26fYx1RqemL3\n HCFSoItxuJh+B8iEObrTGeL95eNHfJq8jj4QoHsHHj/stLM7MJZMBzHmsb6LnXDH9hli\n BjpN0vd/JKloVgM3lccDk3e0Isa3gV9q08V1bl6jrrdB4Gl1gjGAtUSLPVauQptctLoQ\n Lx5IR/hie2adrT5999yRSZKwuw2ME3mVSUFE7hHBBJTuKb47g6tPOtg7qn49NVQCJMpF\n fpWA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1775914408; x=1776519208;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=Y3bARdSd1XA9sRfElWN5eZEv4uVEqD3vLt+CiIj/VIo=;\n b=c2ELDJdRpJJ1SwWn0puKk8ziSWF3xsTE8Ur3T8TXC0Uh/oQZxMFMDj4TF8p1Tpwfsk\n ISJyQrpgp94401xjX39xcdUl6g61e8UjHV90D+oVTMwbFKABWqMV56yJL4IJ8spaB5HI\n bJTIiTpmTktoEJtfqu9k0bDZftFV8002EeQpHSlrqZKDOSJ6X1hn7bEXAsWmL1n+loTP\n nrkNTEtxMyQ3ffBS1foLEht136amEn4mzGNaJC1AnN2xUrrwpD2gTF/wVTs3NJRPHGaQ\n gi/vGgfky8EkJbiEp3Be+OJj8/NagCyNjk8k5oDH6J/3RBql1vukGC9JiExzTwAPZ8VY\n kKJg==","X-Gm-Message-State":"AOJu0YwwqRee1MwIrEMhgS0RWck+fcbtAsa1umSUz200iNw2Bre43TbE\n 9x+/vP2LwIMQU+P1sEIOJyDLuQuTnzitymY0hOhdz8NHnzaMvnKf0lSDMdtdQQ==","X-Gm-Gg":"AeBDieu7uFed2pYU5Sd8crfEFMvJSmzBXzMqlaPsdBPnMgZweb/IuEFteqYhrfx8Xps\n u06d4uH1KFpqPrB/hkL1rJEZJP49MQBtWY8FdVmdD1PlS9uZ0/AIcxLNO3upWSwQCJ2+QbCkhzN\n i2JXH1uR85c7gY4kkU4Z44lFQ43nDSlNjvGug5wniWNZckpkU4fv3Mz3BrIvjfYC0Tp1Wc7gpz3\n mrcyH8iYBFjtR42/Jc8hV4ctrqbcR7K7dAh/Q58c5fv2vk3ljP26MVwH+fH5li1X0jGY/jhivg8\n EiY6VgVD8YcrGJZgQb6NdETBsFK2steOIB0ZHm0LH+iaaiCt3O6LagIzsW98uFuPIDDdFhKPQvi\n 4ZkzGvOFqi2ACHxbLE1sK4PtAAdloNJUFb9PjGq1lUFvcGIl5zbSecfsJwXR0hXuP0qkXv5u2d2\n 9R9RcgPU34NSjFJQ26bazNUo9UKT/IbHavmyp2cp026NOHp5m5BG3Qhxz8CunToT1AcEh9DtLfA\n mniMa6PgY45iQwVigfJsaQZNhDiejRBvyJPeYp31xJHrP7H6llTp2TFA9RN0zXHEN6NFiN99HGa\n 25LM","X-Received":"by 2002:a05:6214:528f:b0:896:fbdd:ef14 with SMTP id\n 6a1803df08f44-8ac8619177cmr97475186d6.12.1775914408544;\n Sat, 11 Apr 2026 06:33:28 -0700 (PDT)","From":"Alvaro Begue <alvaro.begue@gmail.com>","To":"gcc-patches@gcc.gnu.org","Cc":"jwakely@redhat.com, libstdc++@gcc.gnu.org,\n Alvaro Begue <alvaro.begue@gmail.com>","Subject":"[PATCH 1/5] libstdc++: Fix numeric save offset on Zone lines [PR\n 124851]","Date":"Sat, 11 Apr 2026 09:33:05 -0400","Message-Id":"\n <a08fa4509d28c856eb6063edf8285c00797b415b.1775912642.git.alvaro.begue@gmail.com>","X-Mailer":"git-send-email 2.34.1","In-Reply-To":"<cover.1775912642.git.alvaro.begue@gmail.com>","References":"<cover.1775912642.git.alvaro.begue@gmail.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-BeenThere":"gcc-patches@gcc.gnu.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Gcc-patches mailing list <gcc-patches.gcc.gnu.org>","List-Unsubscribe":"<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>","List-Archive":"<https://gcc.gnu.org/pipermail/gcc-patches/>","List-Post":"<mailto:gcc-patches@gcc.gnu.org>","List-Help":"<mailto:gcc-patches-request@gcc.gnu.org?subject=help>","List-Subscribe":"<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>","Errors-To":"gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"},"content":"When a Zone line specifies a numeric value as its RULES field (the\nconstant DST save value for that zone line, e.g. Africa/Gaborone's\n\"2 1 CAST\" line), the parser stored the standard offset alone in\nZoneInfo::m_offset. ZoneInfo::to() then returned that as\nsys_info::offset, dropping the numeric save and reporting a total\noffset that was wrong by the save amount.\n\nThis was inconsistent with the two ZoneInfo constructors that take a\nsys_info, which previously stored the *total* offset (stdoff + save) in\nm_offset. As a result m_offset's semantics depended on which code path\ncreated the ZoneInfo, and only the parser path's lines with non-zero\nnumeric save were observably broken.\n\nFix by giving m_offset a single semantics: always the standard offset\nonly. The two sys_info-taking constructors now subtract the save before\nstoring, and to() adds it back when reconstructing the sys_info.\n\nThe remaining .offset() callers inside _M_get_sys_info already expect\nthe standard offset (they are computing rule firing times, where the\nsave component is added separately from the active rule's save value),\nso no other call sites need adjustment.\n\nlibstdc++-v3/ChangeLog:\n\n\tPR libstdc++/124851\n\t* src/c++20/tzdb.cc (ZoneInfo::ZoneInfo(sys_info&&)): Store\n\tstdoff only in m_offset (subtract info.save).\n\t(ZoneInfo::ZoneInfo(const pair<sys_info, string_view>&)):\n\tLikewise.\n\t(ZoneInfo::offset()): Document new semantics.\n\t(ZoneInfo::to(sys_info&)): Add m_save back to offset() when\n\tpopulating sys_info::offset.\n\t* testsuite/std/time/time_zone/numeric_save.cc: New test.\n---\n libstdc++-v3/src/c++20/tzdb.cc                | 13 ++--\n .../std/time/time_zone/numeric_save.cc        | 66 +++++++++++++++++++\n 2 files changed, 75 insertions(+), 4 deletions(-)\n create mode 100644 libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc","diff":"diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc\nindex eb6ac7d07..33217f0a9 100644\n--- a/libstdc++-v3/src/c++20/tzdb.cc\n+++ b/libstdc++-v3/src/c++20/tzdb.cc\n@@ -478,11 +478,12 @@ namespace std::chrono\n \n       ZoneInfo(sys_info&& info)\n       : m_buf(std::move(info.abbrev)), m_expanded(true), m_save(info.save),\n-\tm_offset(info.offset), m_until(info.end)\n+\tm_offset(info.offset - seconds(info.save)), m_until(info.end)\n       { }\n \n       ZoneInfo(const pair<sys_info, string_view>& info)\n-      : m_expanded(true), m_save(info.first.save), m_offset(info.first.offset),\n+      : m_expanded(true), m_save(info.first.save),\n+\tm_offset(info.first.offset - seconds(info.first.save)),\n \tm_until(info.first.end)\n       {\n \tif (info.second.size())\n@@ -494,7 +495,9 @@ namespace std::chrono\n \tm_buf += info.first.abbrev;\n       }\n \n-      // STDOFF: Seconds from UTC during standard time.\n+      // STDOFF: Seconds from UTC during standard time.  Always the\n+      // standard offset only; the saved value (if any) is in m_save and\n+      // is added back when reconstructing a sys_info via to().\n       seconds\n       offset() const noexcept { return m_offset; }\n \n@@ -539,7 +542,9 @@ namespace std::chrono\n \t  return false;\n \n \tinfo.end = until();\n-\tinfo.offset = offset();\n+\t// m_offset is the standard offset only; add the saved value to\n+\t// reconstruct the total offset.  See ZoneInfo's m_offset comment.\n+\tinfo.offset = offset() + seconds(m_save);\n \tinfo.save = minutes(m_save);\n \tinfo.abbrev = format();\n \tformat_abbrev_str(info); // expand %z\ndiff --git a/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc b/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc\nnew file mode 100644\nindex 000000000..d7b78f215\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/std/time/time_zone/numeric_save.cc\n@@ -0,0 +1,66 @@\n+// { dg-do run { target c++20 } }\n+// { dg-require-effective-target tzdb }\n+// { dg-require-effective-target cxx11_abi }\n+// { dg-xfail-run-if \"no weak override on AIX\" { powerpc-ibm-aix* } }\n+\n+// Regression test: when a Zone line specifies a numeric value as its\n+// RULES field, that value is the constant DST save value for that zone\n+// line.  ZoneInfo::to() previously set sys_info::offset to the zone\n+// line's STDOFF only, ignoring the parsed save.  Per [time.zone.info.sys]\n+// sys_info::offset is the *total* UTC offset (stdoff + save), so any\n+// zone line with a non-zero numeric save reported the wrong offset.\n+//\n+// Mirrors Africa/Gaborone's tzdata, which uses\n+//   2 - CAT 1943 S 19 2\n+//   2 1 CAST 1944 Mar 19 2     <-- numeric \"1\" RULES, save = +1h\n+//   2 - CAT\n+// The middle line is what triggers the bug.\n+\n+#include <chrono>\n+#include <fstream>\n+#include <testsuite_hooks.h>\n+\n+static bool override_used = false;\n+\n+namespace __gnu_cxx\n+{\n+  const char* zoneinfo_dir_override() {\n+    override_used = true;\n+    return \"./\";\n+  }\n+}\n+\n+int\n+main()\n+{\n+  using namespace std::chrono;\n+\n+  std::ofstream(\"tzdata.zi\") << R\"(# version test_numeric_save\n+Z Test/Gaborone 2 -  CAT  1943 Sep 19 2\n+                2 1  CAST 1944 Mar 19 2\n+                2 -  CAT\n+)\";\n+\n+  const auto& db = reload_tzdb();\n+  VERIFY( override_used );\n+  VERIFY( db.version == \"test_numeric_save\" );\n+\n+  auto* tz = locate_zone(\"Test/Gaborone\");\n+\n+  // Sample well inside the CAST (numeric-save) zone line.\n+  auto info = tz->get_info(sys_days(1943y/December/15));\n+  VERIFY( info.offset == 3h );        // stdoff +2h + save +1h\n+  VERIFY( info.save == 60min );\n+  VERIFY( info.abbrev == \"CAST\" );\n+\n+  // Bordering zone lines should report the standard offset with save 0.\n+  auto before = tz->get_info(sys_days(1943y/September/1));\n+  VERIFY( before.offset == 2h );\n+  VERIFY( before.save == 0min );\n+  VERIFY( before.abbrev == \"CAT\" );\n+\n+  auto after = tz->get_info(sys_days(1944y/April/15));\n+  VERIFY( after.offset == 2h );\n+  VERIFY( after.save == 0min );\n+  VERIFY( after.abbrev == \"CAT\" );\n+}\n","prefixes":["1/5"]}