https://gcc.gnu.org/g:66fbafb77551bcb01abd28c5c59f73fa576be0f6
commit r15-11157-g66fbafb77551bcb01abd28c5c59f73fa576be0f6 Author: Jonathan Wakely <[email protected]> Date: Sat Mar 14 10:32:11 2026 +0000 libstdc++: Fix time zone transitions for Rule changes during DST [PR116110] The Australia/Broken_Hill example in PR libstdc++/116110 demonstrates a bug in the time zone code. The current code gives incorrect results when a zone changes from one named Rule to another during DST, e.g. the Broken_Hill time zone uses: 9:30 AN AC%sT 2000 9:30 AS AC%sT So the AS Rules take effect on 2000 Jan 1 which is during DST (which runs from October to March). The fix for this is to update info.offset and info.save when we find an active rule, instead of only updating the 'letters' variable. The new tests for Pacific/Kiritimati and Pacific/Apia have some FIXME comments. The UNTIL times of the zone changes are incorrectly interpreted as UTC not wall time (due to the main topic of Bug 116110), and the "24" time for the Pacific/Apia zone is incorrectly ignored so that the change happens 24h too early (due to Bug 124513). Those tests will need to be adjusted later when the bugs are fixed. libstdc++-v3/ChangeLog: PR libstdc++/116110 * src/c++20/tzdb.cc (time_zone::_M_get_sys_info): Update info.offset and info.save to values from the active rule. * testsuite/std/time/time_zone/116110.cc: New test. Reviewed-by: Tomasz KamiĆski <[email protected]> (cherry picked from commit 663e5ade184813a8a5f6f76b873c3e212c1e8e75) Diff: --- libstdc++-v3/src/c++20/tzdb.cc | 6 +- .../testsuite/std/time/time_zone/116110.cc | 85 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc index e92590079f75..5c54606a9761 100644 --- a/libstdc++-v3/src/c++20/tzdb.cc +++ b/libstdc++-v3/src/c++20/tzdb.cc @@ -909,7 +909,11 @@ namespace std::chrono } if (active_rule) - letters = active_rule->letters; + { + info.offset = ri.offset() + active_rule->save; + info.save = chrono::duration_cast<minutes>(active_rule->save); + letters = active_rule->letters; + } else if (first_std) letters = first_std->letters; } diff --git a/libstdc++-v3/testsuite/std/time/time_zone/116110.cc b/libstdc++-v3/testsuite/std/time/time_zone/116110.cc new file mode 100644 index 000000000000..d86011cf5739 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/time_zone/116110.cc @@ -0,0 +1,85 @@ +// { dg-do run { target c++20 } } +// { dg-require-effective-target tzdb } +// { dg-require-effective-target cxx11_abi } + +#include <chrono> +#include <testsuite_hooks.h> + +using namespace std::chrono; + +void +test_broken_hill() +{ + /* + R AN 1996 2005 - Mar lastSun 2s 0 S + R AN 2000 o - Aug lastSun 2s 1 D + Z Australia/Broken_Hill 9:25:48 - LMT 1895 Feb + 10 - AEST 1896 Aug 23 + 9 - ACST 1899 May + 9:30 AU AC%sT 1971 + 9:30 AN AC%sT 2000 + 9:30 AS AC%sT + */ + auto* tz = locate_zone("Australia/Broken_Hill"); + auto info = tz->get_info(sys_days(2000y/February/29d) + 23h + 23min + 23s); + VERIFY( info.offset == 630min ); + VERIFY( info.save == 60min ); + VERIFY( info.abbrev == "ACDT" ); +} + +void +test_kiritimati() +{ + /* No named rules involved here, just change of fixed STDOFF at 1994-12-31: + Z Pacific/Kiritimati -10:29:20 - LMT 1901 + -10:40 - %z 1979 Oct + -10 - %z 1994 Dec 31 + 14 - %z + */ + auto* tz = locate_zone("Pacific/Kiritimati"); + local_seconds t = local_days(1994y/December/31); + + sys_seconds ut(t.time_since_epoch() /* FIXME: should be + 10h */); + sys_info info; + info = tz->get_info(ut - 1s); + VERIFY( info.offset == -10h ); + VERIFY( info.save == 0h ); + VERIFY( info.abbrev == "-10" ); + info = tz->get_info(ut); + VERIFY( info.offset == 14h ); + VERIFY( info.save == 0h ); + VERIFY( info.abbrev == "+14" ); +} + +void +test_apia() +{ + /* Refers to same named rule before/after change at 2011-12-29 24:00 + but the offset changed from -11h+1h to +13h+1h + Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5 + -11:26:56 - LMT 1911 + -11:30 - %z 1950 + -11 WS %z 2011 Dec 29 24 + 13 WS %z + */ + auto* tz = locate_zone("Pacific/Apia"); + local_seconds t = local_days(2011y/December/29) + 24h; + + sys_seconds ut(t.time_since_epoch() /* FIXME: should be + 10h */ - 24h ); + sys_info info; + info = tz->get_info(ut - 1s); + VERIFY( info.offset == (-11h + info.save) ); + VERIFY( info.save == 1h ); + VERIFY( info.abbrev == "-10" ); + info = tz->get_info(ut); + VERIFY( info.offset == (13h + info.save) ); + VERIFY( info.save == 1h ); + VERIFY( info.abbrev == "+14" ); +} + +int main() +{ + test_broken_hill(); + test_kiritimati(); + test_apia(); +}
