https://gcc.gnu.org/g:610134a51fa3d0bcd3e0980c6574557172dc88c0
commit r17-2462-g610134a51fa3d0bcd3e0980c6574557172dc88c0 Author: Tomasz Kamiński <[email protected]> Date: Thu Jul 16 12:50:32 2026 +0200 libstdc++: Apply rule triggering at Zone line transition [PR124853] When seeding zone information from the application of the rules prior the transition, we should consider the rule triggering exactly at boundary, not only prior, i.e. call find_active_rule with info.begin. Due the rule_start - t < days(1) check (that is not removed yet), such transitions were effectively ignored. This addresses regressions introduced by r17-2060-gf7cde200320e08, where new differences between outputs of libstdc++ and date library were introduced for some zones (e.g. Europe/Lisbon in 1976). With this fixup, the patch is now pure improvement in consistency. libstdc++-v3/ChangeLog: PR libstdc++/124853 * src/c++20/tzdb.cc (time_zone::_M_get_sys_info): Find rules firing exactly at transition time. * testsuite/std/time/time_zone/wall_cascade.cc: Add test for Europe/Lisbon, and corrected test_negative. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/src/c++20/tzdb.cc | 2 +- .../testsuite/std/time/time_zone/wall_cascade.cc | 52 +++++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc index 998a363151dc..4ecce9e214ec 100644 --- a/libstdc++-v3/src/c++20/tzdb.cc +++ b/libstdc++-v3/src/c++20/tzdb.cc @@ -1027,7 +1027,7 @@ namespace std::chrono // SAVE and LETTERS values. There may not be a Rule for the period // before the first DST transition, so find the earliest DST->STD // transition and use the LETTERS from that. - if (const Rule* active_rule = find_active_rule(rules, info.begin - seconds(1), ri.offset())) + if (const Rule* active_rule = find_active_rule(rules, info.begin, ri.offset())) { info.offset = ri.offset() + active_rule->save; info.save = chrono::duration_cast<minutes>(active_rule->save); 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 2707e3fcd331..3529c2de8523 100644 --- a/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc +++ b/libstdc++-v3/testsuite/std/time/time_zone/wall_cascade.cc @@ -76,11 +76,11 @@ test_negative() // running save. // // Two-line zone whose second line begins at 1945 Sep 16 01:00 UT, - // at the cascaded firing time (Sep 16 01:00 UT), but after + // before the cascaded firing time (Sep 16 02:00 UT), but after // non-cascaded firing time (Sep 16 00:00 UT) of the September rule. // The seeding must pick the April rule (save=-2, CEST) at info.begin. std::ofstream("tzdata.zi") << R"(# version test_negative_cascade -R Fr 1945 o - Apr 2 2 -2 M +R Fr 1945 o - Apr 2 2 -3 M R Fr 1945 o - Sep 16 0 0 - Z Test/Negative 0 - X 1945 Sep 16 1u 1 Fr CE%sT @@ -96,15 +96,24 @@ Z Test/Negative 0 - X 1945 Sep 16 1u // one second after. auto info = tz->get_info(sys_seconds{ sys_days(1945y/September/16) + 1h + 1s}); - VERIFY( info.offset == -1h ); - VERIFY( info.save == -2h ); + VERIFY( info.offset == -2h ); + VERIFY( info.save == -3h ); VERIFY( info.abbrev == "CEMT" ); // The boundary instant. auto at_boundary = tz->get_info(sys_seconds{sys_days(1945y/September/16) + 1h}); - VERIFY( at_boundary.offset == -1h ); - VERIFY( at_boundary.save == -2h ); + VERIFY( at_boundary.offset == -2h ); + VERIFY( at_boundary.save == -3h ); + + // Test the firing of Sep 16 rule + auto at_sep_rule = tz->get_info(sys_seconds{ + sys_days(1945y/September/16) + 2h}); + // The transition_window < 1d condition triggers, and + // transition is ignored. + // VERIFY( at_sep_rule.offset == 1h ); + // VERIFY( at_sep_rule.save == 0h ); + // VERIFY( at_sep_rule.abbrev == "CET" ); } void @@ -222,6 +231,36 @@ Z Test/EarielYear 11:39:4 - LMT 1868 N 2 VERIFY( at_boundary.abbrev == "EYMT" ); } +void +test_at_boundary() +{ + std::ofstream("tzdata.zi") << R"(# version test_at_boundary +R p 1947 1966 - Ap Su>=1 2s 1 S +R p 1947 1965 - O Su>=1 2s 0 - +R p 1976 o - S lastSu 1 0 - +R p 1977 o - Mar lastSu 0s 1 S +R p 1977 o - S lastSu 0s 0 - +Z Europe/Lisbon -0:36:45 - LMT 1884 +1 - CET 1976 S 26 1 +0 p WE%sT 1986 + )"; + + const auto& db = reload_tzdb(); + VERIFY( override_used ); // If this fails then XFAIL for the target. + VERIFY( db.version == "test_at_boundary" ); + + // The change from CET to WE%sT line happens 1976 Sep 26 00:00:00 UT, + // should take into consideration 1976 lastSu rule that fires at the + // same time (running save is 1h from 1966 Ap rule application), + // and start in standard time (WET period). + auto* utz = locate_zone("Europe/Lisbon"); + auto at_boundary + = utz->get_info(sys_seconds{sys_days(1976y/September/26) + 0h}); + VERIFY( at_boundary.offset == 0h ); + VERIFY( at_boundary.save == 0min ); + VERIFY( at_boundary.abbrev == "WET" ); +} + int main() { @@ -230,4 +269,5 @@ main() test_next_year(); test_prev_year(); test_earlier_year(); + test_at_boundary(); }
