On Tue, Jun 30, 2026 at 12:24 PM Ville Voutilainen < [email protected]> wrote:
> On Tue, 30 Jun 2026 at 11:00, Tomasz Kamiński <[email protected]> wrote: > > > > When _M_get_sys_info seeds a Zone line by looking up the active rule > > just before info.begin, the previous code 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 most zones this gives the right answer because the search > > only matters when no rule has fired yet, but for zones whose rule > > set has wall-time rules whose effective firing time depends on a > > prior rule's save it produces wrong answers. > > > > Canonical case: Europe/Paris around 1945. France's rules > > > > R Fr 1945 o - Apr 2 2 2 M > > R Fr 1945 o - Sep 16 3 0 - > > > > both use plain wall time. In Paris's stdoff=1 frame, the September > > rule's at_time of 03:00 wall translates to UT Sep 16 02:00 if no > > prior save is applied, but to UT Sep 16 00:00 once the running save > > of 2h from the April rule is taken into account. When seeding a > > sys_info whose info.begin falls between those two values, the simple > > search picks the April rule (save=2 → CEMT, total offset 3h) when > > the correct answer is the September rule (save=0 → CET, total offset > > 1h). libstdc++ reports this as a sustained CEMT stretch where zic > > and libc agree on CET. > > > > To address above the finding algorithm, is now expanded to collect > > three rule transitions around specified time t, while continuing to > > ignore the save. > > * curr_tran: transition happening before or at time t, > > * prev_tran: transition preceding above transition, > > * next_tran: transition happening after tiem t. > > > > This collects sufficient information to adjust the start_time (if > > Wall time is used) for curr_tran (save of prev_tran) and next_tran > > (save of curr_tran). Assuming that applying save value does not > > change order of transition (cascading save would be ill-defined > > otherwise), after the adjustment the actual active rule is: > > * next_tran.rule: if the adjustment pushed next_tran.when to > > time before or at t, which happen for positive save (see > > test_positive), > > * prev_tran.rule: if adjustment pushed curr_tran.when to time > > after time t, which happens for negative save (see test_negative), > > * curr_tran.rule. > > > > For the time at the start (Jan/1) or end of the year (December/31), > > for each rule, in addition to transition in year of t, we check > > transitions in previous or next year respectively (years in range > > [first_year, last_year]). This handle rules whose firing (specified > > in local time) crosses a year boundary due to a large stdoff or save. > > One example is Pacific/Auckland's 1946 Jan 1 rule, in stdoff=12h, > > fires at 1945-12-31 11:30 UT, see test_next_year. > > > > The fallback "earliest STD rule" logic is preserved for the case > > where no rule has fired yet, but is extracted to separate function. > > This lookup is optimized, by searching the rules by name, from, and > > save in that order, grouping std rules in given year together. > > > > PR libstdc++/124853 > > > > libstdc++-v3/ChangeLog: > > > > * src/c++20/tzdb.cc > > (time_zone::_M_get_sys_info): Extract code blocks to > > separate functions, and invoke them. > > (<unnamed>::find_active_rule): Modify algorithm to > > handle cascading saves. > > (<unnamed>::find_first_std): Simplified implementation > > benefiting from reordering of rules. > > (chrono::reload_tzdb): Sort rules by name, from and save. > > * testsuite/std/time/time_zone/wall_cascade.cc: New test. > > > > Co-authored-by: Álvaro Begué <[email protected]> > > Signed-off-by: Tomasz Kamiński <[email protected]> > > Signed-off-by: Álvaro Begué <[email protected]> > > --- > > v4 restores the expansion of the year range to previous/next year > > from Álvaro v2 versions. It also adds a test for situations when > > expansion is necessary (based on Pacific/Auckland). This version > > is more targeted, and only expands the range for Dec/31 and Jan/1. > > We do not check local time (t - stdoff) date, as save may still > > move the date to previous/next year. > > > > Futhermore: > > * we record last transition for the rules that no longer applies, > > as one happening before (see test_eariel) > > That should probably be test_earlier, and s/eariel/earlier/g? > Yes, fixed also in-code locally: diff --git a/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc b/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc index e06673f08ac..2707e3fcd33 100644 --- a/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc +++ b/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc @@ -157,7 +157,7 @@ Z Pacific/AucklandUT 11:39:4 - LMT 1868 N 2 void test_prev_year() { - // The syntetic version of above, where the local + // The synthetic version of above, where the local // time for rule is moved to previous year. // The NZ 1946 rule triggers at 1946 Dec 31 22:00:00 // local time, which correspond to 1947 Jan 1 10:00:00 UT. @@ -199,10 +199,10 @@ Z Test/PrevYear -11:39:4 - LMT 1868 N 2 } void -test_eariel_year() +test_earlier_year() { - // Syntethic example where PY 1941 rule is still running. - std::ofstream("tzdata.zi") << R"(# version test_eariel_year + // Synthetic example where PY 1941 rule is still running. + std::ofstream("tzdata.zi") << R"(# version test_earlier_year R EY 1934 1941 - Ap lastSu 2 0 M R EY 1934 1940 - S lastSu 2 0:30 S Z Test/EarielYear 11:39:4 - LMT 1868 N 2 @@ -212,7 +212,7 @@ Z Test/EarielYear 11:39:4 - LMT 1868 N 2 const auto& db = reload_tzdb(); VERIFY( override_used ); // If this fails then XFAIL for the target. - VERIFY( db.version == "test_eariel_year" ); + VERIFY( db.version == "test_earlier_year" ); auto* utz = locate_zone("Test/EarielYear"); auto at_boundary @@ -229,5 +229,5 @@ main() test_negative(); test_next_year(); test_prev_year(); - test_eariel_year(); + test_earlier_year(); }
