On Sun, 13 Nov 2022 at 01:17, Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested x86_64-linux and powerpc64le-linux. Pushed to trunk.
>
> -- >8 --
>
> Also add the basic types for timezones, without the non-inline
> definitions needed to actually use them.
This is the patch for the rest of the time zone support.
Not pushed yet.
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<typename _Tp>
+ inline constexpr bool __is_duration_v = false;
+ template<typename _Rep, typename _Period>
+ inline constexpr bool __is_duration_v<duration<_Rep, _Period>> = true;
+ template<typename _Tp>
+ inline constexpr bool __is_time_point_v = false;
+ template<typename _Clock, typename _Dur>
+ inline constexpr bool __is_time_point_v<time_point<_Clock, _Dur>> = 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 @@
+// <format> 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
+// <http://www.gnu.org/licenses/>.
+
+/** @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 <sstream> // ostringstream
+#include <iomanip> // setw, setfill
+#include <format>
+
+#include <bits/charconv.h>
+
+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<typename _CharT>
+ 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<C>(S, L##S)
+#define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S)
+
+
+ // Write an arbitrary duration suffix into the buffer.
+ template<typename _Period>
+ 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<typename _Period, typename _CharT>
+ 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<typename _CharT, typename _Traits,
+ typename _Rep, typename _Period>
+ 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<intmax_t>::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<period, _CharT>(__buf, sizeof(__buf));
+ __os << std::move(__s).str();
+ return __os;
+ }
+
+/// @cond undocumented
+namespace __detail
+{
+ // An unspecified type returned by `chrono::local_time_format`.
+ template<typename _Duration>
+ 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<typename _Duration>
+ 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<typename _CharT>
+ 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<typename _CharT>
+ struct __formatter_chrono
+ {
+ template<typename _ParseContext>
+ 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 _Tp, typename _FormatContext>
+ 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<typename _Tp>
+ 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<chrono::days>(__t);
+ else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ return __t._M_time - chrono::floor<chrono::days>(__t._M_time);
+ }
+
+ template<typename _Tp>
+ 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<chrono::days>(__t);
+ const auto __ymd = chrono::year_month_day{__days};
+ const auto __hms = chrono::hh_mm_ss{__t - __days};
+ return pair(__ymd, __hms);
+ }
+
+
+ template<typename _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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<chrono::hours>(__x);
+ if (__mod)
+ {
+ // TODO: locale's alternative representation
+ }
+ else
+ {
+ _CharT __buf[2];
+ }
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ 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<chrono::days>(__t).count();
+ else
+ {
+ }
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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<chrono::seconds>(__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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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 _Tp, typename _FormatContext>
+ 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<chrono::hours>(__s);
+ auto __minutes = chrono::duration_cast<chrono::minutes>(__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 _Tp, typename _FormatContext>
+ 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<typename _Rep, typename _Period, typename _CharT>
+ struct formatter<chrono::duration<_Rep, _Period>, _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 _Out>
+ 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<typename _CharT>
+ struct formatter<chrono::day, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::month, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year, _CharT>;
+
+ template<typename _CharT>
+ struct formatter<chrono::weekday, _CharT>
+ {
+
+
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+
+ // TODO define these
+ template<typename _CharT>
+ struct formatter<chrono::weekday_indexed, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::weekday_last, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::month_day, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::month_day_last, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::month_weekday, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::month_weekday_last, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year_month, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year_month_day, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year_month_day_last, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year_month_weekday, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::year_month_weekday_last, _CharT>;
+ template<typename _Rep, typename _Period, typename _CharT>
+ struct formatter<chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::sys_info, _CharT>;
+ template<typename _CharT>
+ struct formatter<chrono::local_info, _CharT>;
+
+ template<typename _Duration, typename _CharT>
+ struct formatter<chrono::sys_time<_Duration>, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _CharT>
+ struct formatter<chrono::utc_time<_Duration>, _CharT>
+ : __format::__formatter_chrono<_CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _CharT>
+ struct formatter<chrono::tai_time<_Duration>, _CharT>
+ : __format::__formatter_chrono<_CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _CharT>
+ struct formatter<chrono::gps_time<_Duration>, _CharT>
+ : __format::__formatter_chrono<_CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _CharT>
+ struct formatter<chrono::file_time<_Duration>, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::file_time<_Duration>& __t,
+ _FormatContext& __ctx) const
+ {
+ using namespace chrono;
+ return _M_f._M_format(clock_cast<system_clock>(__t), __ctx);
+ }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _Duration, typename _CharT>
+ struct formatter<chrono::local_time<_Duration>, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_Date|__format::_TimeOfDay);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _CharT>
+ struct formatter<chrono::__detail::__local_time_fmt<_Duration>, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ {
+ return _M_f._M_parse(__pc, __format::_DateTime);
+ }
+
+ template<typename _FormatContext>
+ 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<typename _Duration, typename _TimeZonePtr, typename _CharT>
+ struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT>
+ : formatter<chrono::__detail::__local_time_fmt<_Duration>, _CharT>
+ {
+ template<typename _FormatContext>
+ 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<typename _CharT, typename _Traits, typename _Rep, typename _Period,
+ typename _Alloc = allocator<_CharT>>
+ 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<typename _CharT, typename _Traits>
+ inline basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d)
+ {
+ using _Ctx = __conditional_t<is_same_v<_CharT, char>,
+ 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<typename _CharT, typename _Traits>
+ inline basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const month& __m)
+ {
+ using _Ctx = __conditional_t<is_same_v<_CharT, char>,
+ 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<typename _CharT, typename _Traits>
+ inline basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y)
+ {
+ using _Ctx = __conditional_t<is_same_v<_CharT, char>,
+ 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<typename _CharT, typename _Traits>
+ inline basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday& __wd)
+ {
+ using _Ctx = __conditional_t<is_same_v<_CharT, char>,
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ inline basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os,
+ const year_month_day& __ymd)
+ {
+ using _Ctx = __conditional_t<is_same_v<_CharT, char>,
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits>
+ 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<typename _CharT, typename _Traits, typename _Duration,
+ typename _TimeZonePtr>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ requires (!treat_as_floating_point_v<typename _Duration::rep>)
+ && ratio_less_v<typename _Duration::period, days::period>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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<typename _CharT, typename _Traits, typename _Duration>
+ 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 <bits/chrono.h>
#if __cplusplus >= 202002L
-# include <sstream>
# 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/shared_ptr.h>
# include <bits/unique_ptr.h>
#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<long long>(__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<typename _Duration>
@@ -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<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>&);
+
+ 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<typename _Duration, typename _TimeZonePtr = const time_zone*>
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<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
- };
+ 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<seconds>(__ut);
- return chrono::get_leap_second_info(__s);
- }
+ __us = chrono::time_point_cast<seconds>(__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<typename _Period>
- 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<typename _Period, typename _CharT>
- 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<typename _CharT, typename _Traits,
- typename _Rep, typename _Period>
- 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<intmax_t>::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<period, _CharT>(__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 <bits/chrono_io.h>
+#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 <chrono>
+#include <fstream> // ifstream
+#include <sstream> // istringstream
+#include <algorithm> // ranges::upper_bound, ranges::lower_bound
+#include <atomic> // atomic_ref
+#include <memory> // atomic<shared_ptr<T>>
+#include <mutex> // mutex
+#include <filesystem>
+
+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<seconds> 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<shared_ptr<tzdb_list::_Node>> tzdb_list::_S_head_owner{};
+#else
+ // list_mutex must be locked to access this.
+ constinit shared_ptr<tzdb_list::_Node> 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<tzdb_list::_Node*> head(tzdb_list::_S_head);
+ if (head == nullptr) [[unlikely]]
+ reload_tzdb(); // populates list
+ return the_list;
+ }
+
+ // This is
+ const tzdb&
+ get_tzdb()
+ {
+ atomic_ref<tzdb_list::_Node*> 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<tzdb_list::_Node*> head(tzdb_list::_S_head);
+ if (head != nullptr) [[likely]]
+ {
+ lock_guard<mutex> l(list_mutex);
+ const tzdb& current = tzdb_list::_S_head_owner->db;
+ if (current.version == version)
+ return current;
+ }
+#endif
+
+ vector<leap_second> leaps
+ {
+ (leap_second) 78796800, // 1 Jul 1972
+ (leap_second) 94694400, // 1 Jan 1973
+ (leap_second) 126230400, // 1 Jan 1974
+ (leap_second) 157766400, // 1 Jan 1975
+ (leap_second) 189302400, // 1 Jan 1976
+ (leap_second) 220924800, // 1 Jan 1977
+ (leap_second) 252460800, // 1 Jan 1978
+ (leap_second) 283996800, // 1 Jan 1979
+ (leap_second) 315532800, // 1 Jan 1980
+ (leap_second) 362793600, // 1 Jul 1981
+ (leap_second) 394329600, // 1 Jul 1982
+ (leap_second) 425865600, // 1 Jul 1983
+ (leap_second) 489024000, // 1 Jul 1985
+ (leap_second) 567993600, // 1 Jan 1988
+ (leap_second) 631152000, // 1 Jan 1990
+ (leap_second) 662688000, // 1 Jan 1991
+ (leap_second) 709948800, // 1 Jul 1992
+ (leap_second) 741484800, // 1 Jul 1993
+ (leap_second) 773020800, // 1 Jul 1994
+ (leap_second) 820454400, // 1 Jan 1996
+ (leap_second) 867715200, // 1 Jul 1997
+ (leap_second) 915148800, // 1 Jan 1999
+ (leap_second)1136073600, // 1 Jan 2006
+ (leap_second)1230768000, // 1 Jan 2009
+ (leap_second)1341100800, // 1 Jul 2012
+ (leap_second)1435708800, // 1 Jul 2015
+ (leap_second)1483228800, // 1 Jan 2017
+ };
+ 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<tzdb_list::_Node>();
+#else
+ auto node = std::make_unique<tzdb_list::_Node>();
+#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<time_zone::_Impl>();
+ 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<mutex> 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<mutex> 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<seconds>& 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<time_zone>& zones,
+ const vector<time_zone_link>& 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<decltype(p.base())>(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 <chrono>
+#include <format>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using std::format;
+ using namespace std::chrono;
+
+ auto st = sys_days{2000y/January/1};
+ auto gt = clock_cast<gps_clock>(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 <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+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<typename T>
+concept stream_insertable
+ = requires (std::ostream& out, const T& t) { out << t; };
+
+using fp_sys_time = std::chrono::sys_time<std::chrono::duration<float>>;
+static_assert( !stream_insertable<fp_sys_time> );
+
+static_assert( !stream_insertable<std::chrono::sys_days> );
+
+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 <chrono>
+#include <format>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using std::format;
+ using namespace std::chrono;
+
+ auto st = sys_days{2000y/January/1};
+ auto tt = clock_cast<tai_clock>(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 <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ auto t = sys_days{July/1/2015} - 500ms;
+ auto u = clock_cast<utc_clock>(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 <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+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();
+}
@@ -39,9 +39,15 @@
#else
#include <bits/chrono.h>
-#if __cplusplus > 201703L
-# include <sstream> // ostringstream
-# include <bits/charconv.h>
+
+#if __cplusplus >= 202002L
+# include <sstream>
+# 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/shared_ptr.h>
+# include <bits/unique_ptr.h>
#endif
namespace std _GLIBCXX_VISIBILITY(default)
@@ -102,6 +108,357 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
seconds elapsed;
};
+ template<typename _Duration>
+ leap_second_info
+ get_leap_second_info(const utc_time<_Duration>& __ut);
+
+ /** A clock that measures Universal Coordinated Time (UTC).
+ *
+ * The epoch is 1970-01-01 00:00:00.
+ *
+ * @since C++20
+ */
+ class utc_clock
+ {
+ public:
+ using rep = system_clock::rep;
+ using period = system_clock::period;
+ using duration = chrono::duration<rep, period>;
+ using time_point = chrono::time_point<utc_clock>;
+ static constexpr bool is_steady = false;
+
+ static time_point
+ now()
+ { return from_sys(system_clock::now()); }
+
+ template<typename _Duration>
+ static sys_time<common_type_t<_Duration, seconds>>
+ to_sys(const utc_time<_Duration>& __t)
+ {
+ using _CDur = common_type_t<_Duration, seconds>;
+ const auto __li = chrono::get_leap_second_info(__t);
+ sys_time<_CDur> __s{__t.time_since_epoch() - seconds{__li.elapsed}};
+ if (__li.is_leap_second)
+ __s = chrono::floor<seconds>(__s) + seconds{1} - _CDur{1};
+ return __s;
+ }
+
+ template<typename _Duration>
+ 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};
+ }
+ };
+
+ /** A clock that measures International Atomic Time.
+ *
+ * The epoch is 1958-01-01 00:00:00.
+ *
+ * @since C++20
+ */
+ class tai_clock
+ {
+ public:
+ using rep = system_clock::rep;
+ using period = system_clock::period;
+ using duration = chrono::duration<rep, period>;
+ using time_point = chrono::time_point<tai_clock>;
+ static constexpr bool is_steady = false; // XXX true for CLOCK_TAI?
+
+ // TODO move into lib, use CLOCK_TAI on linux, add extension point.
+ static time_point
+ now()
+ { return from_utc(utc_clock::now()); }
+
+ template<typename _Duration>
+ static utc_time<common_type_t<_Duration, seconds>>
+ to_utc(const tai_time<_Duration>& __t)
+ {
+ using _CDur = common_type_t<_Duration, seconds>;
+ return utc_time<_CDur>{__t.time_since_epoch()} - 378691210s;
+ }
+
+ template<typename _Duration>
+ static tai_time<common_type_t<_Duration, seconds>>
+ from_utc(const utc_time<_Duration>& __t)
+ {
+ using _CDur = common_type_t<_Duration, seconds>;
+ return tai_time<_CDur>{__t.time_since_epoch()} + 378691210s;
+ }
+ };
+
+ /** A clock that measures GPS time.
+ *
+ * The epoch is 1980-01-06 00:00:00.
+ *
+ * @since C++20
+ */
+ class gps_clock
+ {
+ public:
+ using rep = system_clock::rep;
+ using period = system_clock::period;
+ using duration = chrono::duration<rep, period>;
+ using time_point = chrono::time_point<gps_clock>;
+ static constexpr bool is_steady = false; // XXX
+
+ // TODO move into lib, add extension point.
+ static time_point
+ now()
+ { return from_utc(utc_clock::now()); }
+
+ template<typename _Duration>
+ static utc_time<common_type_t<_Duration, seconds>>
+ to_utc(const gps_time<_Duration>& __t)
+ {
+ using _CDur = common_type_t<_Duration, seconds>;
+ return utc_time<_CDur>{__t.time_since_epoch()} + 315964809s;
+ }
+
+ template<typename _Duration>
+ static gps_time<common_type_t<_Duration, seconds>>
+ from_utc(const utc_time<_Duration>& __t)
+ {
+ using _CDur = common_type_t<_Duration, seconds>;
+ return gps_time<_CDur>{__t.time_since_epoch()} - 315964809s;
+ }
+ };
+
+
+ template<typename _DestClock, typename _SourceClock>
+ struct clock_time_conversion
+ { };
+
+ // Identity conversions
+
+ template<typename _Clock>
+ struct clock_time_conversion<_Clock, _Clock>
+ {
+ template<typename _Duration>
+ time_point<_Clock, _Duration>
+ operator()(const time_point<_Clock, _Duration>& __t) const
+ { return __t; }
+ };
+
+ template<>
+ struct clock_time_conversion<system_clock, system_clock>
+ {
+ template<typename _Duration>
+ sys_time<_Duration>
+ operator()(const sys_time<_Duration>& __t) const
+ { return __t; }
+ };
+
+ template<>
+ struct clock_time_conversion<utc_clock, utc_clock>
+ {
+ template<typename _Duration>
+ utc_time<_Duration>
+ operator()(const utc_time<_Duration>& __t) const
+ { return __t; }
+ };
+
+ // Conversions between system_clock and utc_clock
+
+ template<>
+ struct clock_time_conversion<utc_clock, system_clock>
+ {
+ template<typename _Duration>
+ utc_time<common_type_t<_Duration, seconds>>
+ operator()(const sys_time<_Duration>& __t) const
+ { return utc_clock::from_sys(__t); }
+ };
+
+ template<>
+ struct clock_time_conversion<system_clock, utc_clock>
+ {
+ template<typename _Duration>
+ sys_time<common_type_t<_Duration, seconds>>
+ operator()(const utc_time<_Duration>& __t) const
+ { return utc_clock::to_sys(__t); }
+ };
+
+ template<typename _Tp, typename _Clock>
+ inline constexpr bool __is_time_point_for_v = false;
+
+ template<typename _Clock, typename _Duration>
+ inline constexpr bool
+ __is_time_point_for_v<time_point<_Clock, _Duration>, _Clock> = true;
+
+ // Conversions between system_clock and other clocks
+
+ template<typename _SourceClock>
+ struct clock_time_conversion<system_clock, _SourceClock>
+ {
+ template<typename _Duration, typename _Src = _SourceClock>
+ auto
+ operator()(const time_point<_SourceClock, _Duration>& __t) const
+ -> decltype(_Src::to_sys(__t))
+ {
+ using _Ret = decltype(_SourceClock::to_sys(__t));
+ static_assert(__is_time_point_for_v<_Ret, system_clock>);
+ return _SourceClock::to_sys(__t);
+ }
+ };
+
+ template<typename _DestClock>
+ struct clock_time_conversion<_DestClock, system_clock>
+ {
+ template<typename _Duration, typename _Dest = _DestClock>
+ auto
+ operator()(const sys_time<_Duration>& __t) const
+ -> decltype(_Dest::from_sys(__t))
+ {
+ using _Ret = decltype(_DestClock::from_sys(__t));
+ static_assert(__is_time_point_for_v<_Ret, _DestClock>);
+ return _DestClock::from_sys(__t);
+ }
+ };
+
+ // Conversions between utc_clock and other clocks
+
+ template<typename _SourceClock>
+ struct clock_time_conversion<utc_clock, _SourceClock>
+ {
+ template<typename _Duration, typename _Src = _SourceClock>
+ auto
+ operator()(const time_point<_SourceClock, _Duration>& __t) const
+ -> decltype(_Src::to_utc(__t))
+ {
+ using _Ret = decltype(_SourceClock::to_utc(__t));
+ static_assert(__is_time_point_for_v<_Ret, utc_clock>);
+ return _SourceClock::to_utc(__t);
+ }
+ };
+
+ template<typename _DestClock>
+ struct clock_time_conversion<_DestClock, utc_clock>
+ {
+ template<typename _Duration, typename _Dest = _DestClock>
+ auto
+ operator()(const utc_time<_Duration>& __t) const
+ -> decltype(_Dest::from_utc(__t))
+ {
+ using _Ret = decltype(_DestClock::from_utc(__t));
+ static_assert(__is_time_point_for_v<_Ret, _DestClock>);
+ return _DestClock::from_utc(__t);
+ }
+ };
+
+ /// @cond undocumented
+ namespace __detail
+ {
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ concept __clock_convs
+ = requires (const time_point<_SourceClock, _Duration>& __t) {
+ clock_time_conversion<_DestClock, _SourceClock>{}(__t);
+ };
+
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ concept __clock_convs_sys
+ = requires (const time_point<_SourceClock, _Duration>& __t) {
+ clock_time_conversion<_DestClock, system_clock>{}(
+ clock_time_conversion<system_clock, _SourceClock>{}(__t));
+ };
+
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ concept __clock_convs_utc
+ = requires (const time_point<_SourceClock, _Duration>& __t) {
+ clock_time_conversion<_DestClock, utc_clock>{}(
+ clock_time_conversion<utc_clock, _SourceClock>{}(__t));
+ };
+
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ concept __clock_convs_sys_utc
+ = requires (const time_point<_SourceClock, _Duration>& __t) {
+ clock_time_conversion<_DestClock, utc_clock>{}(
+ clock_time_conversion<utc_clock, system_clock>{}(
+ clock_time_conversion<system_clock, _SourceClock>{}(__t)));
+ };
+
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ concept __clock_convs_utc_sys
+ = requires (const time_point<_SourceClock, _Duration>& __t) {
+ clock_time_conversion<_DestClock, system_clock>{}(
+ clock_time_conversion<system_clock, utc_clock>{}(
+ clock_time_conversion<utc_clock, _SourceClock>{}(__t)));
+ };
+
+ } // namespace __detail
+ /// @endcond
+
+ /// Convert a time point to a different clock.
+ template<typename _DestClock, typename _SourceClock, typename _Duration>
+ inline auto
+ clock_cast(const time_point<_SourceClock, _Duration>& __t)
+ requires __detail::__clock_convs<_DestClock, _SourceClock, _Duration>
+ || __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration>
+ || __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration>
+ || __detail::__clock_convs_sys_utc<_DestClock, _SourceClock, _Duration>
+ || __detail::__clock_convs_utc_sys<_DestClock, _SourceClock, _Duration>
+ {
+ constexpr bool __direct
+ = __detail::__clock_convs<_DestClock, _SourceClock, _Duration>;
+ if constexpr (__direct)
+ {
+ return clock_time_conversion<_DestClock, _SourceClock>{}(__t);
+ }
+ else
+ {
+ constexpr bool __convert_via_sys_clock
+ = __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration>;
+ constexpr bool __convert_via_utc_clock
+ = __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration>;
+ if constexpr (__convert_via_sys_clock)
+ {
+ static_assert(!__convert_via_utc_clock,
+ "clock_cast requires a unique best conversion, but "
+ "conversion is possible via system_clock and also via"
+ "utc_clock");
+ return clock_time_conversion<_DestClock, system_clock>{}(
+ clock_time_conversion<system_clock, _SourceClock>{}(__t));
+ }
+ else if constexpr (__convert_via_utc_clock)
+ {
+ return clock_time_conversion<_DestClock, utc_clock>{}(
+ clock_time_conversion<utc_clock, _SourceClock>{}(__t));
+ }
+ else
+ {
+ constexpr bool __convert_via_sys_and_utc_clocks
+ = __detail::__clock_convs_sys_utc<_DestClock,
+ _SourceClock,
+ _Duration>;
+
+ if constexpr (__convert_via_sys_and_utc_clocks)
+ {
+ constexpr bool __convert_via_utc_and_sys_clocks
+ = __detail::__clock_convs_utc_sys<_DestClock,
+ _SourceClock,
+ _Duration>;
+ static_assert(!__convert_via_utc_and_sys_clocks,
+ "clock_cast requires a unique best conversion, but "
+ "conversion is possible via system_clock followed by "
+ "utc_clock, and also via utc_clock followed by "
+ "system_clock");
+ return clock_time_conversion<_DestClock, utc_clock>{}(
+ clock_time_conversion<utc_clock, system_clock>{}(
+ clock_time_conversion<system_clock, _SourceClock>{}(__t)));
+ }
+ else
+ {
+ return clock_time_conversion<_DestClock, system_clock>{}(
+ clock_time_conversion<system_clock, utc_clock>{}(
+ clock_time_conversion<utc_clock, _SourceClock>{}(__t)));
+ }
+ }
+ }
+ }
+
// CALENDRICAL TYPES
// CLASS DECLARATIONS
@@ -2055,6 +2412,387 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __h + 12h;
}
}
+
+ // C++20 [time.zones] Time zones
+
+ struct sys_info
+ {
+ sys_seconds begin;
+ sys_seconds end;
+ seconds offset;
+ minutes save;
+ string abbrev;
+ };
+
+ struct local_info
+ {
+ static constexpr int unique = 0;
+ static constexpr int nonexistent = 1;
+ static constexpr int ambiguous = 2;
+
+ int result;
+ sys_info first;
+ sys_info second;
+ };
+
+ class nonexistent_local_time : public runtime_error
+ {
+ public:
+ template<typename _Duration>
+ nonexistent_local_time(const local_time<_Duration>& __tp,
+ const local_info& __i)
+ : runtime_error(_S_make_what_str(__tp, __i))
+ { __glibcxx_assert(__i.result == local_info::nonexistent); }
+
+ private:
+ template<typename _Duration> // TODO
+ static string
+ _S_make_what_str(const local_time<_Duration>&, const local_info&);
+ };
+
+ class ambiguous_local_time : public runtime_error
+ {
+ public:
+ template<typename _Duration>
+ ambiguous_local_time(const local_time<_Duration>& __tp,
+ const local_info& __i)
+ : runtime_error(_S_make_what_str(__tp, __i))
+ { __glibcxx_assert(__i.result == local_info::nonexistent); }
+
+ private:
+ template<typename _Duration> // TODO
+ static string
+ _S_make_what_str(const local_time<_Duration>&, const local_info&);
+ };
+
+ enum class choose { earliest, latest };
+
+ class time_zone
+ {
+ public:
+ time_zone(time_zone&&) = default;
+ time_zone& operator=(time_zone&&) = default;
+
+ string_view name() const noexcept { return _M_name; }
+
+ template<typename _Duration>
+ sys_info
+ get_info(const sys_time<_Duration>& __st) const;
+
+ template<typename _Duration>
+ local_info
+ get_info(const local_time<_Duration>& __tp) const;
+
+ template<typename _Duration>
+ sys_time<common_type_t<_Duration, seconds>>
+ to_sys(const local_time<_Duration>& __tp) const;
+
+ template<typename _Duration>
+ sys_time<common_type_t<_Duration, seconds>>
+ to_sys(const local_time<_Duration>& __tp, choose __z) const;
+
+ template<typename _Duration>
+ local_time<common_type_t<_Duration, seconds>>
+ to_local(const sys_time<_Duration>& __tp) const;
+
+ friend bool
+ operator==(const time_zone& __x, const time_zone& __y) noexcept
+ { return __x.name() == __y.name(); }
+
+ friend strong_ordering
+ operator<=>(const time_zone& __x, const time_zone& __y) noexcept
+ { return __x.name() <=> __y.name(); }
+
+ private:
+ string _M_name;
+ struct _Impl;
+ unique_ptr<_Impl> _M_impl;
+ };
+
+ struct tzdb;
+ const time_zone* locate_zone(string_view __tz_name);
+ const time_zone* current_zone();
+
+ class time_zone_link
+ {
+ public:
+ time_zone_link(time_zone_link&&) = default;
+ time_zone_link& operator=(time_zone_link&&) = default;
+
+ string_view name() const noexcept { return _M_name; }
+ string_view target() const noexcept { return _M_target; }
+
+ friend bool
+ operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept
+ { return __x.name() == __y.name(); }
+
+ friend strong_ordering
+ operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept
+ { return __x.name() <=> __y.name(); }
+
+ private:
+ friend const tzdb& reload_tzdb();
+ // TODO unspecified additional constructors
+ string _M_name;
+ string _M_target;
+ };
+
+ class leap_second
+ {
+ public:
+ leap_second(const leap_second&) = default;
+ leap_second& operator=(const leap_second&) = default;
+
+ constexpr sys_seconds
+ date() const noexcept
+ {
+ if (_M_s >= _M_s.zero()) [[likely]]
+ return sys_seconds(_M_s);
+ return sys_seconds(-_M_s);
+ }
+
+ constexpr seconds
+ value() const noexcept
+ {
+ if (_M_s >= _M_s.zero()) [[likely]]
+ return seconds(1);
+ return seconds(-1);
+ }
+
+ // This can be defaulted because the database will never contain two
+ // leap_second objects with the same date but different signs.
+ friend constexpr bool
+ operator==(const leap_second&, const leap_second&) noexcept = default;
+
+ friend constexpr strong_ordering
+ operator<=>(const leap_second& __x, const leap_second& __y) noexcept
+ { return __x.date() <=> __y.date(); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator==(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return __x.date() == __y; }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator<(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return __x.date() < __y; }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator<(const sys_time<_Duration>& __x,
+ const leap_second& __y) noexcept
+ { return __x < __y.date(); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator>(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return __y < __x.date(); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator>(const sys_time<_Duration>& __x,
+ const leap_second& __y) noexcept
+ { return __y.date() < __x; }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator<=(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return !(__y < __x.date()); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator<=(const sys_time<_Duration>& __x,
+ const leap_second& __y) noexcept
+ { return !(__y.date() < __x); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator>=(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return !(__x.date() < __y); }
+
+ template<typename _Duration>
+ friend constexpr bool
+ operator>=(const sys_time<_Duration>& __x,
+ const leap_second& __y) noexcept
+ { return !(__x < __y.date()); }
+
+ template<three_way_comparable_with<seconds> _Duration>
+ friend constexpr auto
+ operator<=>(const leap_second& __x,
+ const sys_time<_Duration>& __y) noexcept
+ { return __x.date() <=> __y; }
+
+ private:
+ explicit leap_second(seconds::rep __s) : _M_s(__s) { }
+
+ friend const tzdb& reload_tzdb();
+ template<typename _Dur>
+ friend leap_second_info
+ get_leap_second_info(const utc_time<_Dur>&);
+
+ seconds _M_s; // == date().time_since_epoch() * value().count()
+ };
+
+ template<class _Tp> struct zoned_traits { };
+
+ template<>
+ struct zoned_traits<const time_zone*>
+ {
+ static const time_zone*
+ default_zone()
+ { return std::chrono::locate_zone("UTC"); }
+
+ static const time_zone*
+ locate_zone(string_view __name)
+ { return std::chrono::locate_zone(__name); }
+ };
+
+ struct tzdb
+ {
+ string version;
+ vector<time_zone> zones;
+ vector<time_zone_link> links;
+ vector<leap_second> leap_seconds;
+
+ const time_zone*
+ locate_zone(string_view __tz_name) const;
+
+ const time_zone*
+ current_zone() const;
+
+ private:
+ friend const tzdb& reload_tzdb();
+
+ struct _Rule;
+ vector<_Rule> _M_rules;
+ };
+
+ class tzdb_list
+ {
+ struct _Node;
+ public:
+ tzdb_list(const tzdb_list&) = delete;
+ tzdb_list& operator=(const tzdb_list&) = delete;
+
+ class const_iterator
+ {
+ public:
+ using value_type = tzdb;
+ using reference = const tzdb&;
+ using pointer = const tzdb*;
+ using difference_type = ptrdiff_t;
+ using iterator_category = forward_iterator_tag;
+
+ constexpr const_iterator() = default;
+ const_iterator(const const_iterator&) = default;
+ const_iterator(const_iterator&&) = default;
+ const_iterator& operator=(const const_iterator&) = default;
+ const_iterator& operator=(const_iterator&&) = default;
+
+ reference operator*() const noexcept;
+ pointer operator->() const noexcept { return &**this; }
+ const_iterator& operator++();
+ const_iterator operator++(int);
+
+ bool operator==(const const_iterator&) const noexcept = default;
+
+ private:
+ explicit const_iterator(const shared_ptr<_Node>&) noexcept;
+
+ shared_ptr<_Node> _M_node;
+ void* _M_reserved = nullptr;
+ };
+
+ // TODO const tzdb& front() const noexcept;
+
+ const_iterator erase_after(const_iterator);
+
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept { return {}; }
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator cend() const noexcept { return end(); }
+
+ private:
+ constexpr explicit tzdb_list(nullptr_t);
+
+ friend const tzdb_list& get_tzdb_list();
+ friend const tzdb& get_tzdb();
+ friend const tzdb& reload_tzdb();
+
+ static _Node* _S_head;
+ static shared_ptr<_Node> _S_head_owner;
+ };
+
+ // TODO
+ // const tzdb_list& get_tzdb_list();
+ // const tzdb& get_tzdb();
+
+ // const tzdb& reload_tzdb();
+ // string remove_version();
+
+ template<typename _Duration, typename _TimeZonePtr = const time_zone*>
+ class zoned_time; // TODO
+
+ using zoned_seconds = zoned_time<seconds>;
+
+ template<typename _Duration>
+ 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<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
+ };
+
+ auto __s = __ut.time_since_epoch().count();
+ auto __pos = std::upper_bound(__leaps.begin(), __leaps.end(), __s);
+ return {
+ __pos != __leaps.begin() && __pos[-1] == __s,
+ seconds{__pos - __leaps.begin()}
+ };
+ }
+ else
+ {
+ auto __s = chrono::time_point_cast<seconds>(__ut);
+ return chrono::get_leap_second_info(__s);
+ }
+ }
+
/// @} group chrono
#endif // C++20
} // namespace chrono
new file mode 100644
@@ -0,0 +1,38 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using namespace std::chrono;
+
+ gps_seconds gps_epoch{0s};
+ utc_seconds gps_as_utc{sys_days{1980y/January/Sunday[1]}.time_since_epoch() + 9s};
+
+ VERIFY( clock_cast<utc_clock>(gps_epoch) == gps_as_utc );
+ VERIFY( gps_epoch == clock_cast<gps_clock>(gps_as_utc) );
+
+ tai_seconds tai_epoch{0s};
+ VERIFY( clock_cast<tai_clock>(clock_cast<gps_clock>(tai_epoch)) == tai_epoch );
+}
+
+void
+test02()
+{
+ using namespace std::chrono;
+
+ sys_days d{2022y/November/12};
+ VERIFY( clock_cast<system_clock>(clock_cast<gps_clock>(d)) == d );
+ gps_seconds t(1234567s);
+ VERIFY( clock_cast<gps_clock>(clock_cast<system_clock>(t)) == t );
+ VERIFY( clock_cast<gps_clock>(clock_cast<utc_clock>(t)) == t );
+}
+
+int main()
+{
+ test01();
+ test02();
+}
new file mode 100644
@@ -0,0 +1,41 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using namespace std::chrono;
+
+ tai_seconds tai_epoch{0s};
+ utc_seconds tai_as_utc{sys_days{1958y/January/1}.time_since_epoch() - 10s};
+
+ VERIFY( clock_cast<utc_clock>(tai_epoch) == tai_as_utc );
+ VERIFY( tai_epoch == clock_cast<tai_clock>(tai_as_utc) );
+
+ sys_days y2k{2000y/January/1};
+ tai_seconds y2k_as_tai{clock_cast<tai_clock>(y2k)};
+ utc_seconds y2k_as_utc = utc_clock::from_sys(y2k);
+ VERIFY( clock_cast<utc_clock>(y2k_as_tai) == y2k_as_utc );
+ VERIFY( y2k_as_tai == clock_cast<tai_clock>(y2k_as_utc) );
+}
+
+void
+test02()
+{
+ using namespace std::chrono;
+
+ sys_days d{2022y/November/12};
+ VERIFY( clock_cast<system_clock>(clock_cast<tai_clock>(d)) == d );
+ tai_seconds t(1234567s);
+ VERIFY( clock_cast<tai_clock>(clock_cast<system_clock>(t)) == t );
+ VERIFY( clock_cast<tai_clock>(clock_cast<utc_clock>(t)) == t );
+}
+
+int main()
+{
+ test01();
+ test02();
+}
new file mode 100644
@@ -0,0 +1,24 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ using namespace std::chrono;
+
+ auto epoch = sys_seconds{sys_days{1970y/January/1}};
+ auto utc_epoch = clock_cast<utc_clock>(epoch);
+ VERIFY( utc_epoch.time_since_epoch() == 0s );
+
+ auto y2k = sys_seconds{sys_days{2000y/January/1}};
+ auto utc_y2k = clock_cast<utc_clock>(y2k);
+ VERIFY( utc_y2k.time_since_epoch() == 946'684'822s );
+}
+
+int main()
+{
+ test01();
+}