[PATCH] libstdc++: Cache leap second expiry time in <chrono> [PR123165]
Jonathan Wakely
jwakely@redhat.com
Wed May 6 16:09:39 GMT 2026
On Wed, 6 May 2026 at 17:05, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
>
> On Wed, May 6, 2026 at 5:14 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>>
>> On Wed, 6 May 2026 at 15:47, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>> >
>> >
>> >
>> > On Wed, May 6, 2026 at 4:18 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>> >>
>> >> This change allows the hardcoded list of leap seconds in <chrono> to be
>> >> used even when the program is executing after the hardcoded expiry date
>> >> in that header. If the OS-provided leapseconds file has a later expiry
>> >> date (or contains new leap seconds added after the one in 2017) then the
>> >> new __detail::__leap_seconds_expiry() function will return that new
>> >> dynamically-obtained expiry date. The __detail::__get_leap_second_info
>> >> function in the header can check that new expiry date instead of relying
>> >> only on the hardcoded one.
>> >>
>> >> This change means that in the worst case we now make two calls into the
>> >> library (one to get the dynamic expiry date and then possibly another
>> >> one to get the actual list of new leap seconds). Previously we just make
>> >> one, to get the list. The change seems worthwhile, because it means that
>> >> in more cases we don't need to increment+decrement the reference count
>> >> on a tzdb object and use its leapseconds vector, we can just use the
>> >> hardcoded array.
>> >>
>> >> The new expiry date is stored in a global variable, rather than being
>> >> per-tzdb object, but that seems fine because we only ever expect that
>> >> expiry date to move forwards in time, not to move forwards and backwards
>> >> as new tzdb objects are loaded by chrono::reload_tzdb(). Even if a new
>> >> list of leap seconds is loaded, we still expect an expiry date that was
>> >> loaded previously to be valid.
>> >
>> > I was wondering about it in connection to the test that overrides the zone directory,
>> > and may move the date around. But for those already impacted by the fact that
>> > any date with an expiry date before one that we hardcode is ignored. However,
>> > for that reason, I would ensure we do not move the date backward; suggestion below.
>> > (In also could move backward if reading #experies fails).
>> >
>> >>
>> >>
>> >> libstdc++-v3/ChangeLog:
>> >>
>> >> PR libstdc++/123165
>> >> * acinclude.m4 (libtool_VERSION): Bump version.
>> >> * config/abi/pre/gnu.ver (GLIBCXX_3.4.36): Add new symbol
>> >> version and export new symbol.
>> >> * configure: Regenerate.
>> >> * include/std/chrono (__detail::__leap_seconds_expiry):
>> >> Declare new function.
>> >> (__detail::__get_leap_second_info): Use new function.
>> >> * src/c++20/tzdb.cc (__detail::__leap_seconds_expiry): Define.
>> >> (tzdb_list::_Node::_S_read_leap_seconds): Read 'expires' line
>> >> from leapseconds file and optionally update global cache.
>> >> * testsuite/std/time/tzdb/leap_seconds.cc: Add expires line to
>> >> replacement leapseconds file.
>> >> * testsuite/util/testsuite_abi.cc: Update known_versions and
>> >> latestp.
>> >> ---
>> >>
>> >> Tested x86_64-linux.
>> >>
>> >>
>> >> libstdc++-v3/acinclude.m4 | 2 +-
>> >> libstdc++-v3/config/abi/pre/gnu.ver | 7 ++
>> >> libstdc++-v3/configure | 2 +-
>> >> libstdc++-v3/include/std/chrono | 9 +-
>> >> libstdc++-v3/src/c++20/tzdb.cc | 88 ++++++++++++++++---
>> >> .../testsuite/std/time/tzdb/leap_seconds.cc | 1 +
>> >> libstdc++-v3/testsuite/util/testsuite_abi.cc | 3 +-
>> >> 7 files changed, 96 insertions(+), 16 deletions(-)
>> >>
>> >> diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
>> >> index 8dc9e17b214c..3a4b11a98a28 100644
>> >> --- a/libstdc++-v3/acinclude.m4
>> >> +++ b/libstdc++-v3/acinclude.m4
>> >> @@ -4085,7 +4085,7 @@ changequote([,])dnl
>> >> fi
>> >>
>> >> # For libtool versioning info, format is CURRENT:REVISION:AGE
>> >> -libtool_VERSION=6:35:0
>> >> +libtool_VERSION=6:36:0
>> >>
>> >> # Everything parsed; figure out what files and settings to use.
>> >> case $enable_symvers in
>> >> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
>> >> index bd4da6418295..35aaf89984d1 100644
>> >> --- a/libstdc++-v3/config/abi/pre/gnu.ver
>> >> +++ b/libstdc++-v3/config/abi/pre/gnu.ver
>> >> @@ -2623,6 +2623,13 @@ GLIBCXX_3.4.35 {
>> >>
>> >> } GLIBCXX_3.4.34;
>> >>
>> >> +# GCC 17.1.0
>> >> +GLIBCXX_3.4.36 {
>> >> +
>> >> + _ZNSt6chrono8__detail21__leap_seconds_expiryEv;
>> >> +
>> >> +} GLIBCXX_3.4.35;
>> >> +
>> >> # Symbols in the support library (libsupc++) have their own tag.
>> >> CXXABI_1.3 {
>> >>
>> >> diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
>> >> index 6713e4504b1c..013c388b9c2f 100755
>> >> --- a/libstdc++-v3/configure
>> >> +++ b/libstdc++-v3/configure
>> >> @@ -51418,7 +51418,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;}
>> >> fi
>> >>
>> >> # For libtool versioning info, format is CURRENT:REVISION:AGE
>> >> -libtool_VERSION=6:35:0
>> >> +libtool_VERSION=6:36:0
>> >>
>> >> # Everything parsed; figure out what files and settings to use.
>> >> case $enable_symvers in
>> >> diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
>> >> index 674f867dcdc7..228293f12bef 100644
>> >> --- a/libstdc++-v3/include/std/chrono
>> >> +++ b/libstdc++-v3/include/std/chrono
>> >> @@ -3217,6 +3217,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >>
>> >> namespace __detail
>> >> {
>> >> + // The list below is known to be valid until (at least) this date.
>> >> + // This value is defined in the library (possibly to a newer value than
>> >> + // the hardcoded value below) and can change at runtime.
>> >> + sys_seconds __leap_seconds_expiry();
>> >> +
>> >> inline leap_second_info
>> >> __get_leap_second_info(sys_seconds __ss, bool __is_utc)
>> >> {
>> >> @@ -3252,12 +3257,12 @@ namespace __detail
>> >> 1435708800, // 1 Jul 2015
>> >> 1483228800, // 1 Jan 2017
>> >> };
>> >> +#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI // use chrono::tzdb
>> >> // The list above is known to be valid until (at least) this date
>> >> // and only contains positive leap seconds.
>> >> constexpr sys_seconds __expires(1798416000s); // 2026-12-28 00:00:00 U
>> >
>> >
>> >>
>> >> -#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
>> >> - if (__ss > __expires)
>> >> + if (__ss > __expires && __ss > __leap_seconds_expiry())
>> >> {
>> >> // Use updated leap_seconds from tzdb.
>> >> size_t __n = std::size(__leaps);
>> >> diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
>> >> index b0fbfc46a6d3..ba0020814ba8 100644
>> >> --- a/libstdc++-v3/src/c++20/tzdb.cc
>> >> +++ b/libstdc++-v3/src/c++20/tzdb.cc
>> >> @@ -1251,6 +1251,40 @@ namespace std::chrono
>> >> }
>> >> #endif // TZDB_DISABLED
>> >>
>> >> +namespace
>> >> +{
>> >> +#if ATOMIC_LONG_LOCK_FREE == 2
>> >> + using expiry_type = unsigned long;
>> >> +#else
>> >> + using expiry_type = unsigned;
>> >> +#endif
>> >> + // When GCC 16.1 was released with stable C++20 chrono support (in 2026),
>> >> + // the last leap second in the list was the one in 2017. If another leap
>>
>> >> + // second is introduced in future, objects compiled by GCC 16.1 will not
>> >> + // contain that leap second in the hardcoded list in <chrono>.
>> >> + // This expiry time must be less than that first post-2017 leap second,
>> >> + // so that old copies of __get_leap_second_info will use tzdb::leap_seconds
>> >> + // which will contain the post-2017 leap seconds.
>> >> + // If no new leap second is introduced, then this expiry time can just be
>> >> + // updated to the 'expires' value read from the leapseconds file.
>> >> + // tzdata 2026a leapseconds list expires at 2026-12-28 00:00:00 UTC
>> >> + constinit std::atomic<expiry_type> leap_seconds_expiry{leap27th_expiry};
>> >
>> > Could you extract the __expires from tzdb_list::_Node::_S_read_leap_seconds()
>> > (in this file) as the global (but TU-local) variables, and use the __expires as initializer here,
>> > so we do not need to update two prices. This will not add any symbols to the library, as we are in source file.
>> > (This is why I am not suggesting doing that in header, as we will need to make the data inline).
>
> OK, as I understand the code, this value can be updated to one second
> prior to the date of the 28th leap second, or current time, if none was inserted.
> However, the commit message does not contain the number of leap seconds know for GCC17,
>
> Maybe change comment to something like this:
> // When GCC 16.1 was released with stable C++20 chrono support (in 2026),
> // the leap second data contained 27 entries, with the last one in 2017.
> constexpr min_leap_second_count = 27; /// review: use that as magic number, so searching for it will find this comment
Ack.
More information about the Libstdc++
mailing list