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();
 }

Reply via email to