[PATCH v2] libstdc++: Improve handling of leap second expiry time [PR123165]
Tomasz Kaminski
tkaminsk@redhat.com
Tue May 12 11:17:38 GMT 2026
On Tue, May 12, 2026 at 1:09 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.
>
> 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.
>
> 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.
> (fixed_expiry, num_leap_seconds): New globals.
> (__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 <tkaminsk@redhat.com>
> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> ---
>
> v2: Expanded the new test as requested by Tomasz, adding a check for
> !override_used and testing the same times again. I also added a third
> fake leap second, with a negative value, and checked that the 2127 date
> has a total of 27s + 1s + -1s + -1s == 26s elapsed. That way it's
> checking that the final total is 26s which is different from the default
> of 27s, so we know that we didn't just do 27s + 0s by mistake.
>
OK, thanks.
>
> libstdc++-v3/acinclude.m4 | 2 +-
> libstdc++-v3/config/abi/pre/gnu.ver | 7 +
> libstdc++-v3/configure | 2 +-
> libstdc++-v3/include/std/chrono | 110 ++++-----
> libstdc++-v3/src/c++20/tzdb.cc | 212 ++++++++++++++----
> .../std/time/clock/utc/leap_second_info-2.cc | 108 +++++++++
> libstdc++-v3/testsuite/util/testsuite_abi.cc | 3 +-
> 7 files changed, 343 insertions(+), 101 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 e7bc4fbf90c7..27a434d8a7a5 100644
> --- a/libstdc++-v3/acinclude.m4
> +++ b/libstdc++-v3/acinclude.m4
> @@ -4087,7 +4087,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..06892cd2d18b 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__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 3785ad94f330..6e5e424b9dbb 100755
> --- a/libstdc++-v3/configure
> +++ b/libstdc++-v3/configure
> @@ -51420,7 +51420,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..692fd6025e7d 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);
> +#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.
>
> - 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
> + // 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);
> -
> - // 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) };
> -
> - 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])
> - {
> - if ((__s + 1) >= __pos[-1])
> - return {true, __elapsed};
> - --__elapsed;
> - }
> - }
> - return {false, __elapsed};
> + return __after_last;
> }
> +
> + seconds::rep __s = __ss.time_since_epoch().count();
> + const seconds::rep* __first = std::begin(__leaps);
> + const seconds::rep* __last = std::end(__leaps);
> +
> + // 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])
> + {
> + if ((__s + 1) >= __pos[-1])
> + return {true, __elapsed};
> + --__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 b0fbfc46a6d3..587e88bc0dd5 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
> @@ -196,6 +197,9 @@ 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[];
> };
>
> // Implementation of the private constructor used for the singleton
> object.
> @@ -1251,56 +1255,168 @@ 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.
> + constinit atomic<unsigned> num_leap_seconds{0};
> +}
> +
> + 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 = num_leap_seconds.load(memory_order::relaxed);
> + 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 been initialized and contains a tzdb object
> + // with new leap seconds, which we want to use here.
> + // The relaxed load above does not synchronize with anything, so to
> + // ensure that the get_tzdb_list() below will see a tzdb object set
> + // by _S_replace_head, we load num_leap_seconds again with acquire
> + // ordering:
> + (void) num_leap_seconds.load(memory_order::acquire);
> + else
> + {
> + // 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.
> + num_leap_seconds.compare_exchange_strong(num_leaps,
> + num_fixed_leaps,
> +
> memory_order::relaxed);
> + 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 +1425,7 @@ namespace std::chrono
>
> if (!s.starts_with("Leap"))
> continue;
> +
> istringstream li(std::move(s));
> li.exceptions(ios::failbit);
> li.ignore(4);
> @@ -1339,12 +1456,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
> @@ -1413,6 +1532,13 @@ namespace std::chrono
> _S_head_owner = std::move(new_head);
> #endif
> _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.
> + // The release op here synchronizes with the acquire op there.
> + num_leap_seconds.store(new_head_ptr->db.leap_seconds.size(),
> + memory_order::release);
> +
> 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 000000000000..a888ae036489
> --- /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 8fb38355cadd..4e80c5f184a6 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.54.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260512/e73b7ae5/attachment-0001.htm>
More information about the Libstdc++
mailing list