Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2228546/?format=api
{ "id": 2228546, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2228546/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/CAF8dVMXpxL9xuAzYPyRsix1WT_kOkWoNQEO=GSqkEbU-h1PbVg@mail.gmail.com/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/1.2/projects/17/?format=api", "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, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<CAF8dVMXpxL9xuAzYPyRsix1WT_kOkWoNQEO=GSqkEbU-h1PbVg@mail.gmail.com>", "list_archive_url": null, "date": "2026-04-26T23:54:34", "name": "[v2,3/5] libstdc++: Resolve named-rule UNTIL save adjustment [PR116110]", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "b185b6090b6d966ad24b23a171225584ad7cbba5", "submitter": { "id": 93119, "url": "http://patchwork.ozlabs.org/api/1.2/people/93119/?format=api", "name": "Álvaro Begué", "email": "alvaro.begue@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/CAF8dVMXpxL9xuAzYPyRsix1WT_kOkWoNQEO=GSqkEbU-h1PbVg@mail.gmail.com/mbox/", "series": [ { "id": 501555, "url": "http://patchwork.ozlabs.org/api/1.2/series/501555/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=501555", "date": "2026-04-26T23:42:54", "name": "libstdc++: chrono tzdb correctness fixes", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/501555/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2228546/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2228546/checks/", "tags": {}, "related": [], "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=JH2PQgbn;\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=JH2PQgbn", "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=pass smtp.remote-ip=209.85.222.50" ], "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 4g3k9T6JcYz1yJX\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 27 Apr 2026 09:55:57 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id CE4D24BABF14\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 26 Apr 2026 23:55:55 +0000 (GMT)", "from mail-ua1-f50.google.com (mail-ua1-f50.google.com\n [209.85.222.50])\n by sourceware.org (Postfix) with ESMTPS id D412B4BB24D2\n for <gcc-patches@gcc.gnu.org>; Sun, 26 Apr 2026 23:55:11 +0000 (GMT)", "by mail-ua1-f50.google.com with SMTP id\n a1e0cc1a2514c-94ab69af6c8so6491453241.0\n for <gcc-patches@gcc.gnu.org>; Sun, 26 Apr 2026 16:55:11 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org CE4D24BABF14", "OpenDKIM Filter v2.11.0 sourceware.org D412B4BB24D2" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org D412B4BB24D2", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org D412B4BB24D2", "ARC-Seal": [ "i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1777247711; cv=pass;\n b=mcVMi3Af97r8vDRupddo/A+KXfaBPhZNB3G1EhpakxL756NoHyFWecHIDv/nBoG39PkUtvnU9lWTALkZZH4usoNwovwE86sgZhwIAh62tBOWiEnqNuImMr1Y92EU1B+LcpzT1kS2MXR9vMxgSaJcjdE1WyknqC3MnPdckzpEBKI=", "i=1; a=rsa-sha256; t=1777247711; cv=none;\n d=google.com; s=arc-20240605;\n b=HgJgFaXU2HrOH1i84vmSSk7pg4x7wk5ZsKcal9gEFoLMS7rulF6KzWJNLrfgk59v5z\n imTH3fOPc624lYX048lHhqp8W1Ws1o+UellYd1BQasYPPUAbZzAhtJKpToJoRrBS3ERX\n qh244xDpOcHXt+YMMQTs2vIGhvhxOdOMA2PaImwM5rrxCwZlEfSoiXgl+e7h6Orqn18A\n h6nCkLCjT5rUTqInNbTLGcvjGYVmirdMQkHCSOJg4zYZ5Q7dkc02BV7D+9smBpTvH3W3\n EP4vJiyg1Aqv0FEIyEq8+AjH8oObrZkQP3TdqGo1M9LcZ+ThbFxL+nG8J/UDCLLsiNt3\n nlJA==" ], "ARC-Message-Signature": [ "i=2; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777247711; c=relaxed/simple;\n bh=50H/mQHlrYk9/w6uvi2LgBhyDlJ6CT63yJlgXfKGW5M=;\n h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To;\n b=lgiHOmzzcTB93GM3GTYzJLuCr7IMgZSg8LVgCK/tPVTJqXhd+JmscWt0AL2NnKnSJ9u/Kj9p6L9azpaqNBABIk+oQu+zc7HcH7m4MhN8Jm+ms0TU01HHrwNuBgmE+obFzartI0PQOd25yQfUAUxyOpTVm3tYJZisFqkmly/uq/8=", "i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n h=cc:to:subject:message-id:date:from:mime-version:dkim-signature;\n bh=dhC6nx0fkwXQB0ohlDFDY0X4QQEjHOsVfjpnickeF6k=;\n fh=sWbBxiX87E8F1zPxXVV3X4tQqxV9sZLoQvmjvveliwE=;\n b=LEItfCAQpio6MaXzfWJkczX06QsOGMRnacJZHtYIiIyBdsrC+JGaVDlx/JnxkK9IWV\n JZPahkY5BtBdM7k9d+KhcMHNuBv1mSWqwhuBzIqY/BqMG48a7eX6G9+mJEPNbBZzlsL+\n pQ5KcLEAizq2wWYRTBgpsLN88HQwqOazNHO3rHDNeEHjFtf9DhSDzZs1Q4u354lxpK+n\n 1tMbUqBwSwFs6PygBhh41jXGq0ayXyfqK8zd4iFq35l+PKVE1yn7qrZ+OOPG1H80H4Jh\n 9eYYhj/jvTnkZlYwK6Nj4FdLbofIvv9ZHNHQdm2xZtRdwuYFLRuuBImz+iE7DcBKD1tt\n mDHg==; darn=gcc.gnu.org" ], "ARC-Authentication-Results": [ "i=2; server2.sourceware.org", "i=1; mx.google.com; arc=none" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777247711; x=1777852511; darn=gcc.gnu.org;\n h=cc:to:subject:message-id:date:from:mime-version:from:to:cc:subject\n :date:message-id:reply-to;\n bh=dhC6nx0fkwXQB0ohlDFDY0X4QQEjHOsVfjpnickeF6k=;\n b=JH2PQgbn0QWRteJCVQfk1wuCsHbtQdVkiHz7sv8ZJgbC9ACaU30V39d8Jp/8h1B4Mj\n VGc0VwABgqrYfONfcWLV/basZBg+kJ+jpySYoKVSmidH1Yf+Dqnhc58VZBVMZbwQ8IG7\n jIXDs0YCejRi/fpG8oZ/pifyJLfe9fEbGLC8nZPM3l9fDLrCYRj80Q0iy1b9LsGqoYPt\n jcIvvbhVhxb7Xx4rnbPwGx8z2F/QSt1keFnBSYN5xyms9LLqWWgxYKveP1QdljSvCvek\n R9mSSk2Ismq3THpi+qPpOk6kqNbd6u+fYl/ByaDDV3jdG/AeiEYDzfBx5JWJI81d8yaq\n 2vBA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777247711; x=1777852511;\n h=cc:to:subject:message-id:date:from:mime-version:x-gm-gg\n :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n bh=dhC6nx0fkwXQB0ohlDFDY0X4QQEjHOsVfjpnickeF6k=;\n b=XxWMF2ju+IhfAGBvQCDXy4xoEZkjMQ1PTXm5tOgW/TLHwzC3fvsBMR3FypXiKEVIoG\n x8kFQKExDmQ2FLsoJN+wJ7ScFJUb3saibm8xTwND/yxmsMGBRZkY4sZ4ZeWzdzSjPVPF\n CUc1z9zaWy/y3HD4mh86ozT1N0oySg3lQSxWKOtt/t6DTLUKD3O+DIWqxTZNioFKB82v\n diIMf8ZUu/bvipqzjSm3ghY1n3ZyOG4BtOm1wQM2Zqi6QoMKJjrH4Htawas27MUJw+8N\n I+Vzf8dSPsgs33XvkF9GLv5otXLukgC4CWciVJdzjJfd//HjvsWcpqlw9X19gdb/Sz0y\n g78A==", "X-Gm-Message-State": "AOJu0Yx1VCe+GbY1jUcb4Kr5BYZ/jG84u8EUBejgamimSk5erfS8Q6zF\n qm75ZAraxL4MqIchDfwTBd9HT53eKFBzcEzkPwPJvuO+7zeOhf6N9znvNZbfTCTnAD2CU8uWE5u\n 27iczgZLn0KMOPx50ckpS05hV7jTc9oiWlZx+6y4=", "X-Gm-Gg": "AeBDievMLNTFv++ufOTA2BEd6mlm9RoKgdg+dc4c3or2C0KZHJr2IyXsxyXNYU10O26\n AN5Xfkgb3S23+D2Ym2X0m9/NJp8HAYUPwubVueklVdwQRrvrkVAzxW0Mckj7lNyl2LifLP6r6gg\n 954nG/c9TUUiozTuU0deErO4F6EHwpHZrn132zocVYAdTmt7aa1F9e6RQMOfSBJnMXJ0A+UplDo\n +3WTg7pIdph1W+F0D3x1hwGipFSRzFdLHn4Kz/8y66Cv4bdIDJgj8DKDtUwU2ypSB3PdzldMg7t\n SYd8Cl+lm1bFh/wcIWLDw6sriryzyuJ8A5Xgo9Cd+W9iV5burRTvrWiyU7VgNovi796/WlC+A0J\n ef/JTlKfJT6e9Gx67f4tv9yFvUfk+APQykLbZOMaoCZATzv1J3PYK+6GIHydWsm3KIAn9ug==", "X-Received": "by 2002:a05:6102:5087:b0:611:aecd:34d9 with SMTP id\n ada2fe7eead31-616fd8084ffmr12900275137.9.1777247710937; Sun, 26 Apr 2026\n 16:55:10 -0700 (PDT)", "MIME-Version": "1.0", "From": "=?utf-8?b?w4FsdmFybyBCZWd1w6k=?= <alvaro.begue@gmail.com>", "Date": "Sun, 26 Apr 2026 19:54:34 -0400", "X-Gm-Features": "AVHnY4JT7fAEyA-iUbQF1Tcf1rHtnt0mMCbC-1Z7OOIXPVDSeKuZD-nJxoXpCkk", "Message-ID": "\n <CAF8dVMXpxL9xuAzYPyRsix1WT_kOkWoNQEO=GSqkEbU-h1PbVg@mail.gmail.com>", "Subject": "[PATCH v2 3/5] libstdc++: Resolve named-rule UNTIL save adjustment\n [PR116110]", "To": "gcc-patches@gcc.gnu.org", "Cc": "jwakely@redhat.com, libstdc++@gcc.gnu.org, tkaminsk@redhat.com,\n\t=?utf-8?b?w4FsdmFybyBCZWd1w6k=?= <alvaro.begue@gmail.com>", "Content-Type": "multipart/alternative; boundary=\"0000000000007d069b065065bad7\"", "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": "The previous patches for PR 116110 left one case unresolved: a Zone\nline with a wall-time UNTIL whose RULES field is a named rule set.\nThe save value used to convert the wall UNTIL to UTC depends on which\nrule of the set was active at the UNTIL instant, but at parse time\nthe rule records have not all been loaded, so the active-rule lookup\ncan't yet be performed.\n\nThe remaining FIXME in operator>>(istream&, ZoneInfo&) caused zones\nlike Africa/Algiers (around 1977-10-21) to place their zone-line\nboundary one save-period off from the canonical zic interpretation,\nproducing brief incorrect sys_info windows during DST transitions.\n\nThis commit defers the save adjustment to a fixup pass run after\nranges::stable_sort(node->rules) at the end of reload_tzdb.\n\nA new ZoneInfo::m_until_save_pending bit (stolen from m_pos:15, which\nbecomes m_pos:14 -- still 16384 max, far above any realistic offset\ninto m_buf) marks pending entries. The parser sets this bit when\nit sees a wall-time UNTIL on a named-rule line and skips the save\nsubtraction. The fixup pass walks every zone's ZoneInfos, looks up\nthe active rule, and applies the deferred adjustment.\n\nThe active-rule lookup uses a new helper find_pre_until_rule() that\nwalks all (rule, year) pairs in chronological order and maintains a\nrunning save value, so wall-time rules' TIME fields are interpreted\nrelative to the cascaded save state. The boundary it compares\nagainst shrinks as the running save grows, which gives zic.c's\ninterpretation: a rule firing at exactly the wall UNTIL belongs to\nthe next zone line, not the current one.\n\nThe lazy-expansion seeding code that finds the active rule at\ninfo.begin is also updated to use a half-open `rule_start < t`\nwindow with `t = info.begin + 1s`, so a rule firing at exactly\ninfo.begin (the new line's first instant) is correctly identified\nas in force. Without this, the new line would seed with the wrong\nsave and the first sys_info would have the wrong total offset and\nabbreviation.\n\nThe test_apia case in 116110.cc had a hardcoded `+11h` workaround\nfor the unfixed bug; with this fix in place the workaround is removed\nand the value becomes the canonical `+10h`.\n\nlibstdc++-v3/ChangeLog:\n\nPR libstdc++/116110\n* src/c++20/tzdb.cc (ZoneInfo): Add m_until_save_pending bit\n(stolen from m_pos:15) and accessors until_save_pending(),\nset_until_save_pending(), clear_until_save_pending(), and\nadjust_until().\n(find_pre_until_rule): New function. Chronological cascade\nwalker with iterative-boundary semantics, used by the post-\nparse fixup pass.\n(operator>>(istream&, ZoneInfo&)): Set m_until_save_pending\nwhen the wall UNTIL on a named-rule line cannot have its save\nsubtracted at parse time. Replaces the FIXME.\n(time_zone::_M_get_sys_info): Change the seeding active-\nrule lookup to use t = info.begin + 1s, so a rule firing at\nexactly info.begin is included.\n(reload_tzdb): After sorting node->rules, run a fixup pass over\nevery ZoneInfo with until_save_pending set, calling\nfind_pre_until_rule and adjust_until.\n* testsuite/std/time/time_zone/116110.cc (test_apia): Remove\nthe +11h workaround for the unfixed named-rule UNTIL bug; the\ncanonical +10h boundary is now produced.\n* testsuite/std/time/time_zone/pr116110_named.cc: New test.\n\nSigned-off-by: Álvaro Begué <alvaro.begue@gmail.com>\n---\n libstdc++-v3/src/c++20/tzdb.cc | 122 +++++++++++++++++-\n .../testsuite/std/time/time_zone/116110.cc | 5 +-\n .../std/time/time_zone/pr116110_named.cc | 74 +++++++++++\n 3 files changed, 196 insertions(+), 5 deletions(-)\n create mode 100644\nlibstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc", "diff": "diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc\nindex c0d62bc35..648b9f85a 100644\n--- a/libstdc++-v3/src/c++20/tzdb.cc\n+++ b/libstdc++-v3/src/c++20/tzdb.cc\n@@ -518,6 +518,20 @@ namespace std::chrono\n sys_seconds\n until() const noexcept { return m_until; }\n\n+ // True if this is a named-rule zone line whose wall-time UNTIL still\n+ // needs its SAVE adjustment applied. See reload_tzdb for the fixup.\n+ bool\n+ until_save_pending() const noexcept { return m_until_save_pending; }\n+\n+ void\n+ set_until_save_pending() noexcept { m_until_save_pending = 1; }\n+\n+ void\n+ clear_until_save_pending() noexcept { m_until_save_pending = 0; }\n+\n+ void\n+ adjust_until(seconds s) noexcept { m_until -= s; }\n+\n friend istream& operator>>(istream&, ZoneInfo&);\n\n bool\n@@ -571,8 +585,9 @@ namespace std::chrono\n }\n\n string m_buf; // rules() + ' ' + format() OR letters + ' ' +\nformat()\n- uint_least16_t m_pos : 15 = 0; // offset of format() in m_buf\n+ uint_least16_t m_pos : 14 = 0; // offset of format() in m_buf\n uint_least16_t m_expanded : 1 = 0;\n+ uint_least16_t m_until_save_pending : 1 = 0;\n duration<int_least16_t, ratio<60>> m_save{};\n sec32_t m_offset{};\n sys_seconds m_until{};\n@@ -679,6 +694,81 @@ namespace std::chrono\n }\n #endif\n };\n+\n+ // Find the Rule whose save value is in force at the wall-time UNTIL\n+ // of a Zone line, given that `wall_minus_stdoff` is the line's UNTIL\n+ // with STDOFF subtracted and `stdoff` is the line's standard offset.\n+ //\n+ // Walks (rule, year) pairs chronologically, maintaining a running\n+ // save value used to interpret subsequent Wall-indicator rules.\n+ // The boundary `wall_minus_stdoff - running_save` shrinks as save\n+ // accumulates, so a rule firing AT the boundary is treated as\n+ // belonging to the next zone line.\n+ //\n+ // The calendar window extends by one year on each side to catch\n+ // rules whose wall at_time crosses a year boundary in UT due to a\n+ // large stdoff or save.\n+ template<typename _RuleRange>\n+ const Rule*\n+ find_pre_until_rule(const _RuleRange& rules,\n+ sys_seconds wall_minus_stdoff, seconds stdoff)\n+ {\n+ if (rules.empty())\n+ return nullptr;\n+\n+ const year last_year\n+ = year_month_day{chrono::floor<days>(wall_minus_stdoff)}.year()\n+ + years(1);\n+ year first_year = year::max();\n+ for (const auto& r : rules)\n+ if (r.from < first_year)\n+ first_year = r.from;\n+ if (first_year > last_year)\n+ return nullptr;\n+\n+ struct Pending\n+ {\n+ const Rule* rule;\n+ year y;\n+ sys_seconds approx_when;\n+ };\n+ vector<Pending> pending;\n+ pending.reserve(64);\n+ for (year y = first_year; y <= last_year; ++y)\n+ for (const auto& r : rules)\n+ {\n+ if (y < r.from || y > r.to)\n+ continue;\n+ seconds approx_off{};\n+ if (r.when.indicator == at_time::Wall\n+ || r.when.indicator == at_time::Standard)\n+ approx_off = stdoff;\n+ pending.push_back({&r, y, r.start_time(y, approx_off)});\n+ }\n+ std::sort(pending.begin(), pending.end(),\n+ [](const Pending& a, const Pending& b) {\n+ return a.approx_when < b.approx_when;\n+ });\n+\n+ seconds running_save{};\n+ sys_seconds boundary = wall_minus_stdoff;\n+ const Rule* last_fired = nullptr;\n+ for (const auto& p : pending)\n+ {\n+ seconds offset{};\n+ if (p.rule->when.indicator == at_time::Wall)\n+ offset = stdoff + running_save;\n+ else if (p.rule->when.indicator == at_time::Standard)\n+ offset = stdoff;\n+ sys_seconds fire = p.rule->start_time(p.y, offset);\n+ if (fire >= boundary)\n+ continue;\n+ last_fired = p.rule;\n+ running_save = p.rule->save;\n+ boundary = wall_minus_stdoff - running_save;\n+ }\n+ return last_fired;\n+ }\n } // namespace\n #endif // TZDB_DISABLED\n\n@@ -867,7 +957,9 @@ namespace std::chrono\n\n if (letters.empty())\n {\n- sys_seconds t = info.begin - seconds(1);\n+ // info.begin + 1s makes the strict `rule_start < t` search\n+ // inclusive of a rule that fires at exactly info.begin.\n+ sys_seconds t = info.begin + seconds(1);\n const year_month_day date(chrono::floor<days>(t));\n\n // Try to find a Rule active before this time, to get initial\n@@ -1625,6 +1717,27 @@ namespace std::chrono\n ranges::sort(node->db.links, {}, &time_zone_link::name);\n ranges::stable_sort(node->rules, {}, &Rule::name);\n\n+ // For every Zone line whose UNTIL was a wall-time expression on a\n+ // named-rule line, the parser deferred the SAVE adjustment because\n+ // the active rule was not yet identifiable. Now that all Rule\n+ // records are loaded and indexed, find the rule active just before\n+ // the wall UNTIL and subtract its save from m_until.\n+ for (const auto& tz : node->db.zones)\n+ {\n+ auto& infos = tz._M_impl->infos;\n+ for (auto& info : infos)\n+ {\n+ if (!info.until_save_pending())\n+ continue;\n+ auto rules = ranges::equal_range(node->rules, info.rules(),\n+ ranges::less{}, &Rule::name);\n+ if (const Rule* r\n+ = find_pre_until_rule(rules, info.until(), info.offset()))\n+ info.adjust_until(seconds(r->save));\n+ info.clear_until_save_pending();\n+ }\n+ }\n+\n return Node::_S_replace_head(std::move(head), std::move(node));\n #else\n __throw_disabled();\n@@ -2386,7 +2499,10 @@ namespace std::chrono\n {\n if (inf.m_expanded) // Not a named Rule, SAVE is known now.\n inf.m_until -= inf.m_save;\n- // else Named Rule, SAVE is unknown. FIXME: PR 116110\n+ else\n+ // Named Rule: defer SAVE adjustment until reload_tzdb\n+ // has loaded all Rule records.\n+ inf.set_until_save_pending();\n }\n }\n }\ndiff --git a/libstdc++-v3/testsuite/std/time/time_zone/116110.cc\nb/libstdc++-v3/testsuite/std/time/time_zone/116110.cc\nindex 26b9ba33c..7827387b5 100644\n--- a/libstdc++-v3/testsuite/std/time/time_zone/116110.cc\n+++ b/libstdc++-v3/testsuite/std/time/time_zone/116110.cc\n@@ -65,8 +65,9 @@ test_apia()\n auto* tz = locate_zone(\"Pacific/Apia\");\n local_seconds t = local_days(2011y/December/29) + 24h;\n\n- // FIXME: this should be + 10h but we do not account for DST yet, so +\n11h.\n- sys_seconds ut(t.time_since_epoch() + 11h );\n+ // The wall UNTIL is interpreted in the prior offset (-11h + save 1h\n+ // = -10h), so the boundary is at local_days + 24h + 10h.\n+ sys_seconds ut(t.time_since_epoch() + 10h );\n sys_info info;\n info = tz->get_info(ut - 1s);\n VERIFY( info.offset == (-11h + info.save) );\ndiff --git a/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc\nb/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc\nnew file mode 100644\nindex 000000000..b3bf4eb1a\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc\n@@ -0,0 +1,74 @@\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+// Africa/Algiers 1977-10-21: a Zone line whose RULES references a\n+// named Rule and whose UNTIL is a wall-time expression. The wall\n+// UNTIL is interpreted using the SAVE value in force just before the\n+// boundary (the May-6 rule's save=1, not the Oct-21 rule's save=0\n+// even though the Oct-21 rule fires at the same wall instant).\n+//\n+// Rule d 1977 May 6 0:00 wall save=1\n+// Rule d 1977 Oct 21 0:00 wall save=0\n+// Z A 0 d WE%sT 1977 O 21\n+// 1 d CE%sT\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_pr116110_named\n+R d 1977 o - May 6 0 1 S\n+R d 1977 o - O 21 0 0 -\n+Z Test/Algiers 0 d WE%sT 1977 O 21\n+ 1 d CE%sT\n+)\";\n+\n+ const auto& db = reload_tzdb();\n+ VERIFY( override_used );\n+ VERIFY( db.version == \"test_pr116110_named\" );\n+\n+ auto* tz = locate_zone(\"Test/Algiers\");\n+\n+ // Just before the boundary: still in the first Zone line under\n+ // the May-6 rule (save=1, WEST, total +1).\n+ auto pre = tz->get_info(sys_days{1977y/October/20} + 22h);\n+ VERIFY( pre.offset == 1h );\n+ VERIFY( pre.save == 1h );\n+ VERIFY( pre.abbrev == \"WEST\" );\n+\n+ // The boundary is Oct 20 23:00 UTC (= wall 00:00 - stdoff(0) - save(1)).\n+ // At and after the boundary we are in the second line (CET).\n+ auto at = tz->get_info(sys_days{1977y/October/20} + 23h);\n+ VERIFY( at.offset == 1h ); // stdoff 1 + save 0 (CET, second line)\n+ VERIFY( at.save == 0min );\n+ VERIFY( at.abbrev == \"CET\" );\n+\n+ // A second query inside the second line, well clear of the boundary.\n+ auto after = tz->get_info(sys_days{1977y/October/21} + 12h);\n+ VERIFY( after.offset == 1h );\n+ VERIFY( after.save == 0min );\n+ VERIFY( after.abbrev == \"CET\" );\n+\n+ // A query inside the [Oct 20 23:00, Oct 21 00:00] UTC window must be\n+ // in the second line, not in a leftover stretch from the first line.\n+ auto window = tz->get_info(sys_days{1977y/October/20} + 23h + 30min);\n+ VERIFY( window.offset == 1h );\n+ VERIFY( window.abbrev == \"CET\" );\n+}\n", "prefixes": [ "v2", "3/5" ] }