[PATCH] libstdc++: Resolve named-rule UNTIL save adjustment [PR116110]
Tomasz Kaminski
tkaminsk@redhat.com
Thu Jul 16 15:32:52 GMT 2026
On Thu, Jul 16, 2026 at 2:53 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Wed, 15 Jul 2026 at 10:43 +0200, Tomasz Kamiński wrote:
> >The previous patches for PR 116110 left one case unresolved: a Zone
> >line with a wall-time UNTIL whose RULES field is a named rule set.
> >The save value used to convert the wall UNTIL to UTC depends on which
> >rule of the set was active at the UNTIL instant, but at parse time
> >the rule records have not all been loaded, so the active-rule lookup
> >can't yet be performed.
> >
> >The remaining FIXME in operator>>(istream&, ZoneInfo&) caused zones
> >like Africa/Algiers (around 1977-10-21) to place their zone-line
> >boundary one save-period off from the canonical zic interpretation,
> >producing brief incorrect sys_info windows during DST transitions.
> >
> >This commit defers the save adjustment to _M_get_sys_info call,
> >and applies it only for the ZoneInfo surrounding the specified
> >time. This assumes that applying the save does not change relative
> >order of ZoneInfo (i.e. the until members are spaced more than save).
> >
> >The m_expanded member is replaced with four state _M_state setting:
> >Expanded, and three separte state used for rule based zones:
> >* SaveKnown - m_save contains save value at time m_until
> >* SavePending - m_save is defualted to zero, m_until is correct
> >* UntilPending - as above, but also m_until time need to adjusted
> >The parser set the UntilPending state when it sees a wall-time
> >UNTIL on a named-rule line, and SaveKnown if not UNTIL date is
> >specified.
> >
> >The fixup in _M_get_sys_info, is performed by calc_save function
> >on previous/current zone. If necessary (depending on m_state) this
> >function updates m_save and m_until members, and returns true
> >if the m_until was shifted in the process. In such case, we redo
> >the comparision of until() against tp, and move iterator to active
> >zone info if necessary. The save at boundary for previous/current
> >ZoneInfo is required to implement proper zone merging, so it is
> >always computed.
> >
> >The active-rule lookup either reuses the existing find_active_rule
> >overload accepting sys_seconds (if UNTIL is not affected by save),
> >or newly introduced overload accepting local_seconds (local time).
> >The local_seconds versions, follows the same logic for finding
> >rule transitions (extracted to find_surrounding_transitions)
> >surrounding the sys_time. The final active rule, is then determined
> >after converting the transition times to local time, including the
> >running save.
> >
> >The test_apia case in 116110.cc had a hardcoded `+11h` workaround
> >for the unfixed bug; with this fix in place the workaround is removed
> >and the value becomes the canonical `+10h`.
> >
> >libstdc++-v3/ChangeLog:
> >
> > PR libstdc++/116110
> > * src/c++20/tzdb.cc (ZoneInfo::calc_save, ZoneInfo::State): Define.
> > (ZoneInfo::m_expanded): Replaced with m_state.
> > (ZoneInfo::m_pos): Reduce the bit with to 14.
> > (ZoneInfo::m_state): Expands m_expanded with four state enum.
> > (ZoneInfo::ZoneInfo, ZoneInfo::expanded, ZoneInfo::set_abbrev):
> > Replace m_expanded usage with m_state and State::Expanded.
> > (Transitions, find_surrounding_transitions): Extracted from
> > find_active_rule.
> > (find_active_rule): Define overload for local_seconds. Implement
> > both in terms of find_surrounding_transitions.
> > (time_zone::_M_get_sys_info): Use ZoneInfo::calc_save to adjust
> > until() on surrounding zones, and shift the current zone iterator
> > (i) accordingly.
> > (operator>>(istream&, ZoneInfo&)): Set m_state for UntilPending
> > for UNTIL time using wall time, and SaveKnown for lines without
> > end date. For clarity, set m_save to 0 if daylight is not used.
> > * testsuite/std/time/time_zone/116110.cc (test_apia): Use correct
> > value of the the total offset.
> > testsuite/std/time/time_zone/pr116110_named.cc: New test.
> >
> >Co-authored-by: Álvaro Begué <alvaro.begue@gmail.com>
> >Signed-off-by: Álvaro Begué <alvaro.begue@gmail.com>
> >Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> >---
> >v3 rewrites the implementation. The major change is the fact that
> >
> >save-adjustment is now applied lazily and locally to the neighbooring
> >zones. This have strong assumtion about zone changes being resonably
> >spaced (i.e. they remain sorted after save adjustment). After considering
> >that I believe this is resonable, as we are discussing save, and not
> >offset (that may jump for 24h).
> >
> >The original v3 also changed the code to find active rule at time of
> >transition, to include transition happening exaclty at info begin.
> >I have dropped it for this version of the patch, as it was not
> >necessary to pass included test, and caused another failures.
> >We should revisit the comparision against the zic result once all
> >fixes are in place.
> >
> >Testing on x86_64-linux. All *time_zone* test passes.
> >We should also see the performance impact before any decision to
> >backport.
>
> This logic is surprisingly easy to follow now - very nice.
>
I am also happy with how this ended up. Took me multiple back and
forth to get to this state, so happy to hear it actually helped.
>
> OK for trunk with one comment typo fix and one whitespace change
> (see below).
>
I also checked the output against Howard's date using your program,
and no new differences were introduced here. There are still some
FAILs remaining.
For the moment, I plan to modify the script to accept the splits,
i.e. situations where libstdc++ produces two zones for some time-range,
that one zone covers in the date output, if they have same parameters.
>
> >
> > libstdc++-v3/src/c++20/tzdb.cc | 221 +++++++++++++-----
> > .../testsuite/std/time/time_zone/116110.cc | 4 +-
> > .../std/time/time_zone/pr116110_named.cc | 74 ++++++
> > 3 files changed, 244 insertions(+), 55 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc
> >
> >diff --git a/libstdc++-v3/src/c++20/tzdb.cc
> b/libstdc++-v3/src/c++20/tzdb.cc
> >index 998a363151d..09c2127b242 100644
> >--- a/libstdc++-v3/src/c++20/tzdb.cc
> >+++ b/libstdc++-v3/src/c++20/tzdb.cc
> >@@ -483,6 +483,8 @@ namespace std::chrono
> > select_std_or_dst_abbrev(info.abbrev, info.save);
> > }
> >
> >+ struct Rule;
> >+
> > // A time zone information record.
> > // Zone NAME STDOFF RULES FORMAT [UNTIL]
> > // Zone Asia/Amman 2:00 Jordan EE%sT 2017 Oct 27 01:00
> >@@ -492,12 +494,12 @@ namespace std::chrono
> > ZoneInfo() = default;
> >
> > ZoneInfo(sys_info&& info)
> >- : m_buf(std::move(info.abbrev)), m_expanded(true),
> m_save(info.save),
> >+ : m_buf(std::move(info.abbrev)), m_state(Expanded),
> m_save(info.save),
> > 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_state(Expanded), m_save(info.first.save),
> > m_offset(info.first.offset - seconds(info.first.save)),
> > m_until(info.first.end)
> > {
> >@@ -533,17 +535,23 @@ namespace std::chrono
> > sys_seconds
> > until() const noexcept { return m_until; }
> >
> >+ // For non-expanded zone (using rules) computes the value
> >+ // of 'save' at the time of transitions, caches it in m_save.
> >+ // Returns true if until value was changed in the process.
> >+ bool
> >+ calc_save(span<const Rule> all_rules) noexcept;
> >+
> > friend istream& operator>>(istream&, ZoneInfo&);
> >
> > bool
> > expanded() const noexcept
> >- { return m_expanded; }
> >+ { return m_state == Expanded; }
> >
> > // For an expanded ZoneInfo this returns the LETTERS that apply to
> the
> > // **next** sys_info after this one.
> > string_view
> > next_letters() const noexcept
> >- { return m_expanded ? rules() : string_view{}; }
> >+ { return (m_state == Expanded) ? rules() : string_view{}; }
> >
> >
> > bool
> >@@ -551,7 +559,7 @@ namespace std::chrono
> > {
> > // If this object references a named Rule then we can't populate
> > // a sys_info yet.
> >- if (!m_expanded)
> >+ if (m_state != Expanded)
> > return false;
> >
> > info.end = until();
> >@@ -565,12 +573,16 @@ namespace std::chrono
> > private:
> > friend class time_zone;
> >
> >+ enum class State : uint_least16_t
> >+ { Expanded, SaveKnown, SavePending, UntilPending };
> >+ using enum State;
> >+
> > void
> > set_abbrev(string abbrev)
> > {
> > m_buf = std::move(abbrev);
> > m_pos = 0;
> >- m_expanded = true;
> >+ m_state = Expanded;
> > }
> >
> > void
> >@@ -586,8 +598,8 @@ namespace std::chrono
> > }
> >
> > string m_buf; // rules() + ' ' + format() OR letters + ' ' +
> format()
> >- uint_least16_t m_pos : 15 = 0; // offset of format() in m_buf
> >- uint_least16_t m_expanded : 1 = 0;
> >+ uint_least16_t m_pos : 14 = 0; // offset of format() in m_buf
> >+ State m_state : 2 = SavePending;
> > duration<int_least16_t, ratio<60>> m_save{};
> > sec32_t m_offset{};
> > sys_seconds m_until{};
> >@@ -695,14 +707,27 @@ namespace std::chrono
> > #endif
> > };
> >
> >- const Rule*
> >- find_active_rule(span<const Rule> rules, sys_seconds t, seconds
> std_offset)
> >+ struct Transitions
> > {
> >- struct Transition {
> >+ struct Entry
> >+ {
> > const Rule* rule;
> > sys_seconds when;
> > };
> >
> >+ Entry prev{nullptr, sys_seconds::min()};
> >+ Entry curr{nullptr, sys_seconds::min()};
> >+ Entry next{nullptr, sys_seconds::max()};
> >+ };
> >+
> >+ // Collect transitions of rules surrounding specified time t:
> >+ // * .curr - rule active directly before or at t,
> >+ // * .prev - rule transition before trans.curr
> >+ // * .next - rule transition directly after t
> >+ Transitions
> >+ find_surrounding_transitions(span<const Rule> rules, sys_seconds t,
> >+ seconds std_offset)
> >+ {
> > const year_month_day date(chrono::floor<days>(t));
> > // Expand the search window for a time near the end
> > // of the year which can be pushed to next/previous
> >@@ -716,16 +741,7 @@ namespace std::chrono
> > if (date.month() == January && date.day() == day(1))
> > --first_year;
> >
> >- // Rule specifying start time as Wall time, should apply
> >- // running 'save' accumulated by earlier rules. To handle
> >- // that we firstly collect transitions surrounding specified
> >- // time t, ignoring the 'save':
> >- // * curr_tran - rule active directly before or at t,
> >- // * prev_tran - rule transition before curr_tran
> >- // * next_tran - rule transition directly after t
> >- Transition prev_tran{nullptr, sys_seconds::min()};
> >- Transition curr_tran{nullptr, sys_seconds::min()};
> >- Transition next_tran{nullptr, sys_seconds::max()};
> >+ Transitions trans;
> > for (const auto& rule : rules)
> > {
> > if (last_year < rule.from) // Rule doesn't apply yet at time t.
> >@@ -761,7 +777,7 @@ namespace std::chrono
> >
> > if (first_year > rule.to)
> > // Rule no longer applies at time t, record last transition
> >- start_before = rule.start_time(rule.to, offset);
> >+ start_before = rule.start_time(rule.to, offset);
> > else if (first_year == last_year)
> > for_year(first_year);
> > else
> >@@ -773,24 +789,35 @@ namespace std::chrono
> > for_year(last_year);
> > }
> >
> >- if (curr_tran.when < start_before)
> >+ if (trans.curr.when < start_before)
> > {
> >- prev_tran = curr_tran;
> >- curr_tran = {&rule, start_before};
> >+ trans.prev = trans.curr;
> >+ trans.curr = {&rule, start_before};
> > }
> >- else if (prev_tran.when < start_before)
> >- prev_tran = {&rule, start_before};
> >+ else if (trans.prev.when < start_before)
> >+ trans.prev = {&rule, start_before};
> >
> >- if (start_after < next_tran.when)
> >- next_tran = {&rule, start_after};
> >+ if (start_after < trans.next.when)
> >+ trans.next = {&rule, start_after};
> > }
> >+ return trans;
> >+ }
> >+
> >+ const Rule*
> >+ find_active_rule(span<const Rule> rules, sys_seconds t, seconds
> std_offset)
> >+ {
> >+ // Rule specifying start time as Wall time, should apply
> >+ // running 'save' accumulated by earlier rules. To handle
> >+ // that we firstly collect transitions surrounding specified
> >+ // time t:
> >+ auto trans = find_surrounding_transitions(rules, t, std_offset);
> >
> > // No rule was active at the time of t, running 'save'
> > // cannot change this output, as we have no save to apply.
> >- if (!curr_tran.rule)
> >+ if (!trans.curr.rule)
> > return nullptr;
> >
> >- auto cascade_save = [](const Rule* from, Transition& to)
> >+ auto cascade_save = [](const Rule* from, Transitions::Entry& to)
> > {
> > if (!from || from->save == seconds(0))
> > return false;
> >@@ -800,19 +827,49 @@ namespace std::chrono
> > return true;
> > };
> >
> >- if (cascade_save(curr_tran.rule, next_tran))
> >- // Running save moved what we considered next_tran to time
> >- // before or at t, in that case next_tran is active rule.
> >- if (next_tran.when <= t)
> >- return next_tran.rule;
> >+ if (cascade_save(trans.curr.rule, trans.next))
> >+ // Running save moved what we considered trans.next to time
> >+ // before or at t, in that case trans.next is active rule.
> >+ if (trans.next.when <= t)
> >+ return trans.next.rule;
> >
> >- if (cascade_save(prev_tran.rule, curr_tran))
> >- // Running save moved what we consider curr_tran to
> >- // time after t, in that case prev_tran is active rule.
> >- if (curr_tran.when > t)
> >- return prev_tran.rule;
> >+ if (cascade_save(trans.prev.rule, trans.curr))
> >+ // Running save moved what we consider trans.curr to
> >+ // time after t, in that case trans.prev is active rule.
> >+ if (trans.curr.when > t)
> >+ return trans.prev.rule;
> >
> >- return curr_tran.rule;
> >+ return trans.curr.rule;
> >+ }
> >+
> >+ const Rule*
> >+ find_active_rule(span<const Rule> rules, local_seconds t, seconds
> std_offset)
> >+ {
> >+ // UNTIL or START time in local time should take a 'save' from
> >+ // active rule. To handle approximate system time at, and
> >+ // collection transitions surrounding it:
> >+ const sys_seconds at(t.time_since_epoch() - std_offset);
> >+ const auto trans = find_surrounding_transitions(rules, at,
> std_offset);
> >+
> >+ auto to_local = [&](const Transitions::Entry& tran, const Rule*
> before)
> >+ {
> >+ local_seconds ls(tran.when.time_since_epoch() + std_offset);
> >+ if (!before || !tran.rule)
> >+ return ls;
> >+ if (tran.rule->when.indicator == at_time::Wall)
> >+ // 'when' was converted from local time without considering
> >+ // running 'save' in first place
> >+ return ls;
> >+ return ls + before->save;
> >+ };
> >+
> >+ // Translate the transitions time to local time, taking 'save'
> >+ // into consideration, and recheck active rule.
> >+ if (to_local(trans.next, trans.curr.rule) <= t)
> >+ return trans.next.rule;
> >+ if (to_local(trans.curr, trans.prev.rule) > t)
> >+ return trans.prev.rule;
> >+ return trans.curr.rule;
> > }
> >
> > const Rule*
> >@@ -835,6 +892,43 @@ namespace std::chrono
> > }
> > return first;
> > }
> >+
> >+ bool
> >+ ZoneInfo::calc_save(span<const Rule> all_rules) noexcept
> >+ {
> >+ // Expanded rule, or the save value was already computed.
> >+ if (m_state < SavePending)
> >+ return false;
> >+
> >+ // Find the rules named by rules()
> >+ span<const Rule> sel_rules
> >+ = ranges::equal_range(all_rules, rules(), ranges::less{},
> &Rule::name);
> >+ if (sel_rules.empty())
> >+ __throw_runtime_error("std::chrono::time_zone::get_info: invalid
> data");
> >+
> >+ if (m_state == UntilPending)
> >+ {
> >+ // UNTIL was specified in Wall time, so it is affected by 'save'.
> >+ // Convert it back to local time, and find active rule using
> that.
> >+ const local_seconds lu(m_until.time_since_epoch() + m_offset);
> >+ if (const Rule* rule = find_active_rule(sel_rules, lu -
> seconds(1), m_offset))
> >+ if (rule->save.count() != 0)
> >+ {
> >+ m_state = SaveKnown;
> >+ m_save = duration_cast<minutes>(rule->save);
> >+ m_until -= rule->save;
> >+ return true;
> >+ }
> >+ }
> >+ else
> >+ // UNTIL was either specified in Universal time, or Standard
> >+ // time (known total offset). m_until is corresponding sys_time
> point.
> >+ if (const Rule* rule = find_active_rule(sel_rules, m_until -
> seconds(1), m_offset))
> >+ m_save = duration_cast<minutes>(rule->save);
> >+
> >+ m_state = SaveKnown;
> >+ return false;
> >+ }
> > } // namespace
> > #endif // TZDB_DISABLED
> >
> >@@ -966,19 +1060,35 @@ namespace std::chrono
> > // Find the transition info for the time point.
> > auto i = ranges::upper_bound(infos, tp, ranges::less{},
> &ZoneInfo::until);
> >
> >+ // Perform the comparision on save adjusted until values (if needed)
> >+ // Assumme that applying the save will not change relative order of
>
> "Assume"
>
> >+ // ZoneInfo objects.
> >+ if (i != infos.begin() && i[-1].calc_save(node->rules) &&
> (i[-1].until() > tp))
> >+ --i;
> >+ else if (i != infos.end() && i->calc_save(node->rules) &&
> (i->until() <= tp))
> >+ ++i;
> >+
> > if (i == infos.end())
> > {
> > if (infos.empty())
> > __throw_runtime_error("std::chrono::time_zone::get_info: invalid
> data");
> >- tp = (--i)->until();
> >- }
> >+ (--i)->calc_save(node->rules);
> >+ tp = i->until();
> >+ }
> >+ else // Guarantee that i->until() is correct
> >+ i->calc_save(node->rules);
> >+
> >
> > sys_info info;
> >
> > if (i == infos.begin())
> > info.begin = sys_days(year::min()/January/1);
> > else
> >- info.begin = i[-1].until();
> >+ {
> >+ ZoneInfo& prev = i[-1];
> >+ prev.calc_save(node->rules);
> >+ info.begin = prev.until();
>
> I think the line above is indented with spaces, but the two before it
> use TAB.
>
> >+ }
> >
> > if (i->to(info)) // We already know a sys_info for this time.
> > return info;
> >@@ -990,10 +1100,10 @@ namespace std::chrono
> > const ZoneInfo& ri = *i;
> >
> > // Find the rules named by ri.rules()
> >- auto rules = ranges::equal_range(node->rules, ri.rules(),
> >- ranges::less{}, &Rule::name);
> >+ span<const Rule> rules = ranges::equal_range(node->rules, ri.rules(),
> >+ ranges::less{},
> &Rule::name);
> >
> >- if (ranges::empty(rules))
> >+ if (rules.empty())
> > __throw_runtime_error("std::chrono::time_zone::get_info: invalid
> data");
> >
> > vector<pair<sys_info, string_view>> new_infos;
> >@@ -1012,8 +1122,6 @@ namespace std::chrono
> > // rule changes always occur between periods of standard time.
> > info.offset = ri.offset();
> > info.save = 0min;
> >- // XXX The ri.until() time point should be
> >- // "interpreted using the rules in effect just before the transition"
> > info.end = ri.until();
> > info.abbrev = ri.format();
> >
> >@@ -2612,6 +2720,7 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > if (rules == "-")
> > {
> > // Standard time always applies, no DST.
> >+ inf.m_save = minutes(0);
> > }
> > else
> > {
> >@@ -2645,14 +2754,20 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > inf.m_until -= seconds(inf.m_offset);
> > if (t.indicator != at_time::Standard)
> > {
> >- if (inf.m_expanded) // Not a named Rule, SAVE is known
> now.
> >+ if (inf.expanded()) // Not a named Rule, SAVE is known
> now.
> > inf.m_until -= inf.m_save;
> >- // else Named Rule, SAVE is unknown. FIXME: PR 116110
> >+ else // else Named Rule, SAVE is unknown, mark as pending
> >+ inf.m_state = ZoneInfo::UntilPending;
> > }
> > }
> > }
> > else
> >- inf.m_until = sys_days(year::max()/December/31);
> >+ {
> >+ inf.m_until = sys_days(year::max()/December/31);
> >+ // The line does not define UNTIL, so it is not affected by save
> >+ if (!inf.expanded())
> >+ inf.m_state = ZoneInfo::SaveKnown;
> >+ }
> >
> > in.clear(in.rdstate() & ios::eofbit);
> > in.exceptions(ex);
> >diff --git a/libstdc++-v3/testsuite/std/time/time_zone/116110.cc
> b/libstdc++-v3/testsuite/std/time/time_zone/116110.cc
> >index 26b9ba33c31..1ec01d35955 100644
> >--- a/libstdc++-v3/testsuite/std/time/time_zone/116110.cc
> >+++ b/libstdc++-v3/testsuite/std/time/time_zone/116110.cc
> >@@ -65,8 +65,8 @@ test_apia()
> > auto* tz = locate_zone("Pacific/Apia");
> > local_seconds t = local_days(2011y/December/29) + 24h;
> >
> >- // FIXME: this should be + 10h but we do not account for DST yet, so +
> 11h.
> >- sys_seconds ut(t.time_since_epoch() + 11h );
> >+ // The wall UNTIL is interpreted in the prior offset (-11h + 1h)
> >+ sys_seconds ut(t.time_since_epoch() + 10h);
> > sys_info info;
> > info = tz->get_info(ut - 1s);
> > VERIFY( info.offset == (-11h + info.save) );
> >diff --git a/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc
> b/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc
> >new file mode 100644
> >index 00000000000..b3bf4eb1aa9
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/time/time_zone/pr116110_named.cc
> >@@ -0,0 +1,74 @@
> >+// { 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* } }
> >+
> >+// Africa/Algiers 1977-10-21: a Zone line whose RULES references a
> >+// named Rule and whose UNTIL is a wall-time expression. The wall
> >+// UNTIL is interpreted using the SAVE value in force just before the
> >+// boundary (the May-6 rule's save=1, not the Oct-21 rule's save=0
> >+// even though the Oct-21 rule fires at the same wall instant).
> >+//
> >+// Rule d 1977 May 6 0:00 wall save=1
> >+// Rule d 1977 Oct 21 0:00 wall save=0
> >+// Z A 0 d WE%sT 1977 O 21
> >+// 1 d CE%sT
> >+
> >+#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_pr116110_named
> >+R d 1977 o - May 6 0 1 S
> >+R d 1977 o - O 21 0 0 -
> >+Z Test/Algiers 0 d WE%sT 1977 O 21
> >+ 1 d CE%sT
> >+)";
> >+
> >+ const auto& db = reload_tzdb();
> >+ VERIFY( override_used );
> >+ VERIFY( db.version == "test_pr116110_named" );
> >+
> >+ auto* tz = locate_zone("Test/Algiers");
> >+
> >+ // Just before the boundary: still in the first Zone line under
> >+ // the May-6 rule (save=1, WEST, total +1).
> >+ auto pre = tz->get_info(sys_days{1977y/October/20} + 22h);
> >+ VERIFY( pre.offset == 1h );
> >+ VERIFY( pre.save == 1h );
> >+ VERIFY( pre.abbrev == "WEST" );
> >+
> >+ // The boundary is Oct 20 23:00 UTC (= wall 00:00 - stdoff(0) -
> save(1)).
> >+ // At and after the boundary we are in the second line (CET).
> >+ auto at = tz->get_info(sys_days{1977y/October/20} + 23h);
> >+ VERIFY( at.offset == 1h ); // stdoff 1 + save 0 (CET, second line)
> >+ VERIFY( at.save == 0min );
> >+ VERIFY( at.abbrev == "CET" );
> >+
> >+ // A second query inside the second line, well clear of the boundary.
> >+ auto after = tz->get_info(sys_days{1977y/October/21} + 12h);
> >+ VERIFY( after.offset == 1h );
> >+ VERIFY( after.save == 0min );
> >+ VERIFY( after.abbrev == "CET" );
> >+
> >+ // A query inside the [Oct 20 23:00, Oct 21 00:00] UTC window must be
> >+ // in the second line, not in a leftover stretch from the first line.
> >+ auto window = tz->get_info(sys_days{1977y/October/20} + 23h + 30min);
> >+ VERIFY( window.offset == 1h );
> >+ VERIFY( window.abbrev == "CET" );
> >+}
> >--
> >2.55.0
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260716/08b547f8/attachment-0001.htm>
More information about the Libstdc++
mailing list