diff options
Diffstat (limited to 'libstdc++-v3/include/std/chrono')
-rw-r--r-- | libstdc++-v3/include/std/chrono | 696 |
1 files changed, 549 insertions, 147 deletions
diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index 33653f8..aeb8f6f 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -46,11 +46,17 @@ # include <string> # include <vector> # include <bits/charconv.h> // __to_chars_len, __to_chars_10_impl -# include <bits/stl_algo.h> // upper_bound TODO: move leap_second_info to .so +# include <bits/stl_algo.h> // upper_bound # include <bits/shared_ptr.h> # include <bits/unique_ptr.h> #endif +#if __cplusplus >= 202002L +// TODO formatting and parsing +// # undef __cpp_lib_chrono +// # define __cpp_lib_chrono 201907L +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -140,7 +146,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { 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}}; + sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed}; if (__li.is_leap_second) __s = chrono::floor<seconds>(__s) + seconds{1} - _CDur{1}; return __s; @@ -149,13 +155,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Duration> [[nodiscard]] static utc_time<common_type_t<_Duration, seconds>> - 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}; - } + from_sys(const sys_time<_Duration>& __t); }; /** A clock that measures International Atomic Time. @@ -2056,7 +2056,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION - chrono::weekday{sys_days{_M_y / _M_m / 1}} + days((_M_wdi.index()-1)*7 + 1)); __glibcxx_assert(__d.count() >= 1); - return __d.count() <= unsigned{(_M_y / _M_m / last).day()}; + return (unsigned)__d.count() <= (unsigned)(_M_y / _M_m / last).day(); } friend constexpr bool @@ -2500,8 +2500,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } +#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI // C++20 [time.zones] Time zones + struct tzdb; + struct sys_info { sys_seconds begin; @@ -2532,9 +2535,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __glibcxx_assert(__i.result == local_info::nonexistent); } private: - template<typename _Duration> // TODO + template<typename _Duration> static string - _S_make_what_str(const local_time<_Duration>&, const local_info&); + _S_make_what_str(const local_time<_Duration>& __tp, + const local_info& __i) + { +#if 1 + return "local time is non-existent"; +#else + std::ostringstream __os; + __os << __tp << " is in a gap between\n" + << local_seconds(__i.first.end.time_since_epoch()) + + __i.first.offset << ' ' << __i.first.abbrev << " and\n" + << local_seconds(__i.second.begin.time_since_epoch()) + + __i.second.offset << ' ' << __i.second.abbrev + << " which are both equivalent to\n" + << __i.first.end << " UTC"; + return std::move(__os).str(); +#endif + } }; class ambiguous_local_time : public runtime_error @@ -2542,16 +2561,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION public: template<typename _Duration> ambiguous_local_time(const local_time<_Duration>& __tp, - const local_info& __i) + const local_info& __i) : runtime_error(_S_make_what_str(__tp, __i)) - { __glibcxx_assert(__i.result == local_info::nonexistent); } + { __glibcxx_assert(__i.result == local_info::ambiguous); } private: - template<typename _Duration> // TODO + template<typename _Duration> static string - _S_make_what_str(const local_time<_Duration>&, const local_info&); + _S_make_what_str(const local_time<_Duration>& __tp, + const local_info& __i) + { +#if 1 + return "local time is ambiguous"; +#else + std::ostringstream __os; + __os << __tp << " is ambiguous. It could be\n" + << __tp << ' ' << __i.first.abbrev << " == " + << __tp - __i.first.offset << " UTC or\n" + << __tp << ' ' << __i.second.abbrev << " == " + << __tp - __i.second.offset << " UTC"; + return std::move(__os).str(); +#endif + } }; + template<typename _Duration> + [[noreturn]] void + __throw_bad_local_time(const local_time<_Duration>& __tp, + const local_info& __i) + { +#if __cpp_exceptions + if (__i.result == local_info::nonexistent) + throw nonexistent_local_time(__tp, __i); + throw ambiguous_local_time(__tp, __i); +#else + __builtin_abort(); +#endif + } + enum class choose { earliest, latest }; class time_zone @@ -2560,46 +2607,188 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION time_zone(time_zone&&) = default; time_zone& operator=(time_zone&&) = default; + ~time_zone(); + + [[nodiscard]] string_view name() const noexcept { return _M_name; } template<typename _Duration> sys_info - get_info(const sys_time<_Duration>& __st) const; + get_info(const sys_time<_Duration>& __st) const + { return _M_get_sys_info(chrono::floor<seconds>(__st)); } template<typename _Duration> local_info - get_info(const local_time<_Duration>& __tp) const; + get_info(const local_time<_Duration>& __tp) const + { return _M_get_local_info(chrono::floor<seconds>(__tp)); } template<typename _Duration> sys_time<common_type_t<_Duration, seconds>> - to_sys(const local_time<_Duration>& __tp) const; + to_sys(const local_time<_Duration>& __tp) const + { + local_info __info = get_info(__tp); + + if (__info.result != local_info::unique) + __throw_bad_local_time(__tp, __info); + + return sys_time<_Duration>(__tp.time_since_epoch()) + - __info.first.offset; + } template<typename _Duration> sys_time<common_type_t<_Duration, seconds>> - to_sys(const local_time<_Duration>& __tp, choose __z) const; + to_sys(const local_time<_Duration>& __tp, choose __z) const + { + local_info __info = get_info(__tp); + + if (__info.result == local_info::nonexistent) + return __info.first.end; // Last second of the previous sys_info. + + sys_time<_Duration> __st(__tp.time_since_epoch()); + + if (__info.result == local_info::ambiguous && __z == choose::latest) + return __st - __info.second.offset; // Time in the later sys_info. + // else if __z == earliest, use __info.first.offset as below: + + return __st - __info.first.offset; + } template<typename _Duration> local_time<common_type_t<_Duration, seconds>> - to_local(const sys_time<_Duration>& __tp) const; + to_local(const sys_time<_Duration>& __tp) const + { + auto __d = (__tp + get_info(__tp).offset).time_since_epoch(); + return local_time<common_type_t<_Duration, seconds>>(__d); + } - friend bool + [[nodiscard]] friend bool operator==(const time_zone& __x, const time_zone& __y) noexcept - { return __x.name() == __y.name(); } + { return __x._M_name == __y._M_name; } - friend strong_ordering + [[nodiscard]] friend strong_ordering operator<=>(const time_zone& __x, const time_zone& __y) noexcept - { return __x.name() <=> __y.name(); } + { return __x._M_name <=> __y._M_name; } private: - string _M_name; + sys_info _M_get_sys_info(sys_seconds) const; + local_info _M_get_local_info(local_seconds) const; + + friend const tzdb& reload_tzdb(); + friend struct tzdb; + friend class tzdb_list; + struct _Impl; + + explicit time_zone(unique_ptr<_Impl> __p); + string _M_name; unique_ptr<_Impl> _M_impl; }; - struct tzdb; const time_zone* locate_zone(string_view __tz_name); const time_zone* current_zone(); + /** The list of `chrono::tzdb` objects + * + * A single object of this type is constructed by the C++ runtime, + * and can be accessed by calling `chrono::get_tzdb_list()`. + * + * The front of the list is the current `tzdb` object and can be accessed + * via `chrono::get_tzdb_list().front()` or `chrono::get_tzdb()` or + * `*chrono::get_tzdb_list().begin()`. + * + * The `chrono::reload_tzdb()` function will check for a newer version + * and if found, insert it at the front of the list. + * + * @since C++20 + */ + class tzdb_list + { + struct _Node; + + public: + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + /** An iterator into the `tzdb_list` + * + * As a extension, in libstdc++ each `tzdb` is reference-counted + * and the `const_iterator` type shares ownership of the object it + * refers to. This ensures that a `tzdb` erased from the list will + * not be destroyed while there is an iterator that refers to it. + */ + 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; + + friend class tzdb_list; + + shared_ptr<_Node> _M_node; + void* _M_reserved = nullptr; + }; + + /** Access the current `tzdb` at the front of the list. + * + * This returns a reference to the same object as `chrono::get_tzdb()`. + * + * @returns A reference to the current tzdb object. + * @since C++20 + */ + const tzdb& front() const noexcept; + + /** Remove the tzdb object _after_ the one the iterator refers to. + * + * Calling this function concurently with any of `front()`, `begin()`, + * or `end()` does not cause a data race, but in general this function + * is not thread-safe. The behaviour may be undefined if erasing an + * element from the list while another thread is calling the same + * function, or incrementing an iterator into the list, or accessing + * the element being erased (unless it is accessed through an iterator). + * + * @param __p A dereferenceable iterator. + * @returns An iterator the element after the one that was erased + * (or `end()` if there is no such element). + * @since C++20 + */ + const_iterator erase_after(const_iterator __p); + + 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 tzdb_list& get_tzdb_list(); + friend const tzdb& get_tzdb(); + friend const tzdb& reload_tzdb(); + friend struct tzdb; + friend class leap_second; + friend struct time_zone::_Impl; + friend class time_zone_link; + }; + class time_zone_link { public: @@ -2619,7 +2808,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: friend const tzdb& reload_tzdb(); - // TODO unspecified additional constructors + friend class tzdb_list::_Node; + + explicit time_zone_link(nullptr_t) { } + string _M_name; string _M_target; }; @@ -2720,10 +2912,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: explicit leap_second(seconds::rep __s) : _M_s(__s) { } + friend class tzdb_list::_Node; + friend const tzdb& reload_tzdb(); - template<typename _Dur> + + template<typename _Duration> friend leap_second_info - get_leap_second_info(const utc_time<_Dur>&); + get_leap_second_info(const utc_time<_Duration>&); seconds _M_s; // == date().time_since_epoch() * value().count() }; @@ -2745,9 +2940,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct tzdb { string version; - vector<time_zone> zones; - vector<time_zone_link> links; - vector<leap_second> leap_seconds; + _GLIBCXX_STD_C::vector<time_zone> zones; + _GLIBCXX_STD_C::vector<time_zone_link> links; + _GLIBCXX_STD_C::vector<leap_second> leap_seconds; const time_zone* locate_zone(string_view __tz_name) const; @@ -2757,146 +2952,353 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: friend const tzdb& reload_tzdb(); - - struct _Rule; - vector<_Rule> _M_rules; + friend class time_zone; + friend class tzdb_list::_Node; }; - class tzdb_list - { - struct _Node; - public: - tzdb_list(const tzdb_list&) = delete; - tzdb_list& operator=(const tzdb_list&) = delete; + tzdb_list& get_tzdb_list(); + const tzdb& get_tzdb(); - class const_iterator + const tzdb& reload_tzdb(); + string remote_version(); + + template<typename _Duration, typename _TimeZonePtr = const time_zone*> + class zoned_time { + static_assert(__is_duration_v<_Duration>); + + using _Traits = zoned_traits<_TimeZonePtr>; + + // Every constructor that accepts a string_view as its first parameter + // does not participate in class template argument deduction. + using string_view = type_identity_t<std::string_view>; + public: - using value_type = tzdb; - using reference = const tzdb&; - using pointer = const tzdb*; - using difference_type = ptrdiff_t; - using iterator_category = forward_iterator_tag; + using duration = common_type_t<_Duration, seconds>; - 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; + zoned_time() requires requires { _Traits::default_zone(); } + { } - reference operator*() const noexcept; - pointer operator->() const noexcept { return &**this; } - const_iterator& operator++(); - const_iterator operator++(int); + zoned_time(const zoned_time&) = default; + zoned_time& operator=(const zoned_time&) = default; - bool operator==(const const_iterator&) const noexcept = default; + zoned_time(const sys_time<_Duration>& __st) + requires requires { _Traits::default_zone(); } + : _M_tp(__st) + { } - private: - explicit const_iterator(const shared_ptr<_Node>&) noexcept; + explicit + zoned_time(_TimeZonePtr __z) : _M_zone(std::move(__z)) { } - shared_ptr<_Node> _M_node; - void* _M_reserved = nullptr; - }; + explicit + zoned_time(string_view __name) + requires requires { + _TimeZonePtr{_Traits::locate_zone(std::string_view{})}; + } + : _M_zone(_Traits::locate_zone(__name)) + { } - // TODO const tzdb& front() const noexcept; + template<typename _Duration2> + zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt) + requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> + : _M_zone(__zt._M_zone), _M_tp(__zt._M_tp) + { } - const_iterator erase_after(const_iterator); + zoned_time(_TimeZonePtr __z, const sys_time<_Duration>& __st) + : _M_zone(std::move(__z)), _M_tp(__st) + { } - 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(); } + zoned_time(string_view __name, const sys_time<_Duration>& __st) + : zoned_time(_Traits::locate_zone(__name), __st) + { } - private: - constexpr explicit tzdb_list(nullptr_t); + zoned_time(_TimeZonePtr __z, const local_time<_Duration>& __tp) + requires requires { + { __z->to_sys(__tp) } -> convertible_to<sys_time<_Duration>>; + } + : _M_zone(std::move(__z)), _M_tp(_M_zone->to_sys(__tp)) + { } - friend const tzdb_list& get_tzdb_list(); - friend const tzdb& get_tzdb(); - friend const tzdb& reload_tzdb(); + zoned_time(string_view __name, const local_time<_Duration>& __tp) + requires requires (_TimeZonePtr __z) { + { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>; + { __z->to_sys(__tp) } -> convertible_to<sys_time<_Duration>>; + } + : zoned_time(_Traits::locate_zone(__name), __tp) + { } - static _Node* _S_head; - static shared_ptr<_Node> _S_head_owner; - }; + zoned_time(_TimeZonePtr __z, const local_time<_Duration>& __tp, + choose __c) + requires requires { + { __z->to_sys(__tp, __c) } -> convertible_to<sys_time<_Duration>>; + } + : _M_zone(std::move(__z)), _M_tp(_M_zone->to_sys(__tp, __c)) + { } - // TODO - // const tzdb_list& get_tzdb_list(); - // const tzdb& get_tzdb(); + zoned_time(string_view __name, const local_time<_Duration>& __tp, + choose __c) + requires requires (_TimeZonePtr __z) { + { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>; + { __z->to_sys(__tp, __c) } -> convertible_to<sys_time<_Duration>>; + } + : _M_zone(_Traits::locate_zone(__name)), + _M_tp(_M_zone->to_sys(__tp, __c)) + { } - // const tzdb& reload_tzdb(); - // string remove_version(); + template<typename _Duration2, typename _TimeZonePtr2> + zoned_time(_TimeZonePtr __z, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> + : _M_zone(__z), _M_tp(__zt._M_tp) + { } + + template<typename _Duration2, typename _TimeZonePtr2> + zoned_time(_TimeZonePtr __z, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt, + choose __c) + requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> + : _M_zone(__z), _M_tp(__zt._M_tp) + { } + + template<typename _Duration2, typename _TimeZonePtr2> + zoned_time(string_view __name, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> + && requires { + { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>; + } + : _M_zone(_Traits::locate_zone(__name)), _M_tp(__zt._M_tp) + { } + + template<typename _Duration2, typename _TimeZonePtr2> + zoned_time(string_view __name, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt, + choose __c) + requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> + && requires { + { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>; + } + : _M_zone(_Traits::locate_zone(__name)), _M_tp(__zt._M_tp) + { } - template<typename _Duration, typename _TimeZonePtr = const time_zone*> - class zoned_time; // TODO + zoned_time& + operator=(const sys_time<_Duration>& __st) + { + _M_tp = __st; + return *this; + } + + zoned_time& + operator=(const local_time<_Duration>& __lt) + { + _M_tp = _M_zone->to_sys(__lt); + return *this; + } + + [[nodiscard]] + operator sys_time<duration>() const { return _M_tp; } + + [[nodiscard]] + explicit operator local_time<duration>() const + { return get_local_time(); } + + [[nodiscard]] + _TimeZonePtr + get_time_zone() const + { return _M_zone; } + + [[nodiscard]] + local_time<duration> + get_local_time() const + { return _M_zone->to_local(_M_tp); } + + [[nodiscard]] + sys_time<duration> + get_sys_time() const + { return _M_tp; } + + [[nodiscard]] + sys_info + get_info() const + { return _M_zone->get_info(_M_tp); } + + [[nodiscard]] friend bool + operator==(const zoned_time&, const zoned_time&) = default; + + private: + _TimeZonePtr _M_zone{ _Traits::default_zone() }; + sys_time<duration> _M_tp{}; + + template<typename _Duration2, typename _TimeZonePtr2> + friend class zoned_time; + }; + + zoned_time() -> zoned_time<seconds>; + + template<typename _Duration> + zoned_time(sys_time<_Duration>) + -> zoned_time<common_type_t<_Duration, seconds>>; + + /// @cond undocumented + template<typename _TimeZonePtrOrName> + using __time_zone_representation + = __conditional_t<is_convertible_v<_TimeZonePtrOrName, string_view>, + const time_zone*, + remove_cvref_t<_TimeZonePtrOrName>>; + /// @endcond + + template<typename _TimeZonePtrOrName> + zoned_time(_TimeZonePtrOrName&&) + -> zoned_time<seconds, __time_zone_representation<_TimeZonePtrOrName>>; + + template<typename _TimeZonePtrOrName, typename _Duration> + zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>) + -> zoned_time<common_type_t<_Duration, seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + + template<typename _TimeZonePtrOrName, typename _Duration> + zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, + choose = choose::earliest) + -> zoned_time<common_type_t<_Duration, seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + + template<typename _Duration, typename _TimeZonePtrOrName, + typename _TimeZonePtr2> + zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, + choose = choose::earliest) + -> zoned_time<common_type_t<_Duration, seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + + template<typename _Dur1, typename _TZPtr1, typename _Dur2, typename _TZPtr2> + [[nodiscard]] + inline bool + operator==(const zoned_time<_Dur1, _TZPtr1>& __x, + const zoned_time<_Dur2, _TZPtr2>& __y) + { + return __x.get_time_zone() == __y.get_time_zone() + && __x.get_sys_time() == __y.get_sys_time(); + } using zoned_seconds = zoned_time<seconds>; +#endif // _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + +namespace __detail +{ + inline leap_second_info + __get_leap_second_info(sys_seconds __ss, bool __is_utc) + { + if (__ss < sys_seconds{}) [[unlikely]] + return {}; + + const seconds::rep __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 + }; + // The list above is known to be valid until (at least) this date + // and only contains positive leap seconds. + const sys_seconds __expires(1687910400s); // 2023-06-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 +#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}; + } + } +} // namespace __detail template<typename _Duration> - leap_second_info + [[nodiscard]] + inline leap_second_info get_leap_second_info(const utc_time<_Duration>& __ut) { - if constexpr (is_same_v<_Duration, seconds>) - { - const seconds::rep __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 - }; - // The list above is known to be valid until 2023-06-28 00:00:00 UTC - const seconds::rep __expires = 1687910400; - const seconds::rep __s = __ut.time_since_epoch().count(); - - const seconds::rep* __first = std::begin(__leaps); - const seconds::rep* __last = std::end(__leaps); - - if (__s > __expires) - { - // TODO: use updated leap_seconds from tzdb -#if 0 - auto __db = get_tzdb_list().begin(); - __first = __db->leap_seconds.data(); - __last = __first + __db->leap_seconds.size(); -#endif - } - - // Don't bother searching the list if we're after the last one. - if (__s > __last[-1]) - return { false, seconds(__last - __first) }; + auto __s = chrono::duration_cast<seconds>(__ut.time_since_epoch()); + return __detail::__get_leap_second_info(sys_seconds(__s), true); + } - auto __pos = std::upper_bound(__first, __last, __s); - return { - __pos != begin(__leaps) && __pos[-1] == __s, - seconds{__pos - __first} - }; - } - else - { - auto __s = chrono::time_point_cast<seconds>(__ut); - return chrono::get_leap_second_info(__s); - } + template<typename _Duration> + [[nodiscard]] + inline utc_time<common_type_t<_Duration, seconds>> + utc_clock::from_sys(const sys_time<_Duration>& __t) + { + using _CDur = common_type_t<_Duration, seconds>; + auto __s = chrono::time_point_cast<seconds>(__t); + const auto __li = __detail::__get_leap_second_info(__s, false); + return utc_time<_CDur>{__t.time_since_epoch()} + __li.elapsed; } /// @} group chrono |