On Thu, 16 Jul 2026 at 12:06, Tomasz Kamiński <[email protected]> wrote:
>
> 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 the regression introduced by r17-2060-gf7cde200320e08,
> where new differences between libstdc++ output and date library where
> introduced fro some zones (e.g. Europe/Lisbon in 1976). With this fixup,
> the patch is now pure improvement in consistency.
>
> libstdc++-v3/ChangeLog:
>
>         * 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.
> ---
> Using your test program, I compared current trunk state with GCC-16
> and found that we have new differences caused by my previous patch.
> All of them are now addressed. Note that this would most likely be
> fixed in follow-up patches, but I think that r17-2060-gf7cde200320e08,
> with this fix may be good candidate for backport, as they should not
> impact performance.
>
> Testing on x86_64-linux locally. *time_zone* test passed with all
> standard modes. OK for trunk?

OK with one typo fixed ("consideration")


>
>  libstdc++-v3/src/c++20/tzdb.cc                |  2 +-
>  .../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 998a363151d..4ecce9e214e 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 2707e3fcd33..f270a13fcad 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 considration 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();
>  }
> --
> 2.55.0
>

Reply via email to