[PATCH v2 1/3] libstdc++: Fix time zone transitions for Rule changes during DST [PR116110]

Tomasz Kaminski tkaminsk@redhat.com
Thu Mar 19 07:05:14 GMT 2026


On Wed, Mar 18, 2026 at 11:23 PM Jonathan Wakely <jwakely@redhat.com> wrote:

> 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 124554). 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.
>
> ---
>
> v2: Additional tests for the Pacific time zones Tomasz identified.
>
LGTM.

>
> Tested x86_64-linux.
>
>  libstdc++-v3/src/c++20/tzdb.cc                |  6 +-
>  .../testsuite/std/time/time_zone/116110.cc    | 85 +++++++++++++++++++
>  2 files changed, 90 insertions(+), 1 deletion(-)
>  create mode 100644 libstdc++-v3/testsuite/std/time/time_zone/116110.cc
>
> diff --git a/libstdc++-v3/src/c++20/tzdb.cc
> b/libstdc++-v3/src/c++20/tzdb.cc
> index 53441880ae6e..e26a9ee38072 100644
> --- a/libstdc++-v3/src/c++20/tzdb.cc
> +++ b/libstdc++-v3/src/c++20/tzdb.cc
> @@ -917,7 +917,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();
> +}
> --
> 2.53.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260319/08b008a2/attachment.htm>


More information about the Libstdc++ mailing list