On Mon, 20 Jul 2026 at 11:16, Jonathan Wakely <[email protected]> wrote:
>
> On Mon, 20 Jul 2026 at 10:54 +0200, Tomasz Kamiński wrote:
> >From: Jonathan Wakely <[email protected]>
> >
> >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.
> >
> >For times after the hardcoded expiry, the inline __get_leap_second_info
> >function calls a new library function which compares the number of
> >hardcoded leap seconds in the header with the number of leap seconds
> >defined in the tzdata leapseconds file (usually provided by the OS).
> >
> >There are three leap second lists that are relevant here. The first is
> >the hardcoded list (and its expiry time) in the <chrono> header. That is
> >fixed when the user code is compiled, and might be out of date by the
> >time the application runs. The second list (and its expiry time) is
> >hardcoded in tzdb.cc in the libstdc++.so library. If the application
> >uses a newer libstdc++.so at runtime than the <chrono> header used at
> >compile time, we can still avoid going to the filesystem for dates
> >within the expiry time of the list in libstdc++.so. The third list is
> >the most up-to-date one which is read from the leapseconds file.
> >
> >The code added by this commit tries to avoid reading the file when
> >possible (because that's slower than using the in-memory lists), and if
> >it does have to go to the file system, it tries to avoid doing so again
> >next time the leap seconds are needed. The later is handled by storing
>
> "by storing the number"
>
> >of recently know leaps seconds in num_leap_seconds of struct
> >NumLeapSeconds, which decides whether to use std::atomic_ref<unsigned>
> >or perform all accesses while holding a lock on the pre-existing mutex
> >used for the tzdb_list singleton. To set_locked/set functions are used
>
> "Two"
>
> >to differentiate #if/#else branches of _Node::_S_replace_head that
> >already held corresponding mutext and use atomic respectivelly.
>
> "mutex"
> "atomics"
> "respectively"
>
> >
> >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::__recent_leap_second_info):
> > Declare new function and make it a friend of varous classes.
> > (leap_second): Make private constructor constexpr. Remove friend
> > declaration for get_leap_second_info.
> > (__detail::__get_leap_second_info): Use new function for times
> > past the hardcoded expiry.
> > * src/c++20/tzdb.cc (tzdb_list::_Node::fixed_leaps): Move array
> > of leap seconds here from _S_read_leap_seconds.
> > (_Node::NumLeapSeconds): New class.
> > (_Node::num_leap_seconds): New static variable.
> > (__detail::__recent_leap_second_info): Define new function.
> > (tzdb_list::_Node::_S_read_leap_seconds): Populate vector from
> > _Node::fixed_leaps. Rename bool variable to clarify meaning.
> > (tzdb_list::_Node::_S_replace_head): Update num_leap_seconds
> > when updating the tzdb_list.
> > * testsuite/util/testsuite_abi.cc: Update known_versions and
> > latestp.
> > * testsuite/std/time/clock/utc/leap_second_info-2.cc: New test.
> >
> >Co-authored-by: Tomasz Kamiński <[email protected]>
> >Signed-off-by: Tomasz Kamiński <[email protected]>
> >(cherry picked from commit e79f0f818c0e42d0d84c90a54684dfb269ac388c)
> >(cherry picked from commit b12fdd952511786bf8d285e7080e94deeb32aa1d)
> >(cherry picked from commit adb4f4a064c009cd616106fa4c648182c18f96d9)
> >(cherry picked from commit a7ad4bee6fe7cd77aceb6e86380dcaeb6da82791)
> >---
> >As suggested by cherry-pick lines this squashes the original and
> >tree follow-up patches. Please double check if I missed something.
>
> Thanks for squashing these, you got all the relevant commits.
>
> >
> >I am landing this first as this does introduce
> >6.36 version that my format patches uses.
> >
> >I have updated the commit description a bit, please check if you
> >have any suggestions.
> >
> >Testing on x86_64-linux.. OK for gcc-16 when all test passes?
> >
> > libstdc++-v3/acinclude.m4 | 2 +-
> > libstdc++-v3/config/abi/pre/gnu.ver | 7 +
> > libstdc++-v3/configure | 2 +-
> > libstdc++-v3/include/std/chrono | 104 +++----
> > libstdc++-v3/src/c++20/tzdb.cc | 294 +++++++++++++++---
> > .../std/time/clock/utc/leap_second_info-2.cc | 108 +++++++
> > libstdc++-v3/testsuite/util/testsuite_abi.cc | 3 +-
> > 7 files changed, 420 insertions(+), 100 deletions(-)
> > create mode 100644
> > libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc
> >
> >diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
> >index e6e4f7b394a..a615692e7be 100644
> >--- a/libstdc++-v3/acinclude.m4
> >+++ b/libstdc++-v3/acinclude.m4
> >@@ -4096,7 +4096,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 bd4da641829..06892cd2d18 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
>
> This needs to be 16.2.0 (I see you've fixed this locally now).
>
> And we need a follow-up patch on trunk to bump the version to 3.4.37
> and move the newer symbols (allocate_at_least etc.) to the new
> version. Maybe that should happen first, so that when the gcc-16
> branch is changed, it matches what's already on trunk.
I've just approved your patch to do exactly that on trunk, so this one
is OK for gcc-16 now (with the commit msg typos fixed), thanks.
>
>
> >+GLIBCXX_3.4.36 {
> >+
> >+
> >_ZNSt6chrono8__detail25__recent_leap_second_infoERNS_16leap_second_infoEj;
> >+
> >+} 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 359545c4bce..bc9dce5e288 100755
> >--- a/libstdc++-v3/configure
> >+++ b/libstdc++-v3/configure
> >@@ -51429,7 +51429,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 674f867dcdc..692fd6025e7 100644
> >--- a/libstdc++-v3/include/std/chrono
> >+++ b/libstdc++-v3/include/std/chrono
> >@@ -131,6 +131,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > leap_second_info
> > get_leap_second_info(const utc_time<_Duration>& __ut);
> >
> >+ /// @cond undocumented
> >+ namespace __detail
> >+ {
> >+ bool __recent_leap_second_info(leap_second_info&, unsigned);
> >+ }
> >+ /// @endcond
> >+
> > /** A clock that measures Universal Coordinated Time (UTC).
> > *
> > * The epoch is 1970-01-01 00:00:00.
> >@@ -2818,6 +2825,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > friend class leap_second;
> > friend struct time_zone::_Impl;
> > friend class time_zone_link;
> >+
> >+ friend bool
> >+ __detail::__recent_leap_second_info(leap_second_info&, unsigned);
> > };
> >
> > class time_zone_link
> >@@ -2943,16 +2953,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > { return __x.date() <=> __y; }
> >
> > private:
> >- explicit leap_second(seconds::rep __s) : _M_s(__s) { }
> >+ constexpr explicit leap_second(seconds::rep __s) : _M_s(__s) { }
> >
> > friend struct tzdb_list::_Node;
> >
> > friend const tzdb& reload_tzdb();
> >
> >- template<typename _Duration>
> >- friend leap_second_info
> >- get_leap_second_info(const utc_time<_Duration>&);
> >-
> > seconds _M_s; // == date().time_since_epoch() * value().count()
> > };
> >
> >@@ -3217,6 +3223,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > namespace __detail
> > {
> >+ // This function is inline to support fast conversions between utc_time
> >+ // and sys_time when possible, without requiring a chrono::tzdb object
> >+ // to be constructed.
> > inline leap_second_info
> > __get_leap_second_info(sys_seconds __ss, bool __is_utc)
> > {
> >@@ -3252,65 +3261,56 @@ namespace __detail
> > 1435708800, // 1 Jul 2015
> > 1483228800, // 1 Jan 2017
> > };
> >+
> >+ // The default result for times after the last leap year.
> >+ constexpr leap_second_info __after_last{
> >+ .is_leap_second = false,
> >+ .elapsed = seconds(std::size(__leaps))
> >+ };
> >+
> > // 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
> > UTC
> >
> >-#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> > if (__ss > __expires)
> > {
> >- // Use updated leap_seconds from tzdb.
> >- size_t __n = std::size(__leaps);
> >-
> >- auto __db = get_tzdb_list().begin();
> >- auto __first = __db->leap_seconds.begin() + __n;
> >- auto __last = __db->leap_seconds.end();
> >- auto __pos = std::upper_bound(__first, __last, __ss);
> >- seconds __elapsed(__n);
> >- for (auto __i = __first; __i != __pos; ++__i)
> >- __elapsed += __i->value();
> >-
> >- if (__is_utc)
> >- {
> >- // Convert utc_time to sys_time:
> >- __ss -= __elapsed;
> >- // See if that sys_time is before (or during) previous leap sec:
> >- if (__pos != __first && __ss < __pos[-1])
> >- {
> >- if ((__ss + 1s) >= __pos[-1])
> >- return {true, __elapsed};
> >- __elapsed -= __pos[-1].value();
> >- }
> >- }
> >- return {false, __elapsed};
> >- }
> >- else
> >+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI // use chrono::tzdb
> >+ // Call into the library because it might have knowledge of new
> >+ // leap seconds loaded at runtime from the tzdata files.
> >+
> >+ // We misuse leap_second_info here to pass {bool, seconds} inputs:
> >+ leap_second_info __info{ .is_leap_second = __is_utc,
> >+ .elapsed = __ss.time_since_epoch() };
> >+ // If this returns true, then __info holds the output result:
> >+ if (__detail::__recent_leap_second_info(__info, std::size(__leaps)))
> >+ return __info;
> > #endif
> >- {
> >- seconds::rep __s = __ss.time_since_epoch().count();
> >- const seconds::rep* __first = std::begin(__leaps);
> >- const seconds::rep* __last = std::end(__leaps);
> >+ return __after_last;
> >+ }
> >
> >- // Don't bother searching the list if we're after the last one.
> >- if (__s > (__last[-1] + (__last - __first) + 1))
> >- return { false, seconds(__last - __first) };
> >+ seconds::rep __s = __ss.time_since_epoch().count();
> >+ const seconds::rep* __first = std::begin(__leaps);
> >+ const seconds::rep* __last = std::end(__leaps);
> >
> >- auto __pos = std::upper_bound(__first, __last, __s);
> >- seconds __elapsed{__pos - __first};
> >- if (__is_utc)
> >+ // Don't bother searching the list if we're after the last one.
> >+ if (__s > (__last[-1] + (__last - __first) + 1))
> >+ return __after_last;
> >+
> >+ auto __pos = std::upper_bound(__first, __last, __s);
> >+ seconds __elapsed{__pos - __first};
> >+ if (__is_utc)
> >+ {
> >+ // Convert utc_time to sys_time:
> >+ __s -= __elapsed.count();
> >+ // See if that sys_time is before (or during) previous leap sec:
> >+ if (__pos != __first && __s < __pos[-1])
> > {
> >- // Convert utc_time to sys_time:
> >- __s -= __elapsed.count();
> >- // See if that sys_time is before (or during) previous leap sec:
> >- if (__pos != __first && __s < __pos[-1])
> >- {
> >- if ((__s + 1) >= __pos[-1])
> >- return {true, __elapsed};
> >- --__elapsed;
> >- }
> >+ if ((__s + 1) >= __pos[-1])
> >+ return {true, __elapsed};
> >+ --__elapsed;
> > }
> >- return {false, __elapsed};
> > }
> >+ return {false, __elapsed};
> > }
> > } // namespace __detail
> >
> >diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
> >index b0fbfc46a6d..0013ad1877c 100644
> >--- a/libstdc++-v3/src/c++20/tzdb.cc
> >+++ b/libstdc++-v3/src/c++20/tzdb.cc
> >@@ -36,6 +36,7 @@
> > #include <memory> // atomic<shared_ptr<T>>
> > #include <mutex> // mutex
> > #include <iomanip> // quoted
> >+#include <span>
> > #if defined __GTHREADS && ! defined _GLIBCXX_HAS_GTHREADS
> > # include <ext/concurrence.h> // __gnu_cxx::__mutex
> > #endif
> >@@ -66,6 +67,7 @@
> > #endif
> >
> > #if USE_ATOMIC_SHARED_PTR && ! USE_ATOMIC_LIST_HEAD
> >+// Cannot use atomic<shared_ptr<T>> without lock-free atomic<T*>.
> > # error Unsupported combination
> > #endif
> >
> >@@ -196,6 +198,13 @@ namespace std::chrono
> > static const tzdb& _S_replace_head(shared_ptr<_Node>,
> > shared_ptr<_Node>);
> >
> > static pair<vector<leap_second>, bool> _S_read_leap_seconds();
> >+
> >+ // This is here because _Node is a friend so can call private
> >constructor.
> >+ static const leap_second fixed_leaps[];
> >+
> >+ // This is a member so that it can access fixed_leaps.
> >+ struct NumLeapSeconds;
> >+ static NumLeapSeconds num_leap_seconds;
> > };
> >
> > // Implementation of the private constructor used for the singleton
> > object.
> >@@ -1251,56 +1260,240 @@ namespace std::chrono
> > }
> > #endif // TZDB_DISABLED
> >
> >+// These are the same values as the array in the <chrono> header, but might
> >+// contain additional leap seconds if the libstdc++.so used at runtime is
> >+// newer than the <chrono> header used to compile parts of the application.
> >+constexpr leap_second tzdb_list::_Node::fixed_leaps[] {
> >+#define LS leap_second
> >+ LS( 78796800), // 1 Jul 1972
> >+ LS( 94694400), // 1 Jan 1973
> >+ LS( 126230400), // 1 Jan 1974
> >+ LS( 157766400), // 1 Jan 1975
> >+ LS( 189302400), // 1 Jan 1976
> >+ LS( 220924800), // 1 Jan 1977
> >+ LS( 252460800), // 1 Jan 1978
> >+ LS( 283996800), // 1 Jan 1979
> >+ LS( 315532800), // 1 Jan 1980
> >+ LS( 362793600), // 1 Jul 1981
> >+ LS( 394329600), // 1 Jul 1982
> >+ LS( 425865600), // 1 Jul 1983
> >+ LS( 489024000), // 1 Jul 1985
> >+ LS( 567993600), // 1 Jan 1988
> >+ LS( 631152000), // 1 Jan 1990
> >+ LS( 662688000), // 1 Jan 1991
> >+ LS( 709948800), // 1 Jul 1992
> >+ LS( 741484800), // 1 Jul 1993
> >+ LS( 773020800), // 1 Jul 1994
> >+ LS( 820454400), // 1 Jan 1996
> >+ LS( 867715200), // 1 Jul 1997
> >+ LS( 915148800), // 1 Jan 1999
> >+ LS(1136073600), // 1 Jan 2006
> >+ LS(1230768000), // 1 Jan 2009
> >+ LS(1341100800), // 1 Jul 2012
> >+ LS(1435708800), // 1 Jul 2015
> >+ LS(1483228800), // 1 Jan 2017
> >+ // If new leap seconds get defined they should be added here.
> >+ // Negative leap seconds are represented as -1 * timestamp.
> >+#undef LS
> >+};
> >+
> >+namespace
> >+{
> >+ // The expiry date corresponding to the list above.
> >+ // tzdata 2026a leapseconds list expires at 2026-12-28 00:00:00 UTC
> >+ constexpr seconds fixed_expiry{1798416000u};
> >+}
> >+
> >+// This holds the most up-to-date number of leap seconds known at runtime.
> >+// Initially zero, updated when _S_read_leap_seconds() is called.
> >+struct tzdb_list::_Node::NumLeapSeconds
> >+{
> >+ // Called by __recent_leap_second_info to read num_leap_seconds.
> >+ unsigned
> >+ get()
> >+ {
> >+#if ATOMIC_INT_LOCK_FREE == 2
> >+ atomic_ref<unsigned> ref(count);
> >+ auto num = ref.load(memory_order::relaxed);
> >+
> >+ if (num == std::size(_Node::fixed_leaps))
> >+ // A leapseconds file has been read and has no new leap seconds.
> >+ return num;
> >+
> >+ if (num == 0)
> >+ // No leapseconds file has been read yet.
> >+ return 0;
> >+
> >+ // The tzdb_list has been initialized and contains a tzdb object with
> >+ // new leap seconds, which the caller is going to use.
> >+ // The relaxed load above does not synchronize with anything, so to
> >+ // ensure that the get_tzdb_list() in the caller will see a tzdb object
> >+ // set by _S_replace_head, we load num_leap_seconds again with acquire
> >+ // ordering:
> >+ return ref.load(memory_order::acquire);
> >+#else
> >+ lock_guard<mutex> l(list_mutex()); // This ensures acquire ordering.
> >+ return count;
> >+#endif
> >+ }
> >+
> >+ // Called by __recent_leap_second_info to set num_leap_seconds when
> >+ // we have determined there are no new leap seconds in a leapseconds file.
> >+ void
> >+ set_to_fixed_size()
> >+ {
> >+#if ATOMIC_INT_LOCK_FREE == 2
> >+ atomic_ref<unsigned> ref(count);
> >+ unsigned expected = 0;
> >+ ref.compare_exchange_strong(expected, std::size(_Node::fixed_leaps),
> >+ memory_order::relaxed);
> >+#else
> >+ lock_guard<mutex> l(list_mutex());
> >+ if (count == 0)
> >+ count = std::size(_Node::fixed_leaps);
> >+#endif
> >+ }
> >+
> >+ // Called by _Node::_S_replace_head
> >+ // The two versions are named differently so that caller has to be
> >explicit
> >+ // about which version it calls, based on whether the mutex is held.
> >+ void
> >+ set(unsigned val)
> >+ {
> >+#if ATOMIC_INT_LOCK_FREE == 2
> >+ atomic_ref<unsigned> ref(count);
> >+ // The release op here synchronizes with the acquire op in get().
> >+ ref.store(val, memory_order::release);
> >+#else
> >+ lock_guard<mutex> l(list_mutex());
> >+ count = val;
> >+#endif
> >+ }
> >+
> >+ void
> >+ set_locked(unsigned val, const lock_guard<mutex>&)
> >+ {
> >+#if ATOMIC_INT_LOCK_FREE == 2
> >+ // Even though the caller locked the mutex, we still need to use an
> >+ // atomic store in this case, because there could be concurrent loads.
> >+ set(val);
> >+#else
> >+ // The only caller of this function locks list_mutex() so we would
> >+ // deadlock if we locked it again here.
> >+ count = val;
> >+#endif
> >+ }
> >+
> >+private:
> >+ unsigned count = 0;
> >+};
> >+
> >+constinit tzdb_list::_Node::NumLeapSeconds
> >tzdb_list::_Node::num_leap_seconds;
> >+
> >+ namespace __detail
> >+ {
> >+ // Called by chrono::__detail::__get_leap_second_info in <chrono>
> >+ // to get leap_second_info for times after the expiry date in the
> >header.
> >+ // The caller provides the time being queried in `info.elapsed` and
> >+ // whether that is a UTC time in `info.is_leap_second`.
> >+ // If it returns true, this function did the lookup and updated `info`.
> >+ // If this returns false, it means the hardcoded list of leap seconds
> >+ // in the header should be used for the lookup.
> >+ bool
> >+ __recent_leap_second_info(leap_second_info& info,
> >+ unsigned num_positive_leaps)
> >+ {
> >+ // Extract the input args from info:
> >+ const auto [is_utc, secs] = info;
> >+ // And then reuse info for the output result:
> >+ info.is_leap_second = false;
> >+ info.elapsed = seconds(num_positive_leaps);
> >+
> >+ auto update_info = [&](span<const leap_second> leaps)
> >+ {
> >+ if (leaps.size() == num_positive_leaps)
> >+ return false; // No new leap seconds, use the array in the header.
> >+
> >+ // info.elapsed already contains the first N leap seconds,
> >+ // so we only search the end of the span.
> >+ auto first = leaps.begin() + num_positive_leaps;
> >+ auto pos = std::upper_bound(first, leaps.end(), sys_seconds(secs));
> >+ for (auto i = first; i != pos; ++i)
> >+ info.elapsed += i->value();
> >+
> >+ if (is_utc)
> >+ {
> >+ // This should never happen, but check it so that pos[-1] is
> >valid:
> >+ if (num_positive_leaps == 0) [[unlikely]]
> >+ return false;
> >+
> >+ // Convert utc_time to sys_time:
> >+ sys_seconds ss(secs - info.elapsed);
> >+ // See if that sys_time is before (or during) previous leap sec:
> >+ if (ss < pos[-1])
> >+ {
> >+ if ((ss + 1s) >= pos[-1])
> >+ info.is_leap_second = true;
> >+ else
> >+ info.elapsed -= pos[-1].value();
> >+ }
> >+ }
> >+ return true;
> >+ };
> >+
> >+ using _Node = tzdb_list::_Node;
> >+
> >+ // If the caller was compiled using an older GCC with an older expiry
> >+ // time in the header than the `fixed_expiry` defined above, we might
> >+ // be able to answer the query easily using the static `fixed_leaps`.
> >+ if (secs <= fixed_expiry)
> >+ return update_info(_Node::fixed_leaps);
> >+
> >+ constexpr auto num_fixed_leaps = std::size(_Node::fixed_leaps);
> >+
> >+ auto num_leaps = _Node::num_leap_seconds.get();
> >+ if (num_leaps == num_fixed_leaps)
> >+ // A leapseconds file has been read and has no new leap seconds:
> >+ return update_info(_Node::fixed_leaps);
> >+ else if (num_leaps == 0)
> >+ {
> >+ // The tzdb_list has not been initialized yet, so we don't know
> >+ // the correct number of leap seconds.
> >+ // We use _S_read_leap_seconds() to read the leapseconds file.
> >+ // If that tells us there are no new leapseconds, we don't need
> >+ // to parse all of tzdata.zi and initialize a whole tzdb object.
> >+ if (_Node::_S_read_leap_seconds().first.size() == num_fixed_leaps)
> >+ {
> >+ // There are no new leap seconds. Remember that so that the next
> >+ // call to this function can just use fixed_leaps.
> >+ _Node::num_leap_seconds.set_to_fixed_size();
> >+ return update_info(_Node::fixed_leaps);
> >+ }
> >+ // else there are new leap seconds. We init tzdb_list so that the
> >+ // new leap seconds are persisted in a tzdb object.
> >+ }
> >+
> >+ // Use updated leap_seconds from tzdb.
> >+ return update_info(get_tzdb_list().begin()->leap_seconds);
> >+ }
> >+ }
> >+
> > // Return leap_second values, and a bool indicating whether the values are
> > // current (true), or potentially out of date (false).
> > pair<vector<leap_second>, bool>
> > tzdb_list::_Node::_S_read_leap_seconds()
> > {
> >- // This list is valid until at least 2026-12-28 00:00:00 UTC.
> >- constexpr auto expires = sys_days{2026y/12/28};
> >- vector<leap_second> leaps
> >- {
> >- (leap_second) 78796800, // 1 Jul 1972
> >- (leap_second) 94694400, // 1 Jan 1973
> >- (leap_second) 126230400, // 1 Jan 1974
> >- (leap_second) 157766400, // 1 Jan 1975
> >- (leap_second) 189302400, // 1 Jan 1976
> >- (leap_second) 220924800, // 1 Jan 1977
> >- (leap_second) 252460800, // 1 Jan 1978
> >- (leap_second) 283996800, // 1 Jan 1979
> >- (leap_second) 315532800, // 1 Jan 1980
> >- (leap_second) 362793600, // 1 Jul 1981
> >- (leap_second) 394329600, // 1 Jul 1982
> >- (leap_second) 425865600, // 1 Jul 1983
> >- (leap_second) 489024000, // 1 Jul 1985
> >- (leap_second) 567993600, // 1 Jan 1988
> >- (leap_second) 631152000, // 1 Jan 1990
> >- (leap_second) 662688000, // 1 Jan 1991
> >- (leap_second) 709948800, // 1 Jul 1992
> >- (leap_second) 741484800, // 1 Jul 1993
> >- (leap_second) 773020800, // 1 Jul 1994
> >- (leap_second) 820454400, // 1 Jan 1996
> >- (leap_second) 867715200, // 1 Jul 1997
> >- (leap_second) 915148800, // 1 Jan 1999
> >- (leap_second)1136073600, // 1 Jan 2006
> >- (leap_second)1230768000, // 1 Jan 2009
> >- (leap_second)1341100800, // 1 Jul 2012
> >- (leap_second)1435708800, // 1 Jul 2015
> >- (leap_second)1483228800, // 1 Jan 2017
> >- };
> >+ // Populate the vector with the leap seconds we already know about:
> >+ vector<leap_second> leaps(fixed_leaps, std::end(fixed_leaps));
> >
> >-#if 0
> >- // This optimization isn't valid if the file has additional leap seconds
> >- // defined since the library was compiled, but the system clock has been
> >- // set to a time before the hardcoded expiration date.
> >- if (system_clock::now() < expires)
> >- return {std::move(leaps), true};
> >-#endif
> >+ bool read_leaps_file = false;
> >
> > #ifndef TZDB_DISABLED
> > if (ifstream ls{zoneinfo_file(leaps_file)})
> > {
> >- auto exp_year = year_month_day(expires).year();
> >+ constexpr year exp_year
> >+ =
> >year_month_day(sys_days(duration_cast<days>(fixed_expiry))).year();
> >+
> > std::string s, w;
> > s.reserve(80); // Avoid later reallocations.
> > while (std::getline(ls, s))
> >@@ -1309,6 +1502,7 @@ namespace std::chrono
> >
> > if (!s.starts_with("Leap"))
> > continue;
> >+
> > istringstream li(std::move(s));
> > li.exceptions(ios::failbit);
> > li.ignore(4);
> >@@ -1339,12 +1533,14 @@ namespace std::chrono
> > leaps.push_back(ls);
> > }
> > }
> >- s = std::move(li).str(); // return storage to s
> >+ s = std::move(li).str(); // give allocated storage back to s
> > }
> >- return {std::move(leaps), true};
> >+
> >+ read_leaps_file = true;
> > }
> > #endif
> >- return {std::move(leaps), false};
> >+
> >+ return {std::move(leaps), read_leaps_file};
> > }
> >
> > #ifndef TZDB_DISABLED
> >@@ -1402,8 +1598,13 @@ namespace std::chrono
> > new_head_ptr->next = curr;
> > }
> > // XXX small window here where _S_head_cache still points to previous
> > tzdb.
> >+ _S_cache_list_head(new_head_ptr);
> >+
> >+ // This allows __recent_leap_second_info() to know that it can use
> >+ // get_tzdb_list()->begin()->leap_seconds to get new leap seconds.
> >+ num_leap_seconds.set(new_head_ptr->db.leap_seconds.size());
> > #else
> >- lock_guard<mutex> l(list_mutex());
> >+ lock_guard<mutex> lock(list_mutex());
> > if (const _Node* h = _S_head_owner.get())
> > {
> > if (h->db.version == new_head_ptr->db.version)
> >@@ -1411,8 +1612,11 @@ namespace std::chrono
> > new_head_ptr->next = _S_head_owner;
> > }
> > _S_head_owner = std::move(new_head);
> >-#endif
> > _S_cache_list_head(new_head_ptr);
> >+
> >+ num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), lock);
> >+#endif
> >+
> > return new_head_ptr->db;
> > }
> >
> >diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc
> >b/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc
> >new file mode 100644
> >index 00000000000..090a28291c8
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc
> >@@ -0,0 +1,108 @@
> >+// { dg-do run { target c++20 } }
> >+// { dg-require-effective-target tzdb }
> >+// { dg-require-effective-target cxx11_abi }
> >+// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } }
> >+
> >+#include <chrono>
> >+#include <fstream>
> >+#include <testsuite_hooks.h>
> >+
> >+using namespace std::chrono_literals;
> >+
> >+static bool override_used = false;
> >+
> >+namespace __gnu_cxx
> >+{
> >+ const char* zoneinfo_dir_override() {
> >+ override_used = true;
> >+ return "./";
> >+ }
> >+}
> >+
> >+void
> >+test_known_leaps()
> >+{
> >+ // Test some values within the list of known leap seconds.
> >+ auto s = std::chrono::utc_seconds(-1s);
> >+ auto lsi = get_leap_second_info(s);
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == 0s );
> >+
> >+ s = std::chrono::utc_seconds(126230402s); // 1 Jan 1974
> >+ lsi = get_leap_second_info(s);
> >+ VERIFY( lsi.is_leap_second == true );
> >+ VERIFY( lsi.elapsed == 3s );
> >+
> >+ s = std::chrono::utc_seconds(1483228826s); // 1 Jan 2017
> >+ lsi = get_leap_second_info(s);
> >+ VERIFY( lsi.is_leap_second == true );
> >+ VERIFY( lsi.elapsed == 27s );
> >+
> >+ // We should not have checked the filesystem for the times above.
> >+ VERIFY( ! override_used );
> >+}
> >+
> >+void
> >+test_future_leaps()
> >+{
> >+ // XXX adjust this if real leap seconds are added to the hardcoded lists:
> >+ const auto hardcoded_count = 27s;
> >+
> >+ // Make sure there's no old file from a previous test run.
> >+ std::ofstream("leapseconds") << "";
> >+
> >+ using std::chrono::years;
> >+ auto s = std::chrono::utc_seconds(1483228826s + years(100)); // 1 Jan 2117
> >+ auto lsi = get_leap_second_info(s);
> >+ VERIFY( override_used ); // If this fails then XFAIL for the target.
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count );
> >+ lsi = get_leap_second_info(s + years(10));
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count );
> >+
> >+ std::ofstream("leapseconds") << R"(
> >+# No need to repeat the real leap seconds here, they're hardcoded in the
> >lib.
> >+# These are fake new leap seconds for testing purposes:
> >+Leap 2099 Dec 31 23:59:60 + S
> >+Leap 2120 Jun 30 23:59:59 - S
> >+Leap 2120 Dec 30 23:59:59 - S
> >+)";
> >+
> >+ override_used = false;
> >+ std::chrono::reload_tzdb();
> >+ VERIFY( override_used );
> >+
> >+ // Check the same dates again using the custom leapseconds file:
> >+ lsi = get_leap_second_info(s);
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count + 1s ); // One positive leap
> >second.
> >+ lsi = get_leap_second_info(s + years(10));
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count - 1s ); // Two negative leap
> >seconds.
> >+
> >+ // Overwrite the custom file again:
> >+ override_used = false;
> >+ std::ofstream("leapseconds") << "";
> >+
> >+ // This should not re-read the custom file, the head of the tzdb_list
> >+ // should already have been populated by calling get_leap_second_info(s):
> >+ auto& tzdb = std::chrono::get_tzdb();
> >+ // The file was not read again:
> >+ VERIFY( ! override_used );
> >+ // The list in the tzdb contains the three fake leap seconds:
> >+ VERIFY( tzdb.leap_seconds.size() == hardcoded_count.count() + 3 );
> >+ // And repeating the queries above gives the same results:
> >+ lsi = get_leap_second_info(s);
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count + 1s ); // One positive leap
> >second.
> >+ lsi = get_leap_second_info(s + years(10));
> >+ VERIFY( lsi.is_leap_second == false );
> >+ VERIFY( lsi.elapsed == hardcoded_count - 1s ); // Two negative leap
> >seconds.
> >+}
> >+
> >+int main()
> >+{
> >+ test_known_leaps();
> >+ test_future_leaps();
> >+}
> >diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc
> >b/libstdc++-v3/testsuite/util/testsuite_abi.cc
> >index 8fb38355cad..4e80c5f184a 100644
> >--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
> >+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
> >@@ -217,6 +217,7 @@ check_version(symbol& test, bool added)
> > known_versions.push_back("GLIBCXX_3.4.33");
> > known_versions.push_back("GLIBCXX_3.4.34");
> > known_versions.push_back("GLIBCXX_3.4.35");
> >+ known_versions.push_back("GLIBCXX_3.4.36");
> > known_versions.push_back("GLIBCXX_LDBL_3.4.31");
> > known_versions.push_back("GLIBCXX_IEEE128_3.4.29");
> > known_versions.push_back("GLIBCXX_IEEE128_3.4.30");
> >@@ -260,7 +261,7 @@ check_version(symbol& test, bool added)
> > test.version_status = symbol::incompatible;
> >
> > // Check that added symbols are added in the latest pre-release
> > version.
> >- bool latestp = (test.version_name == "GLIBCXX_3.4.35"
> >+ bool latestp = (test.version_name == "GLIBCXX_3.4.36"
> > || test.version_name == "CXXABI_1.3.17"
> > || test.version_name == "CXXABI_FLOAT128"
> > || test.version_name == "CXXABI_TM_1");
> >--
> >2.55.0
> >
> >
>