diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 225d6dc482b..8e5d0bf081f 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2485,6 +2485,14 @@ GLIBCXX_3.4.31 { _ZSt15__try_use_facet*; + _ZNSt6chrono11reload_tzdbEv; + _ZNSt6chrono8get_tzdbEv; + _ZNSt6chrono13get_tzdb_listEv; + _ZNSt6chrono14remote_versionB5cxx11Ev; + _ZNSt6chrono14remote_versionEv; + _ZNKSt6chrono4tzdb12current_zoneEv; + _ZNKSt6chrono4tzdb11locate_zoneESt17basic_string_viewIcSt11char_traitsIcEE; + } GLIBCXX_3.4.30; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 27dfa2be2f3..08ce9abbcbc 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -175,6 +175,7 @@ bits_headers = \ ${bits_srcdir}/char_traits.h \ ${bits_srcdir}/charconv.h \ ${bits_srcdir}/chrono.h \ + ${bits_srcdir}/chrono_io.h \ ${bits_srcdir}/codecvt.h \ ${bits_srcdir}/cow_string.h \ ${bits_srcdir}/deque.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 64621922f77..401d0eead58 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -528,6 +528,7 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/char_traits.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/charconv.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/chrono.h \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/chrono_io.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/codecvt.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/cow_string.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/deque.tcc \ diff --git a/libstdc++-v3/include/bits/chrono.h b/libstdc++-v3/include/bits/chrono.h index 05987ca09df..432b25affea 100644 --- a/libstdc++-v3/include/bits/chrono.h +++ b/libstdc++-v3/include/bits/chrono.h @@ -1069,6 +1069,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const time_point<_Clock, _Dur2>& __rhs) { return !(__lhs < __rhs); } +#if __cpp_variable_templates + template + inline constexpr bool __is_duration_v = false; + template + inline constexpr bool __is_duration_v> = true; + template + inline constexpr bool __is_time_point_v = false; + template + inline constexpr bool __is_time_point_v> = true; +#endif + /// @} /// @} group chrono diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h new file mode 100644 index 00000000000..779a65ece91 --- /dev/null +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -0,0 +1,1633 @@ +// Formatting -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/bits/chrono_io.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{chrono} + */ + +#ifndef _GLIBCXX_CHRONO_IO_H +#define _GLIBCXX_CHRONO_IO_H 1 + +#pragma GCC system_header + +#if __cplusplus >= 202002L + +#include // ostringstream +#include // setw, setfill +#include + +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +namespace chrono +{ +/// @addtogroup chrono +/// @{ + +/// @cond undocumented +namespace __detail +{ + // STATICALLY-WIDEN, see C++20 [time.general] + // It doesn't matter for format strings (which can only be char or wchar_t) + // but this returns the narrow string for anything that isn't wchar_t. This + // is done because const char* can be inserted into any ostream type, and + // will be widened at runtime if necessary. + template + consteval auto + _Widen(const char* __narrow, const wchar_t* __wide) + { + if constexpr (is_same_v<_CharT, wchar_t>) + return __wide; + else + return __narrow; + } +#define _GLIBCXX_WIDEN_(C, S) ::std::chrono::__detail::_Widen(S, L##S) +#define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S) + + + // Write an arbitrary duration suffix into the buffer. + template + constexpr const char* + __units_suffix_misc(char* __buf, size_t /* TODO check length? */) noexcept + { + namespace __tc = std::__detail; + char* __p = __buf; + __p[0] = '['; + unsigned __nlen = __tc::__to_chars_len((uintmax_t)_Period::num); + __tc::__to_chars_10_impl(__p + 1, __nlen, (uintmax_t)_Period::num); + __p += 1 + __nlen; + if constexpr (_Period::den != 1) + { + __p[0] = '/'; + unsigned __dlen = __tc::__to_chars_len((uintmax_t)_Period::den); + __tc::__to_chars_10_impl(__p + 1, __dlen, (uintmax_t)_Period::den); + __p += 1 + __dlen; + } + __p[0] = ']'; + __p[1] = 's'; + __p[2] = '\0'; + return __buf; + } + + template + constexpr auto + __units_suffix(char* __buf, size_t __n) noexcept + { + // The standard say these are all narrow strings, which would need to + // be widened at run-time when inserted into a wide stream. We use + // STATICALLY-WIDEN to widen at compile-time. +#define _GLIBCXX_UNITS_SUFFIX(period, suffix) \ + if constexpr (is_same_v<_Period, period>) \ + return _GLIBCXX_WIDEN(suffix); \ + else + + _GLIBCXX_UNITS_SUFFIX(atto, "as") + _GLIBCXX_UNITS_SUFFIX(femto, "fs") + _GLIBCXX_UNITS_SUFFIX(pico, "ps") + _GLIBCXX_UNITS_SUFFIX(nano, "ns") + _GLIBCXX_UNITS_SUFFIX(milli, "ms") +#if _GLIBCXX_USE_ALT_MICROSECONDS_SUFFIX + // Deciding this at compile-time is wrong, maybe use nl_langinfo(CODESET) + // to check runtime environment and return u8"\u00b5s", "\xb5s", or "us". + _GLIBCXX_UNITS_SUFFIX(micro, "\u00b5s") +#else + _GLIBCXX_UNITS_SUFFIX(micro, "us") +#endif + _GLIBCXX_UNITS_SUFFIX(centi, "cs") + _GLIBCXX_UNITS_SUFFIX(deci, "ds") + _GLIBCXX_UNITS_SUFFIX(ratio<1>, "s") + _GLIBCXX_UNITS_SUFFIX(deca, "das") + _GLIBCXX_UNITS_SUFFIX(hecto, "hs") + _GLIBCXX_UNITS_SUFFIX(kilo, "ks") + _GLIBCXX_UNITS_SUFFIX(mega, "Ms") + _GLIBCXX_UNITS_SUFFIX(giga, "Gs") + _GLIBCXX_UNITS_SUFFIX(tera, "Ts") + _GLIBCXX_UNITS_SUFFIX(tera, "Ts") + _GLIBCXX_UNITS_SUFFIX(peta, "Ps") + _GLIBCXX_UNITS_SUFFIX(exa, "Es") + _GLIBCXX_UNITS_SUFFIX(ratio<60>, "min") + _GLIBCXX_UNITS_SUFFIX(ratio<3600>, "h") + _GLIBCXX_UNITS_SUFFIX(ratio<86400>, "d") +#undef _GLIBCXX_UNITS_SUFFIX + return __detail::__units_suffix_misc<_Period>(__buf, __n); + } +} // namespace __detail +/// @endcond + + /** Write a `chrono::duration` to an ostream. + * + * @since C++20 + */ + template + inline basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __os, + const duration<_Rep, _Period>& __d) + { + using period = typename _Period::type; + char __buf[sizeof("[/]s") + 2 * numeric_limits::digits10]; + std::basic_ostringstream<_CharT, _Traits> __s; + __s.flags(__os.flags()); + __s.imbue(__os.getloc()); + __s.precision(__os.precision()); + __s << __d.count(); + __s << __detail::__units_suffix(__buf, sizeof(__buf)); + __os << std::move(__s).str(); + return __os; + } + +/// @cond undocumented +namespace __detail +{ + // An unspecified type returned by `chrono::local_time_format`. + template + struct __local_time_fmt + { + local_time<_Duration> _M_time; + const string* _M_abbrev; + const seconds* _M_offset_sec; + }; + + struct __local_fmt_t; +} +/// @endcond + + /** Return an object that asssociates timezone info with a local time. + * + * A `chrono::local_time` object has no timezone associated with it. This + * function creates an object that allows formatting a `local_time` as + * though it refers to a timezone with the given abbreviated name and + * offset from UTC. + * + * @since C++20 + */ + template + inline __detail::__local_time_fmt<_Duration> + local_time_format(local_time<_Duration> __time, + const string* __abbrev = nullptr, + const seconds* __offset_sec = nullptr) + { return {__time, __abbrev, __offset_sec}; } + + /// @} +} // namespace chrono + +/// @cond undocumented +namespace __format +{ + [[noreturn,__gnu__::__always_inline__]] + inline void + __no_timezone_available() + { __throw_format_error("format error: no timezone available for %Z or %z"); } + + [[noreturn,__gnu__::__always_inline__]] + inline void + __not_valid_for_duration() + { __throw_format_error("format error: chrono-format-spec not valid for " + "chrono::duration"); } + + template + struct _ChronoSpec : _Spec<_CharT> + { + basic_string_view<_CharT> _M_chrono_specs; + }; + + // Represents the information provided by a chrono type. + // e.g. month_weekday has month and weekday but no year or time of day, + // hh_mm_ss has time of day but no date, utc_time is time_point+timezone. + enum _ChronoParts { + _Year = 1, _Month = 2, _Day = 4, _Weekday = 8, _TimeOfDay = 16, + _TimeZone = 32, + _Date = _Year | _Month | _Day | _Weekday, + _DateTime = _Date | _TimeOfDay, + }; + + constexpr _ChronoParts + operator|(_ChronoParts __x, _ChronoParts __y) + { return static_cast<_ChronoParts>((int)__x | (int)__y); } + + template + struct __formatter_chrono + { + template + constexpr typename _ParseContext::iterator + _M_parse(_ParseContext& __pc, _ChronoParts __parts) + { + auto __first = __pc.begin(); + const auto __last = __pc.end(); + _ChronoSpec<_CharT> __spec{}; + + auto __finalize = [this, &__spec] { + _M_spec = __spec; + }; + + auto __finished = [&] { + if (__first == __last || *__first == '}') + { + __finalize(); + return true; + } + return false; + }; + + if (__finished()) + return __first; + + __first = __spec._M_parse_fill_and_align(__first, __last); + if (__finished()) + return __first; + + __first = __spec._M_parse_width(__first, __last, __pc); + if (__finished()) + return __first; + + __first = __spec._M_parse_precision(__first, __last, __pc); + if (__finished()) + return __first; + + __first = __spec._M_parse_locale(__first, __last); + if (__finished()) + return __first; + + // TODO parse chrono-specs, stop at __last or '}' or '{' + // check each conversion-spec against __parts + // (so fail for %Y if no year in parts) + // save range in __spec._M_chrono_specs + + const auto __chrono_specs = __first; + + while (!__finished()) + { + if (*__first == '{') + __throw_format_error("chrono format error: '{' in chrono-specs"); + if (*__first != '%') + { + // literal-char + ++__first; + continue; + } + + if (__first == __last) + __throw_format_error("chrono format error: unescaped '%' " + " in chrono-specs"); + + if (*__first == '%' || *__first == 'n' || *__first == 't') + { + // escaped character + ++__first; + continue; + } + + char __modifier = 0; + if (*__first == 'E' || *__first == 'O') + __modifier = *__first++; + + switch (*__first) + { + // Types that do not support a modifier. + case 'a': + case 'A': + case 'b': + case 'B': + case 'D': + case 'F': + case 'g': + case 'G': + case 'h': + case 'j': + case 'p': + case 'q': + case 'Q': + case 'r': + case 'R': + case 'T': + case 'Z': + if (__modifier) + __modifier = 'x'; + break; + // Types that do not support the O modifier. + case 'c': + case 'C': + case 'x': + case 'X': + case 'Y': + if (__modifier == 'O') + __modifier = 'x'; + break; + // Types that do not support the E modifier. + case 'd': + case 'e': + case 'H': + case 'I': + case 'm': + case 'M': + case 'S': + case 'u': + case 'U': + case 'V': + case 'w': + case 'W': + if (__modifier == 'E') + __modifier = 'x'; + break; + // Types that support both the E and O modifiers. + case 'y': + case 'z': + break; + default: + __throw_format_error("chrono format error: invalid " + "conversion specifier"); + } + if (__modifier == 'x') + __throw_format_error("chrono format error: invalid modifier"); + + _ChronoParts __needed{}; + switch (*__first) + { + case 'a': + case 'A': + case 'w': + __needed = _Weekday; + break; + case 'b': + case 'h': + case 'B': + case 'm': + __needed = _Month; + break; + case 'c': + __needed = _DateTime; + break; + case 'C': + case 'g': + case 'y': + case 'Y': + __needed = _Year; + break; + case 'd': + case 'e': + // TODO day of the month + break; + case 'D': + case 'F': + case 'G': + case 'u': + case 'x': + __needed = _Date; + break; + case 'H': + case 'I': + case 'M': + case 'p': + case 'r': + case 'R': + case 'S': + case 'T': + case 'X': + __needed = _TimeOfDay; + break; + case 'j': + case 'q': + case 'Q': + // TODO needs special handling + break; + case 'U': + case 'V': + case 'W': + __needed = _Month | _Day; + break; + case 'z': + case 'Z': + __needed = _TimeZone; + break; + } + if ((__needed & __parts) != __needed) + __throw_format_error("chrono format error: format argument " + "does not contain the information " + "requested by the chrono-specs"); + ++__first; + } + size_t __n = __first - __chrono_specs; + _M_spec._M_chrono_specs = {__chrono_specs, __n}; + return __first; + } + + template + typename _FormatContext::iterator + _M_format(const _Tp& __t, _FormatContext& __ctx) const + { + basic_ostringstream<_CharT> __os; + if (!_M_spec._M_localized) + __os.imbue(std::locale::classic()); + else + __os.imbue(__ctx.locale()); + + if constexpr (chrono::__is_duration_v<_Tp> + ||__is_specialization_of<_Tp, chrono::hh_mm_ss>) + { + if (__t < __t.zero()) + { + __os << _S_plus_minus[1]; + __t = chrono::abs(__t); + } + } + else if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>) + + if (_M_spec._M_chrono_specs.empty()) + { + // TODO write to stream + } + else + { + // TODO + } + return __ctx.out(); + } + + private: + static constexpr const _CharT* _S_chars=_GLIBCXX_WIDEN("0123456789+-:/"); + static constexpr const _CharT* _S_plus_minus = _S_chars + 10; + static constexpr _CharT _S_colon = _S_chars[12]; + static constexpr _CharT _S_slash = _S_chars[13]; + + + template + static auto + _S_time_since_midnight(const _Tp& __t) + { + using ::std::chrono::__detail::__local_time_fmt; + if constexpr (chrono::__is_duration_v<_Tp>) + return __t; + else if constexpr (chrono::__is_time_point_v<_Tp>) + return __t - chrono::floor(__t); + else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) + return __t._M_time - chrono::floor(__t._M_time); + } + + template + static auto + _S_ymd_hms(const _Tp& __t) + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + // TODO: handle __local_time_fmt? + + const auto __days = chrono::floor(__t); + const auto __ymd = chrono::year_month_day{__days}; + const auto __hms = chrono::hh_mm_ss{__t - __days}; + return pair(__ymd, __hms); + } + + + template + typename _FormatContext::iterator + _M_a_A(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __full) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_b_B(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __full) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_c(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_C(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_d(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_D(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + __out = _M_m(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_slash); + __out = _M_d(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_slash); + __out = _M_y(__t, std::move(__out), __ctx); + } + + template + typename _FormatContext::iterator + _M_e(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_F(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + __out = _M_Y(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_plus_minus[1]); + __out = _M_m(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_plus_minus[1]); + __out = _M_d(__t, std::move(__out), __ctx); + } + + template + typename _FormatContext::iterator + _M_g_G(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __full) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_H(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + auto __x = _S_time_since_midnight(__t); + if (__mod) + { + // TODO: locale's alternative representation + } + else + { + } + } + + template + typename _FormatContext::iterator + _M_I(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + auto __x = _S_time_since_midnight(__t); + auto __h = chrono::duration_cast(__x); + if (__mod) + { + // TODO: locale's alternative representation + } + else + { + _CharT __buf[2]; + } + } + + template + typename _FormatContext::iterator + _M_j(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + int __d; + if constexpr (chrono::__is_duration_v<_Tp>) + __d = chrono::duration_cast(__t).count(); + else + { + } + } + + template + typename _FormatContext::iterator + _M_m(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + template + typename _FormatContext::iterator + _M_M(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + // %n handled in _M_format + + template + typename _FormatContext::iterator + _M_p(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + } + + // %q and %Q handled in _M_format + + template + typename _FormatContext::iterator + _M_r(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + if constexpr (chrono::__is_duration_v<_Tp>) + { + } + else + { + } + } + + template + typename _FormatContext::iterator + _M_R(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + __out = _M_H(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_colon); + return _M_M(__t, std::move(__out), __ctx); + } + + template + typename _FormatContext::iterator + _M_S(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + auto __x = _S_time_since_midnight(__t); + using _Xrep = typename decltype(__x)::rep; + if constexpr (chrono::treat_as_floating_point_v<_Xrep>) + { + // TODO: locale's alternative rep + } + else + { + auto __s = chrono::duration_cast(__x).count(); + __s %= 60; + _CharT __buf[2]; + __buf[0] = _S_chars[__s / 10]; + __buf[1] = _S_chars[__s % 10]; + return __format::__write(std::move(__out), {__buf, 2}); + } + } + + // %t handled in _M_format + + template + typename _FormatContext::iterator + _M_T(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + __out = _M_H(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_colon); + __out = _M_M(__t, std::move(__out), __ctx); + __format::__write(std::move(__out), _S_colon); + return _M_S(__t, std::move(__out), __ctx); + } + + template + typename _FormatContext::iterator + _M_u(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // ISO weekday as a decimal number (1-7), where Monday is 1. + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_U(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Week number of the year as a decimal number. + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_V(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // ISO week-based week number as a decimal number. + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_w(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Weekday as a decimal number (0-6), where Sunday is 0. + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_W(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Week number of the year as a decimal number. + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_x(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Locale's date rep + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_X(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Locale's time rep + if (__mod) + { + // TODO: locale's alt rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_y(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, char __mod = 0) const + { + // Last two decimal digits of the year. + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + if (__mod == 'O') + { + // TODO: locale's alt rep + } + else if (__mod == 'E') + { + // TODO: locale's alt rep of offset from %EC + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_Y(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + // Year as a decimal number. + if constexpr (chrono::__is_duration_v<_Tp>) + __not_valid_for_duration(); + if (__mod) + { + // TODO: locale's alt full year rep + } + else + { + // TODO + } + } + + template + typename _FormatContext::iterator + _M_z(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod = false) const + { + using ::std::chrono::__detail::__local_time_fmt; + + int __neg = 0, __hh = 0, __mm = 0; + if constexpr (chrono::__is_time_point_v<_Tp>) + { + using _Clock = typename _Tp::clock; + if constexpr (is_same_v<_Clock, chrono::system_clock> + || is_same_v<_Clock, chrono::utc_clock> + || is_same_v<_Clock, chrono::tai_clock> + || is_same_v<_Clock, chrono::gps_clock>) + __neg = __hh = __mm = 0; + else + __no_timezone_available(); + } + else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) + { + if (!__t._M_offset_sec) + __no_timezone_available(); + + chrono::seconds __s = chrono::abs(*__t._M_offset_sec); + auto __hours = chrono::duration_cast(__s); + auto __minutes = chrono::duration_cast(__s); + __mm = (__minutes - __hours).count(); + __hh = __hours.count(); + } + else + __no_timezone_available(); + + _CharT __buf[6] = { + _S_plus_minus[__neg], _S_chars[__hh / 10], _S_chars[__hh % 10] + }; + __buf[3] = _S_colon; + __buf[3 + __mod] = _S_chars[__mm / 10]; + __buf[4 + __mod] = _S_chars[__mm % 10]; + return __format::__write(std::move(__out), {__buf, 5u + __mod}); + } + + template + typename _FormatContext::iterator + _M_Z(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + using ::std::chrono::__detail::__local_time_fmt; + + basic_string_view<_CharT> __abbrev{nullptr, 0}; + if constexpr (chrono::__is_time_point_v<_Tp>) + { + const _CharT* __utctaigps = _GLIBCXX_WIDEN("UTCTAIGPS"); + + using _Clock = typename _Tp::clock; + if constexpr (is_same_v<_Clock, chrono::system_clock> + || is_same_v<_Clock, chrono::utc_clock>) + __abbrev = {__utctaigps, 3}; + else if constexpr (is_same_v<_Clock, chrono::tai_clock>) + __abbrev = {__utctaigps + 3, 3}; + else if constexpr (is_same_v<_Clock, chrono::gps_clock>) + __abbrev = {__utctaigps + 6, 3}; + } + else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) + { + if (__t._M_abbrev) + __abbrev = __t._M_abbrev; // TODO: widen + } + + if (!__abbrev.data()) + __no_timezone_available(); + + return __format::__write(std::move(__out), __abbrev); + } + + // %% handled in _M_format + + _ChronoSpec<_CharT> _M_spec; + }; + +} // namespace __format +/// @endcond + + template + struct formatter, _CharT> + { + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { + auto __it = _M_f._M_parse(__pc, __format::_TimeOfDay); + if constexpr (!is_floating_point_v<_Rep>) + if (_M_f._M_spec._M_prec_kind != __format::_WP_none) + __throw_format_error("format error: invalid precision for duration"); + return __it; + } + + template + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::duration<_Rep, _Period>& __d, + basic_format_context<_Out, _CharT>& __fc) const + { + return _M_f._M_format(__d, __fc); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter; + template + struct formatter; + template + struct formatter; + + template + struct formatter + { + + + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + + // TODO define these + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter; + template + struct formatter>, _CharT>; + template + struct formatter; + template + struct formatter; + + template + struct formatter, _CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::sys_time<_Duration>& __t, + _FormatContext& __pc) const + { + return _M_f._M_format(__t, __pc); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + : __format::__formatter_chrono<_CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::utc_time<_Duration>& __t, + _FormatContext& __pc) const + { + return _M_f._M_format(__t, __pc); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + : __format::__formatter_chrono<_CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::tai_time<_Duration>& __t, + _FormatContext& __pc) const + { + return _M_f._M_format(__t, __pc); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + : __format::__formatter_chrono<_CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::gps_time<_Duration>& __t, + _FormatContext& __pc) const + { + return _M_f._M_format(__t, __pc); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::file_time<_Duration>& __t, + _FormatContext& __ctx) const + { + using namespace chrono; + return _M_f._M_format(clock_cast(__t), __ctx); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_Date|__format::_TimeOfDay); + } + + template + typename _FormatContext::iterator + format(const chrono::local_time<_Duration>& __t, + _FormatContext& __ctx) const + { + return _M_f._M_format(__t, __ctx); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + { + template + constexpr typename _ParseContext::iterator + parse(_ParseContext& __pc) + { + return _M_f._M_parse(__pc, __format::_DateTime); + } + + template + typename _FormatContext::iterator + format(const chrono::__detail::__local_time_fmt<_Duration>& __t, + _FormatContext& __ctx) const + { + return _M_f._M_format(__t, __ctx); + } + + private: + __format::__formatter_chrono<_CharT> _M_f; + }; + + template + struct formatter, _CharT> + : formatter, _CharT> + { + template + typename _FormatContext::iterator + format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp, + _FormatContext& __ctx) const + { + using chrono::__detail::__local_time_fmt; + using _Base = formatter<__local_time_fmt<_Duration>, _CharT>; + const chrono::sys_info __info = __tp.get_info(); + return _Base::format({__tp.get_local_time(), &__info.abbrev, + &__info.offset}, __ctx); + } + }; + +namespace chrono +{ +/// @addtogroup chrono +/// @{ + + // TODO: from_stream for duration +#if 0 + template> + basic_istream<_CharT, _Traits>& + from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt, + duration<_Rep, _Period>& __d, + basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr, + minutes* __offset = nullptr) + { + } +#endif + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d) + { + using _Ctx = __conditional_t, + format_context, wformat_context>; + using _Str = basic_string_view<_CharT>; + _Str __s = _GLIBCXX_WIDEN("{:%02d} is not a valid day"); + if (__d.ok()) + __s = __s.substr(0, 7); + __os << std::vformat(__s, make_format_args<_Ctx>((unsigned)__d)); + return __os; + } + + // TODO from_stream for day + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const month& __m) + { + using _Ctx = __conditional_t, + format_context, wformat_context>; + using _Str = basic_string_view<_CharT>; + _Str __s = _GLIBCXX_WIDEN("{:L%b}{} is not a valid month"); + if (__m.ok()) + __os << std::vformat(__os.getloc(), __s.substr(0, 6), + make_format_args<_Ctx>(__m)); + else + __os << std::vformat(__s.substr(6), + make_format_args<_Ctx>((unsigned)__m)); + return __os; + } + + // TODO from_stream for month + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y) + { + using _Ctx = __conditional_t, + format_context, wformat_context>; + using _Str = basic_string_view<_CharT>; + _Str __s = _GLIBCXX_WIDEN("{:%04d} is not a valid year"); + if (__y.ok()) + __s = __s.substr(0, 7); + __os << std::vformat(__s, make_format_args<_Ctx>((int)__y)); + return __os; + } + + // TODO from_stream for year + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday& __wd) + { + using _Ctx = __conditional_t, + format_context, wformat_context>; + using _Str = basic_string_view<_CharT>; + _Str __s = _GLIBCXX_WIDEN("{:L%a}{} is not a valid weekday"); + if (__wd.ok()) + __os << std::vformat(__os.getloc(), __s.substr(0, 6), + make_format_args<_Ctx>(__wd)); + else + __os << std::vformat(__s.substr(6), + make_format_args<_Ctx>((unsigned)__wd)); + return __os; + } + + // TODO from_stream for weekday + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const weekday_indexed& __wdi) + { + // The standard says to format wdi.weekday() and wdi.index() using + // either "{:L}[{}]" or "{:L}[{} is not a valid index]". The {:L} spec + // means to format the weekday using ostringstream, so just do that. + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __wdi.weekday(); + const auto __i = __wdi.index(); + if constexpr (is_same_v<_CharT, char>) + __os2 << std::format("[{}", __i); + else + __os2 << std::format(L"[{}", __i); + basic_string_view<_CharT> __s = _GLIBCXX_WIDEN(" is not a valid index]"); + if (__i >= 1 && __i <= 5) + __os2 << __s.back(); + else + __os2 << __s; + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const weekday_last& __wdl) + { + // As above, just write straight to a stringstream, as if by "{:L}[last]" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __wdl.weekday() << _GLIBCXX_WIDEN("[last]"); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day& __md) + { + // As above, just write straight to a stringstream, as if by "{:L}/{}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __md.month(); + if constexpr (is_same_v<_CharT, char>) + __os2 << '/'; + else + __os2 << L'/'; + __os2 << __md.day(); + __os << __os2.view(); + return __os; + } + + // TODO from_stream for month_day + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const month_day_last& __mdl) + { + // As above, just write straight to a stringstream, as if by "{:L}/last" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __mdl.month(); + if constexpr (is_same_v<_CharT, char>) + __os2 << "/last"; + else + __os2 << L"/last"; + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const month_weekday& __mwd) + { + // As above, just write straight to a stringstream, as if by "{:L}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __mwd.month(); + if constexpr (is_same_v<_CharT, char>) + __os2 << '/'; + else + __os2 << L'/'; + __os2 << __mwd.weekday_indexed(); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const month_weekday_last& __mwdl) + { + // As above, just write straight to a stringstream, as if by "{:L}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __mwdl.month(); + if constexpr (is_same_v<_CharT, char>) + __os2 << '/'; + else + __os2 << L'/'; + __os2 << __mwdl.weekday_last(); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month& __ym) + { + // As above, just write straight to a stringstream, as if by "{}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __ym.year(); + if constexpr (is_same_v<_CharT, char>) + __os2 << '/'; + else + __os2 << L'/'; + __os2 << __ym.month(); + __os << __os2.view(); + return __os; + } + + // TODO from_stream for year_month + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const year_month_day& __ymd) + { + using _Ctx = __conditional_t, + format_context, wformat_context>; + using _Str = basic_string_view<_CharT>; + _Str __s = _GLIBCXX_WIDEN("{:%F}{} is not a valid date"); + __os << std::vformat(__ymd.ok() ? __s.substr(0, 5) : __s.substr(5), + make_format_args<_Ctx>(__ymd)); + return __os; + } + + // TODO from_stream for year_month_day + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const year_month_day_last& __ymdl) + { + // As above, just write straight to a stringstream, as if by "{}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + __os2 << __ymdl.year(); + if constexpr (is_same_v<_CharT, char>) + __os2 << '/'; + else + __os2 << L'/'; + __os2 << __ymdl.month_day_last(); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const year_month_weekday& __ymwd) + { + // As above, just write straight to a stringstream, as if by + // "{}/{:L}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + _CharT __slash; + if constexpr (is_same_v<_CharT, char>) + __slash = '/'; + else + __slash = L'/'; + __os2 << __ymwd.year() << __slash << __ymwd.month() << __slash + << __ymwd.weekday_indexed(); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const year_month_weekday_last& __ymwdl) + { + // As above, just write straight to a stringstream, as if by + // "{}/{:L}/{:L}" + basic_stringstream<_CharT> __os2; + __os2.imbue(__os.getloc); + _CharT __slash; + if constexpr (is_same_v<_CharT, char>) + __slash = '/'; + else + __slash = L'/'; + __os2 << __ymwdl.year() << __slash << __ymwdl.month() << __slash + << __ymwdl.weekday_last(); + __os << __os2.view(); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const hh_mm_ss<_Duration>& __hms) + { + return __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%T}"), __hms); + } + + template + basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i) + { + // Streams out the sys_info object r in an unspecified format. + __os << '[' << __i.begin.time_since_epoch().count() + << ' ' << __i.end.time_since_epoch().count() + << ' ' << __i.offset.count() + << ' ' << __i.save.count() + << ' ' << __i.abbrev + << ']'; + return __os; + } + + template + basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __i) + { + // Streams out the local_info object r in an unspecified format. + __os << '[' << __i.result + << ' ' << __i.first + << ' ' << __i.second + << ']'; + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const zoned_time<_Duration, _TimeZonePtr>& __t) + { + __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T %Z}"), __t); + return __os; + } + + + template + requires (!treat_as_floating_point_v) + && ratio_less_v + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const sys_time<_Duration>& __tp) + { + __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __tp); + return __os; + } + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp) + { + __os << year_month_day{__dp}; + return __os; + } + + // TODO: from_stream for sys_time + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const utc_time<_Duration>& __t) + { + __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); + return __os; + } + + // TODO: from_stream for utc_time + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const tai_time<_Duration>& __t) + { + __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); + return __os; + } + + // TODO: from_stream for tai_time + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const gps_time<_Duration>& __t) + { + __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); + return __os; + } + + // TODO: from_stream for gps_time + + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const file_time<_Duration>& __t) + { + __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); + return __os; + } + + // TODO: from_stream for file_time + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const local_time<_Duration>& __lt) + { + __os << sys_time<_Duration>{__lt.time_since_epoch()}; + return __os; + } + + // TODO: from_stream for local_time +#undef _GLIBCXX_WIDEN + + /// @} group chrono +} // namespace chrono + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif // C++20 + +#endif //_GLIBCXX_CHRONO_IO_H diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index 90b73f8198e..c39161152ce 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -41,11 +41,8 @@ #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 @@ -616,8 +613,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day operator/(const year_month& __ym, const day& __d) noexcept; - - // TODO: Implement operator<<, to_stream, from_stream. }; // MONTH @@ -740,8 +735,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr month_weekday_last operator/(const weekday_last& __wdl, const month& __m) noexcept; - - // TODO: Implement operator<<, to_stream, from_stream. }; inline constexpr month January{1}; @@ -918,8 +911,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday_last operator/(const month_weekday_last& __mwdl, const year& __y) noexcept; - - // TODO: Implement operator<<, to_stream, from_stream. }; // WEEKDAY @@ -1041,8 +1032,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __n = static_cast(__x._M_wd) - __y._M_wd; return days{__detail::__modulo(__n, 7)}; } - - // TODO: operator<<, from_stream. }; inline constexpr weekday Sunday{0}; @@ -1099,8 +1088,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday operator/(const year_month& __ym, const weekday_indexed& __wdi) noexcept; - - // TODO: Implement operator<<. }; constexpr weekday_indexed @@ -1140,8 +1127,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday_last operator/(const year_month& __ym, const weekday_last& __wdl) noexcept; - - // TODO: Implement operator<<. }; constexpr weekday_last @@ -1213,8 +1198,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day operator/(const month_day& __md, int __y) noexcept; - - // TODO: Implement operator<<, from_stream. }; // MONTH_DAY_LAST @@ -1267,8 +1250,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day_last operator/(const month_day_last& __mdl, int __y) noexcept; - - // TODO: Implement operator<<. }; // MONTH_WEEKDAY @@ -1328,8 +1309,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday operator/(const month_weekday& __mwd, int __y) noexcept; - - // TODO: Implement operator<<. }; // MONTH_WEEKDAY_LAST @@ -1390,8 +1369,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday_last operator/(const month_weekday_last& __mwdl, int __y) noexcept; - - // TODO: Implement operator<<. }; // YEAR_MONTH @@ -1533,8 +1510,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day_last operator/(const year_month& __ym, last_spec) noexcept; - - // TODO: Implement operator<<, from_stream. }; // YEAR_MONTH_DAY @@ -1686,8 +1661,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day operator/(const month_day& __md, int __y) noexcept { return chrono::year(__y) / __md; } - - // TODO: Implement operator<<, from_stream. }; // Construct from days since 1970/01/01. @@ -1917,8 +1890,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_day_last operator/(const chrono::month_day_last& __mdl, int __y) noexcept { return chrono::year(__y) / __mdl; } - - // TODO: Implement operator<<. }; // year_month_day ctor from year_month_day_last @@ -2107,8 +2078,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday operator/(const month_weekday& __mwd, int __y) noexcept { return chrono::year(__y) / __mwd; } - - // TODO: Implement operator<<. }; // YEAR_MONTH_WEEKDAY_LAST @@ -2256,8 +2225,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend constexpr year_month_weekday_last operator/(const chrono::month_weekday_last& __mwdl, int __y) noexcept { return chrono::year(__y) / __mwdl; } - - // TODO: Implement operator<<. }; // HH_MM_SS @@ -2364,8 +2331,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return _M_h + _M_m + _M_s + _M_ss; } - // TODO: Implement operator<<. - private: bool _M_is_neg; chrono::hours _M_h; @@ -2415,6 +2380,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // C++20 [time.zones] Time zones + class tzdb; + struct sys_info { sys_seconds begin; @@ -2473,6 +2440,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION time_zone(time_zone&&) = default; time_zone& operator=(time_zone&&) = default; + ~time_zone(); + string_view name() const noexcept { return _M_name; } template @@ -2504,8 +2473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __x.name() <=> __y.name(); } private: - string _M_name; + friend const tzdb& reload_tzdb(); + struct _Impl; + explicit time_zone(unique_ptr<_Impl> __p); + string _M_name; unique_ptr<_Impl> _M_impl; }; @@ -2532,6 +2504,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: friend const tzdb& reload_tzdb(); + + explicit time_zone_link(nullptr_t) { } + // TODO unspecified additional constructors string _M_name; string _M_target; @@ -2632,9 +2607,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION explicit leap_second(seconds::rep __s) : _M_s(__s) { } friend const tzdb& reload_tzdb(); - template + + template friend leap_second_info - get_leap_second_info(const utc_time<_Dur>&); + get_leap_second_info(const utc_time<_Duration>&); + + static leap_second_info _S_get_info(const utc_seconds&); seconds _M_s; // == date().time_since_epoch() * value().count() }; @@ -2705,11 +2683,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: explicit const_iterator(const shared_ptr<_Node>&) noexcept; + friend class tzdb_list; + shared_ptr<_Node> _M_node; void* _M_reserved = nullptr; }; - // TODO const tzdb& front() const noexcept; + const tzdb& front() const noexcept; const_iterator erase_after(const_iterator); @@ -2718,9 +2698,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const_iterator cbegin() const noexcept { return begin(); } const_iterator cend() const noexcept { return end(); } +#ifndef _GLIBCXX_DEFINING_TZDB private: +#endif constexpr explicit tzdb_list(nullptr_t); + static string _S_latest_version(); + shared_ptr<_Node> _S_load(string_view); + friend const tzdb_list& get_tzdb_list(); friend const tzdb& get_tzdb(); friend const tzdb& reload_tzdb(); @@ -2729,12 +2714,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static shared_ptr<_Node> _S_head_owner; }; - // TODO - // const tzdb_list& get_tzdb_list(); - // const tzdb& get_tzdb(); + const tzdb_list& get_tzdb_list(); + const tzdb& get_tzdb(); - // const tzdb& reload_tzdb(); - // string remove_version(); + const tzdb& reload_tzdb(); + string remote_version(); template class zoned_time; // TODO @@ -2745,52 +2729,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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 - }; + if (__ut < utc_time<_Duration>{}) [[unlikely]] + return {}; - 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()} - }; - } + utc_seconds __us; + if constexpr (is_same_v<_Duration, seconds>) + __us = __ut; else - { - auto __s = chrono::time_point_cast(__ut); - return chrono::get_leap_second_info(__s); - } + __us = chrono::time_point_cast(__ut); + return leap_second::_S_get_info(__us); } /// @} group chrono @@ -2821,106 +2768,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @} } // inline namespace chrono_literals } // inline namespace literals - - namespace chrono - { - /// @addtogroup chrono - /// @{ - - /// @cond undocumented - namespace __detail - { - template - const char* - __units_suffix_misc(char* __buf, size_t __n) noexcept - { - namespace __tc = std::__detail; - char* __p = __buf; - __p[0] = '['; - unsigned __nlen = __tc::__to_chars_len((uintmax_t)_Period::num); - __tc::__to_chars_10_impl(__p + 1, __nlen, (uintmax_t)_Period::num); - __p += 1 + __nlen; - if constexpr (_Period::den != 1) - { - __p[0] = '/'; - unsigned __dlen = __tc::__to_chars_len((uintmax_t)_Period::den); - __tc::__to_chars_10_impl(__p + 1, __dlen, (uintmax_t)_Period::den); - __p += 1 + __dlen; - } - __p[0] = ']'; - __p[1] = 's'; - __p[2] = '\0'; - return __buf; - } - - template - auto - __units_suffix(char* __buf, size_t __n) noexcept - { -#define _GLIBCXX_UNITS_SUFFIX(period, suffix) \ - if constexpr (is_same_v<_Period, period>) \ - { \ - if constexpr (is_same_v<_CharT, wchar_t>) \ - return L##suffix; \ - else \ - return suffix; \ - } \ - else - - _GLIBCXX_UNITS_SUFFIX(atto, "as") - _GLIBCXX_UNITS_SUFFIX(femto, "fs") - _GLIBCXX_UNITS_SUFFIX(pico, "ps") - _GLIBCXX_UNITS_SUFFIX(nano, "ns") - _GLIBCXX_UNITS_SUFFIX(micro, "\u00b5s") - _GLIBCXX_UNITS_SUFFIX(milli, "ms") - _GLIBCXX_UNITS_SUFFIX(centi, "cs") - _GLIBCXX_UNITS_SUFFIX(deci, "ds") - _GLIBCXX_UNITS_SUFFIX(ratio<1>, "s") - _GLIBCXX_UNITS_SUFFIX(deca, "das") - _GLIBCXX_UNITS_SUFFIX(hecto, "hs") - _GLIBCXX_UNITS_SUFFIX(kilo, "ks") - _GLIBCXX_UNITS_SUFFIX(mega, "Ms") - _GLIBCXX_UNITS_SUFFIX(giga, "Gs") - _GLIBCXX_UNITS_SUFFIX(tera, "Ts") - _GLIBCXX_UNITS_SUFFIX(tera, "Ts") - _GLIBCXX_UNITS_SUFFIX(peta, "Ps") - _GLIBCXX_UNITS_SUFFIX(exa, "Es") - _GLIBCXX_UNITS_SUFFIX(ratio<60>, "min") - _GLIBCXX_UNITS_SUFFIX(ratio<3600>, "h") - _GLIBCXX_UNITS_SUFFIX(ratio<86400>, "d") -#undef _GLIBCXX_UNITS_SUFFIX - return __detail::__units_suffix_misc<_Period>(__buf, __n); - } - } // namespace __detail - /// @endcond - - template - inline basic_ostream<_CharT, _Traits>& - operator<<(std::basic_ostream<_CharT, _Traits>& __os, - const duration<_Rep, _Period>& __d) - { - using period = typename _Period::type; - char __buf[sizeof("[/]s") + 2 * numeric_limits::digits10]; - std::basic_ostringstream<_CharT, _Traits> __s; - __s.flags(__os.flags()); - __s.imbue(__os.getloc()); - __s.precision(__os.precision()); - __s << __d.count(); - __s << __detail::__units_suffix(__buf, sizeof(__buf)); - __os << std::move(__s).str(); - return __os; - } - - // TODO: from_stream for duration - - /// @} group chrono - } // namespace chrono #endif // C++20 _GLIBCXX_END_NAMESPACE_VERSION } // namespace std +#if __cplusplus >= 202002L +# include +#endif + #endif // C++11 #endif //_GLIBCXX_CHRONO diff --git a/libstdc++-v3/src/c++20/Makefile.am b/libstdc++-v3/src/c++20/Makefile.am index d4c1e26e40e..a95b8c24d21 100644 --- a/libstdc++-v3/src/c++20/Makefile.am +++ b/libstdc++-v3/src/c++20/Makefile.am @@ -36,7 +36,7 @@ else inst_sources = endif -sources = +sources = tzdb.cc vpath % $(top_srcdir)/src/c++20 diff --git a/libstdc++-v3/src/c++20/Makefile.in b/libstdc++-v3/src/c++20/Makefile.in index 9db70a3e7fb..2adc1eb712e 100644 --- a/libstdc++-v3/src/c++20/Makefile.in +++ b/libstdc++-v3/src/c++20/Makefile.in @@ -121,7 +121,7 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libc__20convenience_la_LIBADD = -am__objects_1 = +am__objects_1 = tzdb.lo @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo am_libc__20convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) libc__20convenience_la_OBJECTS = $(am_libc__20convenience_la_OBJECTS) @@ -431,7 +431,7 @@ headers = @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \ @ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc -sources = +sources = tzdb.cc libc__20convenience_la_SOURCES = $(sources) $(inst_sources) # AM_CXXFLAGS needs to be in each subdirectory so that it can be diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc new file mode 100644 index 00000000000..1aae398f871 --- /dev/null +++ b/libstdc++-v3/src/c++20/tzdb.cc @@ -0,0 +1,512 @@ +#define _GLIBCXX_DEFINING_TZDB 1 +#include +#include // ifstream +#include // istringstream +#include // ranges::upper_bound, ranges::lower_bound +#include // atomic_ref +#include // atomic> +#include // mutex +#include + +namespace std::chrono +{ + constexpr tzdb_list::tzdb_list(nullptr_t) { } + + namespace + { + constinit mutex list_mutex; + constinit tzdb_list the_list(nullptr); + } + + struct tzdb_list::_Node + { + tzdb db; + shared_ptr<_Node> _M_next; + + // static shared_ptr<_Node> _S_load(); + }; + + struct tzdb::_Rule + { + string name; // the name of the rule set that contains this line + year from; // first year in which the rule applies + year to; // final year in which the rule applies + month_day on; // the day on which the rule takes effect + hh_mm_ss at; // the time of day at which the rule takes effect + minutes save; // the amount of time to be added when the rule is in effect + string letters; // variable part of TZ abbreviations when rule in effect + }; + + // This is the owning reference to the first tzdb in the list. +#if USE_ATOMIC_SHARED_PTR + constinit atomic> tzdb_list::_S_head_owner{}; +#else + // list_mutex must be locked to access this. + constinit shared_ptr tzdb_list::_S_head_owner = nullptr; +#endif + // Lock-free access to the head of the list. + // This must be accessed atomically, using atomic_ref<_Node*>. + constinit tzdb_list::_Node* tzdb_list::_S_head = nullptr; + + // There are only three ways for users to access the tzdb list. + // get_tzdb_list() returns a reference to the list itself. + // get_tzdb() returns a reference to the front of the list. + // reload_tzdb() returns a reference to the (possibly new) front of the list. + // Those are the only functions that need to check whether the list has + // been populated already. + + const tzdb_list& + get_tzdb_list() + { + atomic_ref head(tzdb_list::_S_head); + if (head == nullptr) [[unlikely]] + reload_tzdb(); // populates list + return the_list; + } + + // This is + const tzdb& + get_tzdb() + { + atomic_ref head(tzdb_list::_S_head); + if (head == nullptr) [[unlikely]] + return reload_tzdb(); // populates list + return head.load()->db; + } + + namespace + { + string + remote_version(istream& zif) + { + char hash; + string label; + string version; + if (zif >> hash >> label >> version) + if (hash == '#' && label == "version" && version.size() == 5) + return version; + __throw_runtime_error("tzdb: no version found in tzdata.zi"); + } + + struct zi_rule + { + }; + } + + struct time_zone::_Impl + { + string name; + }; + + time_zone::time_zone(unique_ptr<_Impl> __p) : _M_impl(std::move(__p)) { } + + time_zone::~time_zone() = default; + + string remote_version() + { + ifstream zif("/usr/share/zoneinfo/tzdata.zi"); + return remote_version(zif); + } + + const tzdb& + reload_tzdb() + { + ifstream zif("/usr/share/zoneinfo/tzdata.zi"); + const string version = remote_version(zif); + +#if USE_ATOMIC_SHARED_PTR + auto head = tzdb_list::_S_head_owner.load(); + if (head != nullptr && head->db.version == version) + return head->db; +#else + atomic_ref head(tzdb_list::_S_head); + if (head != nullptr) [[likely]] + { + lock_guard l(list_mutex); + const tzdb& current = tzdb_list::_S_head_owner->db; + if (current.version == version) + return current; + } +#endif + + vector 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 + }; + ifstream ls("/usr/share/zoneinfo/leapseconds"); + if (ls) + { + std::string s; + while (std::getline(ls, s)) + { + if (!s.starts_with("Leap")) + continue; + istringstream li(std::move(s)); // FIXME: use ispanstream + li.exceptions(ios::failbit); + unsigned yval; + char m; + if (li >> s >> yval >> m) + { + if (m != 'J' && m != 'D') + __throw_runtime_error("tzdb: cannot parse leapseconds file"); + if (yval < 2020) + continue; + const int is_december = m == 'D'; + year_month_day ymd{year(yval), + month(6 + 6 * is_december), + day(30 + is_december)}; + sys_seconds secs(sys_days(ymd) + days(1)); + li >> s >> s >> m; + + if (m != '+' && m != '-') throw 1; // XXX + + seconds::rep val = secs.time_since_epoch().count(); + if (m == '-') [[unlikely]] + val = -val; + leaps.push_back(leap_second(val)); + } + } + } + + // See https://man7.org/linux/man-pages/man8/zic.8.html#FILES + // for documentation of the tzdata.zi file. + +#if USE_ATOMIC_SHARED_PTR + auto node = std::make_shared(); +#else + auto node = std::make_unique(); +#endif + + string line; + line.reserve(511); + string type; + istringstream is; // FIXME: use ispanstream + is.exceptions(ios::failbit); + while (std::getline(zif, line)) + { + if (line.empty() || line.front() == '#') + continue; + is.clear(); + is.str(line); + is.ignore(1); + switch (line.front()) + { + case 'R': // Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S + { + tzdb::_Rule rule; + int from, to; + char type; + string str; + is >> std::quoted(rule.name) >> from >> to >> type >> str; + if (type != '-') + is.setstate(ios::failbit); + rule.from = year(from); + rule.to = year(to); + month m; + day d; + switch (str[0]) + { + case 'J': + switch (str[1]) + { + case 'a': + m = January; + break; + case 'u': + switch (str[2]) + { + case 'n': + m = June; + break; + case 'l': + m = July; + break; + default: + is.setstate(ios::failbit); + } + break; + default: + is.setstate(ios::failbit); + } + break; + case 'F': + m = February; + break; + case 'M': + if (str[1] != 'a') + is.setstate(ios::failbit); + switch (str[2]) + { + case 'r': + m = March; + break; + case 'y': + m = May; + break; + default: + is.setstate(ios::failbit); + } + break; + case 'A': + switch (str[1]) + { + case 'p': + m = April; + break; + case 'u': + m = August; + break; + default: + is.setstate(ios::failbit); + } + break; + case 'S': + m = September; + break; + case 'O': + m = October; + break; + case 'N': + m = November; + break; + case 'D': + m = December; + break; + default: + is.setstate(ios::failbit); + } + is >> str; + // TODO parse ON into d + rule.on = month_day{m, d}; + is >> str; + // TODO parse AT into rule.at + is >> str; + // TODO parse SAVE into rule.save + is >> rule.letters; + node->db._M_rules.push_back(std::move(rule)); + break; + } + case 'L': // Link TARGET LINK-NAME + { + time_zone_link link(nullptr); + is >> std::quoted(link._M_target) >> std::quoted(link._M_name); + node->db.links.push_back(std::move(link)); + break; + } + case 'Z': // Zone NAME STDOFF RULES FORMAT [UNTIL] + { + auto tz = std::make_unique(); + is >> std::quoted(tz->name); + node->db.zones.push_back(time_zone(std::move(tz))); + [[fallthrough]]; // Use default case to parse rest of line ... + } + default: // STDOFF RULES FORMAT [UNTIL] + { + // This is a continuation of the previous Zone line. + time_zone& tz = node->db.zones.back(); + // TODO + } + } + + + } + // TODO: parse file + + // TODO sort vectors + // + // sort rules first, then link each time_zone to rules, + // by replacing the const char* pointing to the rule name + // with const _Rule* pointing to the rule itself. + // Implies time_zone impl needs union { const char* name; const _Rule* r; } + +#if USE_ATOMIC_SHARED_PTR + while (!_M_head_owner.compare_exchange_strong(head, node)) + if (head->db.version == version) + return *head; + // XXX small window here where _S_head still points to previous tzdb. + _S_head = node.get(); + return *node; +#else + lock_guard l(list_mutex); + if (const tzdb_list::_Node* h = head) + { + if (h->db.version == version) + return h->db; + } + auto* pnode = node.get(); + tzdb_list::_S_head_owner = std::move(node); + head = pnode; + return pnode->db; +#endif + } + + // Any call to tzdb_list::front() or tzdb_list::begin() must follow a call + // to get_tzdb_list() so the list has already been populated. + + const tzdb& + tzdb_list::front() const noexcept + { + atomic_ref<_Node*> head(_S_head); + return head.load()->db; + } + + auto + tzdb_list::begin() const noexcept + -> const_iterator + { +#if USE_ATOMIC_SHARED_PTR + return const_iterator{_S_head_owner.load()}; +#else + lock_guard l(list_mutex); + return const_iterator{_S_head_owner}; +#endif + } + + auto + tzdb_list::erase_after(const_iterator p) + -> const_iterator + { + if (p._M_node) [[likely]] + if (auto next = p._M_node->_M_next) [[likely]] + return const_iterator{p._M_node->_M_next = std::move(next->_M_next)}; + + // This is undefined, but let's be kind: + std::__throw_logic_error("std::tzdb_list::erase_after: iterator is not " + "dereferenceable"); + } + + auto + tzdb_list::const_iterator::operator*() const + -> reference + { + return _M_node->db; + } + + auto + tzdb_list::const_iterator::operator++() + -> const_iterator& + { + auto cur = std::move(_M_node); + _M_node = cur->_M_next; + return *this; + } + + auto + tzdb_list::const_iterator::operator++(int) + -> const_iterator + { + auto tmp = std::move(*this); + _M_node = tmp._M_node->_M_next; + return tmp; + } + + leap_second_info + leap_second::_S_get_info(const utc_time& ut) + { + auto begin = the_list.begin(); + const auto& leaps = begin->leap_seconds; + leap_second 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()} + }; + } + + namespace + { + const time_zone* + do_locate_zone(const vector& zones, + const vector& links, + string_view tz_name) noexcept + { + auto get_name = [](const auto& x) { return x.name(); }; + + auto search = [get_name](const auto& v, string_view name) { + auto p = ranges::lower_bound(v, name, {}, get_name); + if (p != v.end() && p->name() == name) + return p.base(); + return static_cast(nullptr); + }; + + if (auto tz = search(zones, tz_name)) + return tz; + + if (auto tz_l = search(links, tz_name)) + return search(zones, tz_l->target()); + + return nullptr; + } + } // namespace + + const time_zone* + tzdb::locate_zone(string_view tz_name) const + { + if (auto tz = do_locate_zone(zones, links, tz_name)) + return tz; + string_view err = "tzdb: cannot locate zone: "; + string str; + str.reserve(err.size() + tz_name.size()); + str += err; + str += tz_name; + __throw_runtime_error(str.c_str()); + } + + const time_zone* + tzdb::current_zone() const + { + error_code ec; + auto path = filesystem::read_symlink("/etc/localtime", ec); + if (!ec) + { + auto first = path.begin(), last = path.end(); + if (std::distance(first, last) > 2) + { + --last; + string name = std::prev(last)->string() + '/' + last->string(); + if (auto tz = do_locate_zone(zones, links, name)) + return tz; + } + } + __throw_runtime_error("tzdb: cannot determine current zone"); + } + + const time_zone* + locate_zone(string_view tz_name) + { + return get_tzdb_list().begin()->locate_zone(tz_name); + } + + const time_zone* + current_zone() + { + return get_tzdb_list().begin()->current_zone(); + } + +} // namespace std diff --git a/libstdc++-v3/testsuite/std/time/clock/gps/io.cc b/libstdc++-v3/testsuite/std/time/clock/gps/io.cc new file mode 100644 index 00000000000..29f3148cf14 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/gps/io.cc @@ -0,0 +1,24 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using std::format; + using namespace std::chrono; + + auto st = sys_days{2000y/January/1}; + auto gt = clock_cast(st); + + auto s = format("{0:%F %T %Z} == {1:%F %T %Z}", st, gt); + VERIFY( s == "2000-01-01 00:00:00 UTC == 2000-01-01 00:00:13 GPS" ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/std/time/clock/system/io.cc b/libstdc++-v3/testsuite/std/time/clock/system/io.cc new file mode 100644 index 00000000000..07ba8c84f40 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/system/io.cc @@ -0,0 +1,37 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using namespace std::chrono; + std::stringstream ss; + ss << sys_seconds{0s} << '\n'; // 1970-01-01 00:00:00 + ss << sys_seconds{946'684'800s} << '\n'; // 2000-01-01 00:00:00 + ss << sys_seconds{946'688'523s} << '\n'; // 2000-01-01 01:02:03 + std::string s1, s2, s3; + std::getline(ss, s1); + std::getline(ss, s2); + std::getline(ss, s3); + VERIFY( s1 == "1970-01-01 00:00:00" ); + VERIFY( s2 == "2000-01-01 00:00:00" ); + VERIFY( s3 == "2000-01-01 01:02:03" ); +} + +template +concept stream_insertable + = requires (std::ostream& out, const T& t) { out << t; }; + +using fp_sys_time = std::chrono::sys_time>; +static_assert( !stream_insertable ); + +static_assert( !stream_insertable ); + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/std/time/clock/tai/io.cc b/libstdc++-v3/testsuite/std/time/clock/tai/io.cc new file mode 100644 index 00000000000..d0255f5431a --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/tai/io.cc @@ -0,0 +1,24 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using std::format; + using namespace std::chrono; + + auto st = sys_days{2000y/January/1}; + auto tt = clock_cast(st); + + auto s = format("{0:%F %T %Z} == {1:%F %T %Z}", st, tt); + VERIFY( s == "2000-01-01 00:00:00 UTC == 2000-01-01 00:00:32 TAI" ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc new file mode 100644 index 00000000000..75e89fc62bb --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc @@ -0,0 +1,40 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using std::ostringstream; + using namespace std::chrono; + + auto t = sys_days{July/1/2015} - 500ms; + auto u = clock_cast(t); + + std::string_view results[] = { + "2015-06-30 23:59:59.500 UTC", + "2015-06-30 23:59:59.750 UTC", + "2015-06-30 23:59:60.000 UTC", + "2015-06-30 23:59:60.250 UTC", + "2015-06-30 23:59:60.500 UTC", + "2015-06-30 23:59:60.750 UTC", + "2015-07-01 00:00:00.000 UTC", + "2015-07-01 00:00:00.250 UTC", + }; + + for (auto result : results) + { + ostringstream out; + out << u << " UTC"; + VERIFY( out.str() == result ); + u += 250ms; + } +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/std/time/hh_mm_ss/io.cc b/libstdc++-v3/testsuite/std/time/hh_mm_ss/io.cc new file mode 100644 index 00000000000..3b50f40c1f6 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/hh_mm_ss/io.cc @@ -0,0 +1,46 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using std::ostringstream; + using std::chrono::hh_mm_ss; + using namespace std::chrono_literals; + + std::locale::global(std::locale::classic()); + + { + hh_mm_ss hms{-4083007ms}; + ostringstream out; + out << hms; + VERIFY( out.str() == "-01:08:03.007" ); + } + + { + hh_mm_ss hms{4083007ms}; + ostringstream out; + out << hms; + VERIFY( out.str() == "01:08:03.007" ); + } + + { + hh_mm_ss hms{65745123ms}; + ostringstream out; + out << hms; + VERIFY( out.str() == "18:15:45.123" ); + } + + ostringstream out; + out << hh_mm_ss{65745s}; + VERIFY( out.str() == "18:15:45" ); +} + +int main() +{ + test01(); +}