From patchwork Sun Nov 13 01:16:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 60505 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4D9863888824 for ; Sun, 13 Nov 2022 01:18:58 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4D9863888824 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1668302338; bh=dems9aSk2o/kzWeAnTu0XEXztqI9dhZ0URdlkZZ1xtI=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=naD0nZXmVYORp8fA8aKkudaDnA0AF0krUzFEwFPXUJIk4W9DFVhiy3M/h9CitsA6x 5YQMZVzHfnqTfINsRkXsCPcOH3I99SbBg0PDY+YeSrcRt4S8BgEIVoBdT9/y0xrl+C AC2RuzFZuYRPDZFslAyLOd6rzpgI+THUxNbMXXiM= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id C25A93888824 for ; Sun, 13 Nov 2022 01:16:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C25A93888824 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-612-3a_4h2AWP8SRPSVLxa9qXw-1; Sat, 12 Nov 2022 20:16:43 -0500 X-MC-Unique: 3a_4h2AWP8SRPSVLxa9qXw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 25DCC811E75; Sun, 13 Nov 2022 01:16:43 +0000 (UTC) Received: from localhost (unknown [10.33.36.199]) by smtp.corp.redhat.com (Postfix) with ESMTP id AA37E42222; Sun, 13 Nov 2022 01:16:42 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Add C++20 clocks Date: Sun, 13 Nov 2022 01:16:40 +0000 Message-Id: <20221113011640.920781-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Tested x86_64-linux and powerpc64le-linux. Pushed to trunk. -- >8 -- Also add the basic types for timezones, without the non-inline definitions needed to actually use them. The get_leap_second_info function currently uses a hardcoded list of leap seconds, correct as of the end of 2022. That needs to be replaced with a dynamically generated list read from the system tzdata. That will be done in a later patch. libstdc++-v3/ChangeLog: * include/std/chrono (utc_clock, tai_clock, gps_clock): Define. (clock_time_conversion, clock_cast): Define. (sys_info, local_info): Define structs for timezone information. (nonexistent_local_time, ambiguous_local_time): Define exceptions for invalid times. (time_zone, time_zone_link, leap_second, zoned_traits, tzdb) (tzdb_list): Define classes representing time zones. (get_leap_second_info): Define new function returning leap second offset for a given time point. * testsuite/std/time/clock/gps/1.cc: New test. * testsuite/std/time/clock/tai/1.cc: New test. * testsuite/std/time/clock/utc/1.cc: New test. --- libstdc++-v3/include/std/chrono | 744 +++++++++++++++++- .../testsuite/std/time/clock/gps/1.cc | 38 + .../testsuite/std/time/clock/tai/1.cc | 41 + .../testsuite/std/time/clock/utc/1.cc | 24 + 4 files changed, 844 insertions(+), 3 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/time/clock/gps/1.cc create mode 100644 libstdc++-v3/testsuite/std/time/clock/tai/1.cc create mode 100644 libstdc++-v3/testsuite/std/time/clock/utc/1.cc diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index c0c3a679609..90b73f8198e 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -39,9 +39,15 @@ #else #include -#if __cplusplus > 201703L -# include // ostringstream -# include + +#if __cplusplus >= 202002L +# include +# include +# include +# include // __to_chars_len, __to_chars_10_impl +# include // upper_bound TODO: move leap_second_info to .so +# include +# include #endif namespace std _GLIBCXX_VISIBILITY(default) @@ -102,6 +108,357 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION seconds elapsed; }; + template + leap_second_info + get_leap_second_info(const utc_time<_Duration>& __ut); + + /** A clock that measures Universal Coordinated Time (UTC). + * + * The epoch is 1970-01-01 00:00:00. + * + * @since C++20 + */ + class utc_clock + { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point; + static constexpr bool is_steady = false; + + static time_point + now() + { return from_sys(system_clock::now()); } + + template + static sys_time> + to_sys(const utc_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + const auto __li = chrono::get_leap_second_info(__t); + sys_time<_CDur> __s{__t.time_since_epoch() - seconds{__li.elapsed}}; + if (__li.is_leap_second) + __s = chrono::floor(__s) + seconds{1} - _CDur{1}; + return __s; + } + + template + static utc_time> + from_sys(const sys_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + utc_time<_Duration> __u(__t.time_since_epoch()); + const auto __li = chrono::get_leap_second_info(__u); + return utc_time<_CDur>{__u} + seconds{__li.elapsed}; + } + }; + + /** A clock that measures International Atomic Time. + * + * The epoch is 1958-01-01 00:00:00. + * + * @since C++20 + */ + class tai_clock + { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point; + static constexpr bool is_steady = false; // XXX true for CLOCK_TAI? + + // TODO move into lib, use CLOCK_TAI on linux, add extension point. + static time_point + now() + { return from_utc(utc_clock::now()); } + + template + static utc_time> + to_utc(const tai_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + return utc_time<_CDur>{__t.time_since_epoch()} - 378691210s; + } + + template + static tai_time> + from_utc(const utc_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + return tai_time<_CDur>{__t.time_since_epoch()} + 378691210s; + } + }; + + /** A clock that measures GPS time. + * + * The epoch is 1980-01-06 00:00:00. + * + * @since C++20 + */ + class gps_clock + { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point; + static constexpr bool is_steady = false; // XXX + + // TODO move into lib, add extension point. + static time_point + now() + { return from_utc(utc_clock::now()); } + + template + static utc_time> + to_utc(const gps_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + return utc_time<_CDur>{__t.time_since_epoch()} + 315964809s; + } + + template + static gps_time> + from_utc(const utc_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + return gps_time<_CDur>{__t.time_since_epoch()} - 315964809s; + } + }; + + + template + struct clock_time_conversion + { }; + + // Identity conversions + + template + struct clock_time_conversion<_Clock, _Clock> + { + template + time_point<_Clock, _Duration> + operator()(const time_point<_Clock, _Duration>& __t) const + { return __t; } + }; + + template<> + struct clock_time_conversion + { + template + sys_time<_Duration> + operator()(const sys_time<_Duration>& __t) const + { return __t; } + }; + + template<> + struct clock_time_conversion + { + template + utc_time<_Duration> + operator()(const utc_time<_Duration>& __t) const + { return __t; } + }; + + // Conversions between system_clock and utc_clock + + template<> + struct clock_time_conversion + { + template + utc_time> + operator()(const sys_time<_Duration>& __t) const + { return utc_clock::from_sys(__t); } + }; + + template<> + struct clock_time_conversion + { + template + sys_time> + operator()(const utc_time<_Duration>& __t) const + { return utc_clock::to_sys(__t); } + }; + + template + inline constexpr bool __is_time_point_for_v = false; + + template + inline constexpr bool + __is_time_point_for_v, _Clock> = true; + + // Conversions between system_clock and other clocks + + template + struct clock_time_conversion + { + template + auto + operator()(const time_point<_SourceClock, _Duration>& __t) const + -> decltype(_Src::to_sys(__t)) + { + using _Ret = decltype(_SourceClock::to_sys(__t)); + static_assert(__is_time_point_for_v<_Ret, system_clock>); + return _SourceClock::to_sys(__t); + } + }; + + template + struct clock_time_conversion<_DestClock, system_clock> + { + template + auto + operator()(const sys_time<_Duration>& __t) const + -> decltype(_Dest::from_sys(__t)) + { + using _Ret = decltype(_DestClock::from_sys(__t)); + static_assert(__is_time_point_for_v<_Ret, _DestClock>); + return _DestClock::from_sys(__t); + } + }; + + // Conversions between utc_clock and other clocks + + template + struct clock_time_conversion + { + template + auto + operator()(const time_point<_SourceClock, _Duration>& __t) const + -> decltype(_Src::to_utc(__t)) + { + using _Ret = decltype(_SourceClock::to_utc(__t)); + static_assert(__is_time_point_for_v<_Ret, utc_clock>); + return _SourceClock::to_utc(__t); + } + }; + + template + struct clock_time_conversion<_DestClock, utc_clock> + { + template + auto + operator()(const utc_time<_Duration>& __t) const + -> decltype(_Dest::from_utc(__t)) + { + using _Ret = decltype(_DestClock::from_utc(__t)); + static_assert(__is_time_point_for_v<_Ret, _DestClock>); + return _DestClock::from_utc(__t); + } + }; + + /// @cond undocumented + namespace __detail + { + template + concept __clock_convs + = requires (const time_point<_SourceClock, _Duration>& __t) { + clock_time_conversion<_DestClock, _SourceClock>{}(__t); + }; + + template + concept __clock_convs_sys + = requires (const time_point<_SourceClock, _Duration>& __t) { + clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}(__t)); + }; + + template + concept __clock_convs_utc + = requires (const time_point<_SourceClock, _Duration>& __t) { + clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}(__t)); + }; + + template + concept __clock_convs_sys_utc + = requires (const time_point<_SourceClock, _Duration>& __t) { + clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(__t))); + }; + + template + concept __clock_convs_utc_sys + = requires (const time_point<_SourceClock, _Duration>& __t) { + clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(__t))); + }; + + } // namespace __detail + /// @endcond + + /// Convert a time point to a different clock. + template + inline auto + clock_cast(const time_point<_SourceClock, _Duration>& __t) + requires __detail::__clock_convs<_DestClock, _SourceClock, _Duration> + || __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration> + || __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration> + || __detail::__clock_convs_sys_utc<_DestClock, _SourceClock, _Duration> + || __detail::__clock_convs_utc_sys<_DestClock, _SourceClock, _Duration> + { + constexpr bool __direct + = __detail::__clock_convs<_DestClock, _SourceClock, _Duration>; + if constexpr (__direct) + { + return clock_time_conversion<_DestClock, _SourceClock>{}(__t); + } + else + { + constexpr bool __convert_via_sys_clock + = __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration>; + constexpr bool __convert_via_utc_clock + = __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration>; + if constexpr (__convert_via_sys_clock) + { + static_assert(!__convert_via_utc_clock, + "clock_cast requires a unique best conversion, but " + "conversion is possible via system_clock and also via" + "utc_clock"); + return clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}(__t)); + } + else if constexpr (__convert_via_utc_clock) + { + return clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}(__t)); + } + else + { + constexpr bool __convert_via_sys_and_utc_clocks + = __detail::__clock_convs_sys_utc<_DestClock, + _SourceClock, + _Duration>; + + if constexpr (__convert_via_sys_and_utc_clocks) + { + constexpr bool __convert_via_utc_and_sys_clocks + = __detail::__clock_convs_utc_sys<_DestClock, + _SourceClock, + _Duration>; + static_assert(!__convert_via_utc_and_sys_clocks, + "clock_cast requires a unique best conversion, but " + "conversion is possible via system_clock followed by " + "utc_clock, and also via utc_clock followed by " + "system_clock"); + return clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(__t))); + } + else + { + return clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(__t))); + } + } + } + } + // CALENDRICAL TYPES // CLASS DECLARATIONS @@ -2055,6 +2412,387 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __h + 12h; } } + + // C++20 [time.zones] Time zones + + struct sys_info + { + sys_seconds begin; + sys_seconds end; + seconds offset; + minutes save; + string abbrev; + }; + + struct local_info + { + static constexpr int unique = 0; + static constexpr int nonexistent = 1; + static constexpr int ambiguous = 2; + + int result; + sys_info first; + sys_info second; + }; + + class nonexistent_local_time : public runtime_error + { + public: + template + nonexistent_local_time(const local_time<_Duration>& __tp, + const local_info& __i) + : runtime_error(_S_make_what_str(__tp, __i)) + { __glibcxx_assert(__i.result == local_info::nonexistent); } + + private: + template // TODO + static string + _S_make_what_str(const local_time<_Duration>&, const local_info&); + }; + + class ambiguous_local_time : public runtime_error + { + public: + template + ambiguous_local_time(const local_time<_Duration>& __tp, + const local_info& __i) + : runtime_error(_S_make_what_str(__tp, __i)) + { __glibcxx_assert(__i.result == local_info::nonexistent); } + + private: + template // TODO + static string + _S_make_what_str(const local_time<_Duration>&, const local_info&); + }; + + enum class choose { earliest, latest }; + + class time_zone + { + public: + time_zone(time_zone&&) = default; + time_zone& operator=(time_zone&&) = default; + + string_view name() const noexcept { return _M_name; } + + template + sys_info + get_info(const sys_time<_Duration>& __st) const; + + template + local_info + get_info(const local_time<_Duration>& __tp) const; + + template + sys_time> + to_sys(const local_time<_Duration>& __tp) const; + + template + sys_time> + to_sys(const local_time<_Duration>& __tp, choose __z) const; + + template + local_time> + to_local(const sys_time<_Duration>& __tp) const; + + friend bool + operator==(const time_zone& __x, const time_zone& __y) noexcept + { return __x.name() == __y.name(); } + + friend strong_ordering + operator<=>(const time_zone& __x, const time_zone& __y) noexcept + { return __x.name() <=> __y.name(); } + + private: + string _M_name; + struct _Impl; + unique_ptr<_Impl> _M_impl; + }; + + struct tzdb; + const time_zone* locate_zone(string_view __tz_name); + const time_zone* current_zone(); + + class time_zone_link + { + public: + time_zone_link(time_zone_link&&) = default; + time_zone_link& operator=(time_zone_link&&) = default; + + string_view name() const noexcept { return _M_name; } + string_view target() const noexcept { return _M_target; } + + friend bool + operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept + { return __x.name() == __y.name(); } + + friend strong_ordering + operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept + { return __x.name() <=> __y.name(); } + + private: + friend const tzdb& reload_tzdb(); + // TODO unspecified additional constructors + string _M_name; + string _M_target; + }; + + class leap_second + { + public: + leap_second(const leap_second&) = default; + leap_second& operator=(const leap_second&) = default; + + constexpr sys_seconds + date() const noexcept + { + if (_M_s >= _M_s.zero()) [[likely]] + return sys_seconds(_M_s); + return sys_seconds(-_M_s); + } + + constexpr seconds + value() const noexcept + { + if (_M_s >= _M_s.zero()) [[likely]] + return seconds(1); + return seconds(-1); + } + + // This can be defaulted because the database will never contain two + // leap_second objects with the same date but different signs. + friend constexpr bool + operator==(const leap_second&, const leap_second&) noexcept = default; + + friend constexpr strong_ordering + operator<=>(const leap_second& __x, const leap_second& __y) noexcept + { return __x.date() <=> __y.date(); } + + template + friend constexpr bool + operator==(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return __x.date() == __y; } + + template + friend constexpr bool + operator<(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return __x.date() < __y; } + + template + friend constexpr bool + operator<(const sys_time<_Duration>& __x, + const leap_second& __y) noexcept + { return __x < __y.date(); } + + template + friend constexpr bool + operator>(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return __y < __x.date(); } + + template + friend constexpr bool + operator>(const sys_time<_Duration>& __x, + const leap_second& __y) noexcept + { return __y.date() < __x; } + + template + friend constexpr bool + operator<=(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return !(__y < __x.date()); } + + template + friend constexpr bool + operator<=(const sys_time<_Duration>& __x, + const leap_second& __y) noexcept + { return !(__y.date() < __x); } + + template + friend constexpr bool + operator>=(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return !(__x.date() < __y); } + + template + friend constexpr bool + operator>=(const sys_time<_Duration>& __x, + const leap_second& __y) noexcept + { return !(__x < __y.date()); } + + template _Duration> + friend constexpr auto + operator<=>(const leap_second& __x, + const sys_time<_Duration>& __y) noexcept + { return __x.date() <=> __y; } + + private: + explicit leap_second(seconds::rep __s) : _M_s(__s) { } + + friend const tzdb& reload_tzdb(); + template + friend leap_second_info + get_leap_second_info(const utc_time<_Dur>&); + + seconds _M_s; // == date().time_since_epoch() * value().count() + }; + + template struct zoned_traits { }; + + template<> + struct zoned_traits + { + static const time_zone* + default_zone() + { return std::chrono::locate_zone("UTC"); } + + static const time_zone* + locate_zone(string_view __name) + { return std::chrono::locate_zone(__name); } + }; + + struct tzdb + { + string version; + vector zones; + vector links; + vector leap_seconds; + + const time_zone* + locate_zone(string_view __tz_name) const; + + const time_zone* + current_zone() const; + + private: + friend const tzdb& reload_tzdb(); + + struct _Rule; + vector<_Rule> _M_rules; + }; + + class tzdb_list + { + struct _Node; + public: + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + class const_iterator + { + public: + using value_type = tzdb; + using reference = const tzdb&; + using pointer = const tzdb*; + using difference_type = ptrdiff_t; + using iterator_category = forward_iterator_tag; + + constexpr const_iterator() = default; + const_iterator(const const_iterator&) = default; + const_iterator(const_iterator&&) = default; + const_iterator& operator=(const const_iterator&) = default; + const_iterator& operator=(const_iterator&&) = default; + + reference operator*() const noexcept; + pointer operator->() const noexcept { return &**this; } + const_iterator& operator++(); + const_iterator operator++(int); + + bool operator==(const const_iterator&) const noexcept = default; + + private: + explicit const_iterator(const shared_ptr<_Node>&) noexcept; + + shared_ptr<_Node> _M_node; + void* _M_reserved = nullptr; + }; + + // TODO const tzdb& front() const noexcept; + + const_iterator erase_after(const_iterator); + + const_iterator begin() const noexcept; + const_iterator end() const noexcept { return {}; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + private: + constexpr explicit tzdb_list(nullptr_t); + + friend const tzdb_list& get_tzdb_list(); + friend const tzdb& get_tzdb(); + friend const tzdb& reload_tzdb(); + + static _Node* _S_head; + static shared_ptr<_Node> _S_head_owner; + }; + + // TODO + // const tzdb_list& get_tzdb_list(); + // const tzdb& get_tzdb(); + + // const tzdb& reload_tzdb(); + // string remove_version(); + + template + class zoned_time; // TODO + + using zoned_seconds = zoned_time; + + template + leap_second_info + get_leap_second_info(const utc_time<_Duration>& __ut) + { + if constexpr (is_same_v<_Duration, seconds>) + { + // TODO move this function into the library and get leaps from tzdb. + vector __leaps + { + 78796800, // 1 Jul 1972 + 94694400, // 1 Jan 1973 + 126230400, // 1 Jan 1974 + 157766400, // 1 Jan 1975 + 189302400, // 1 Jan 1976 + 220924800, // 1 Jan 1977 + 252460800, // 1 Jan 1978 + 283996800, // 1 Jan 1979 + 315532800, // 1 Jan 1980 + 362793600, // 1 Jul 1981 + 394329600, // 1 Jul 1982 + 425865600, // 1 Jul 1983 + 489024000, // 1 Jul 1985 + 567993600, // 1 Jan 1988 + 631152000, // 1 Jan 1990 + 662688000, // 1 Jan 1991 + 709948800, // 1 Jul 1992 + 741484800, // 1 Jul 1993 + 773020800, // 1 Jul 1994 + 820454400, // 1 Jan 1996 + 867715200, // 1 Jul 1997 + 915148800, // 1 Jan 1999 + 1136073600, // 1 Jan 2006 + 1230768000, // 1 Jan 2009 + 1341100800, // 1 Jul 2012 + 1435708800, // 1 Jul 2015 + 1483228800, // 1 Jan 2017 + }; + + auto __s = __ut.time_since_epoch().count(); + auto __pos = std::upper_bound(__leaps.begin(), __leaps.end(), __s); + return { + __pos != __leaps.begin() && __pos[-1] == __s, + seconds{__pos - __leaps.begin()} + }; + } + else + { + auto __s = chrono::time_point_cast(__ut); + return chrono::get_leap_second_info(__s); + } + } + /// @} group chrono #endif // C++20 } // namespace chrono diff --git a/libstdc++-v3/testsuite/std/time/clock/gps/1.cc b/libstdc++-v3/testsuite/std/time/clock/gps/1.cc new file mode 100644 index 00000000000..9403ee1ecca --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/gps/1.cc @@ -0,0 +1,38 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include + +void +test01() +{ + using namespace std::chrono; + + gps_seconds gps_epoch{0s}; + utc_seconds gps_as_utc{sys_days{1980y/January/Sunday[1]}.time_since_epoch() + 9s}; + + VERIFY( clock_cast(gps_epoch) == gps_as_utc ); + VERIFY( gps_epoch == clock_cast(gps_as_utc) ); + + tai_seconds tai_epoch{0s}; + VERIFY( clock_cast(clock_cast(tai_epoch)) == tai_epoch ); +} + +void +test02() +{ + using namespace std::chrono; + + sys_days d{2022y/November/12}; + VERIFY( clock_cast(clock_cast(d)) == d ); + gps_seconds t(1234567s); + VERIFY( clock_cast(clock_cast(t)) == t ); + VERIFY( clock_cast(clock_cast(t)) == t ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/time/clock/tai/1.cc b/libstdc++-v3/testsuite/std/time/clock/tai/1.cc new file mode 100644 index 00000000000..9b36f023c68 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/tai/1.cc @@ -0,0 +1,41 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include + +void +test01() +{ + using namespace std::chrono; + + tai_seconds tai_epoch{0s}; + utc_seconds tai_as_utc{sys_days{1958y/January/1}.time_since_epoch() - 10s}; + + VERIFY( clock_cast(tai_epoch) == tai_as_utc ); + VERIFY( tai_epoch == clock_cast(tai_as_utc) ); + + sys_days y2k{2000y/January/1}; + tai_seconds y2k_as_tai{clock_cast(y2k)}; + utc_seconds y2k_as_utc = utc_clock::from_sys(y2k); + VERIFY( clock_cast(y2k_as_tai) == y2k_as_utc ); + VERIFY( y2k_as_tai == clock_cast(y2k_as_utc) ); +} + +void +test02() +{ + using namespace std::chrono; + + sys_days d{2022y/November/12}; + VERIFY( clock_cast(clock_cast(d)) == d ); + tai_seconds t(1234567s); + VERIFY( clock_cast(clock_cast(t)) == t ); + VERIFY( clock_cast(clock_cast(t)) == t ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/1.cc b/libstdc++-v3/testsuite/std/time/clock/utc/1.cc new file mode 100644 index 00000000000..eef5f3c3a48 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/utc/1.cc @@ -0,0 +1,24 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include + +void +test01() +{ + using namespace std::chrono; + + auto epoch = sys_seconds{sys_days{1970y/January/1}}; + auto utc_epoch = clock_cast(epoch); + VERIFY( utc_epoch.time_since_epoch() == 0s ); + + auto y2k = sys_seconds{sys_days{2000y/January/1}}; + auto utc_y2k = clock_cast(y2k); + VERIFY( utc_y2k.time_since_epoch() == 946'684'822s ); +} + +int main() +{ + test01(); +}