[PATCH v4 4/5] libstdc++: Cascade wall-time saves in lazy expansion seeding [PR124853]
Tomasz Kaminski
tkaminsk@redhat.com
Tue Jun 30 10:31:42 GMT 2026
On Tue, Jun 30, 2026 at 12:24 PM Ville Voutilainen <
ville.voutilainen@gmail.com> wrote:
> On Tue, 30 Jun 2026 at 11:00, Tomasz Kamiński <tkaminsk@redhat.com> 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é <alvaro.begue@gmail.com>
> > Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> > Signed-off-by: Álvaro Begué <alvaro.begue@gmail.com>
> > ---
> > 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();
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260630/a42b1155/attachment-0001.htm>
More information about the Libstdc++
mailing list