[gcc r13-4856] libstdc++: Add std::format support to <chrono>
Jonathan Wakely
redi@gcc.gnu.org
Thu Dec 22 23:34:58 GMT 2022
https://gcc.gnu.org/g:f99b94865fa629cc2cc937128a812b6a23038446
commit r13-4856-gf99b94865fa629cc2cc937128a812b6a23038446
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Thu Dec 22 01:29:22 2022 +0000
libstdc++: Add std::format support to <chrono>
This adds the operator<< overloads and std::formatter specializations
required by C++20 so that <chrono> types can be written to ostreams and
printed with std::format.
libstdc++-v3/ChangeLog:
* include/Makefile.am: Add new header.
* include/Makefile.in: Regenerate.
* include/std/chrono (operator<<): Move to new header.
(nonexistent_local_time::_M_make_what_str): Define correctly.
(ambiguous_local_time::_M_make_what_str): Likewise.
* include/bits/chrono_io.h: New file.
* src/c++20/tzdb.cc (operator<<(ostream&, const Rule&)): Use
new ostream output for month and weekday types.
* testsuite/20_util/duration/io.cc: Test std::format support.
* testsuite/std/time/exceptions.cc: Check what() strings.
* testsuite/std/time/syn_c++20.cc: Uncomment local_time_format.
* testsuite/std/time/time_zone/get_info_local.cc: Enable check
for formatted output of local_info objects.
* testsuite/std/time/clock/file/io.cc: New test.
* testsuite/std/time/clock/gps/io.cc: New test.
* testsuite/std/time/clock/system/io.cc: New test.
* testsuite/std/time/clock/tai/io.cc: New test.
* testsuite/std/time/clock/utc/io.cc: New test.
* testsuite/std/time/day/io.cc: New test.
* testsuite/std/time/format.cc: New test.
* testsuite/std/time/hh_mm_ss/io.cc: New test.
* testsuite/std/time/month/io.cc: New test.
* testsuite/std/time/weekday/io.cc: New test.
* testsuite/std/time/year/io.cc: New test.
* testsuite/std/time/year_month_day/io.cc: New test.
Diff:
---
libstdc++-v3/include/Makefile.am | 1 +
libstdc++-v3/include/Makefile.in | 1 +
libstdc++-v3/include/bits/chrono_io.h | 2469 ++++++++++++++++++++
libstdc++-v3/include/std/chrono | 164 +-
libstdc++-v3/src/c++20/tzdb.cc | 12 +-
libstdc++-v3/testsuite/20_util/duration/io.cc | 48 +
libstdc++-v3/testsuite/std/time/clock/file/io.cc | 23 +
libstdc++-v3/testsuite/std/time/clock/gps/io.cc | 24 +
libstdc++-v3/testsuite/std/time/clock/system/io.cc | 72 +
libstdc++-v3/testsuite/std/time/clock/tai/io.cc | 24 +
libstdc++-v3/testsuite/std/time/clock/utc/io.cc | 120 +
libstdc++-v3/testsuite/std/time/day/io.cc | 75 +
libstdc++-v3/testsuite/std/time/exceptions.cc | 4 +-
libstdc++-v3/testsuite/std/time/format.cc | 117 +
libstdc++-v3/testsuite/std/time/hh_mm_ss/io.cc | 46 +
libstdc++-v3/testsuite/std/time/month/io.cc | 98 +
libstdc++-v3/testsuite/std/time/syn_c++20.cc | 3 +-
.../testsuite/std/time/time_zone/get_info_local.cc | 2 -
libstdc++-v3/testsuite/std/time/weekday/io.cc | 101 +
libstdc++-v3/testsuite/std/time/year/io.cc | 89 +
.../testsuite/std/time/year_month_day/io.cc | 121 +
21 files changed, 3465 insertions(+), 149 deletions(-)
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 92b5450fc14..e91f4ddd4de 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 5d00f90a423..06589d53856 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_io.h b/libstdc++-v3/include/bits/chrono_io.h
new file mode 100644
index 00000000000..4e53cd4aa2e
--- /dev/null
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -0,0 +1,2469 @@
+// <chrono> 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"); }
+
+ [[noreturn,__gnu__::__always_inline__]]
+ inline void
+ __invalid_chrono_spec()
+ { __throw_format_error("format error: chrono-format-spec not valid for "
+ "argument type"); }
+
+ 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, sys_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,
+ _ZonedDateTime = _DateTime | _TimeZone,
+ _Duration = 128 // special case
+ };
+
+ constexpr _ChronoParts
+ operator|(_ChronoParts __x, _ChronoParts __y)
+ { return static_cast<_ChronoParts>((int)__x | (int)__y); }
+
+ // TODO rename this to chrono::__formatter? or chrono::__detail::__formatter?
+ template<typename _CharT>
+ struct __formatter_chrono
+ {
+ using __string_view = basic_string_view<_CharT>;
+ using __string = basic_string<_CharT>;
+
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ _M_parse(_ParseContext& __pc, _ChronoParts __parts)
+ {
+ auto __first = __pc.begin();
+ 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;
+
+ if (__parts & _ChronoParts::_Duration)
+ {
+ __first = __spec._M_parse_precision(__first, __last, __pc);
+ if (__finished())
+ return __first;
+ }
+
+ __first = __spec._M_parse_locale(__first, __last);
+ if (__finished())
+ return __first;
+
+ // Everything up to the end of the string or the first '}' is a
+ // chrono-specs string. Check it is valid.
+ {
+ __string_view __str(__first, __last - __first);
+ auto __end = __str.find('}');
+ if (__end != __str.npos)
+ {
+ __str.remove_suffix(__str.length() - __end);
+ __last = __first + __end;
+ }
+ if (__str.find('{') != __str.npos)
+ __throw_format_error("chrono format error: '{' in chrono-specs");
+ }
+
+ // Parse chrono-specs in [first,last), checking 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++; // Skip leading '%'
+ if (*__chrono_specs != '%')
+ __throw_format_error("chrono format error: no '%' at start of "
+ "chrono-specs");
+
+ _CharT __mod{};
+ bool __conv = true;
+ int __needed = 0;
+
+ while (__first != __last)
+ {
+ enum _Mods { _Mod_none, _Mod_E, _Mod_O, _Mod_E_O };
+ _Mods __allowed_mods = _Mod_none;
+
+ _CharT __c = *__first++;
+ switch (__c)
+ {
+ case 'a':
+ case 'A':
+ __needed = _Weekday;
+ break;
+ case 'b':
+ case 'h':
+ case 'B':
+ __needed = _Month;
+ break;
+ case 'c':
+ __needed = _DateTime;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'C':
+ __needed = _Year;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'd':
+ case 'e':
+ __needed = _Day;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'D':
+ case 'F':
+ __needed = _Date;
+ break;
+ case 'g':
+ case 'G':
+ __needed = _Date;
+ break;
+ case 'H':
+ case 'I':
+ __needed = _TimeOfDay;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'j':
+ if (!(__parts & _Duration))
+ __needed = _Date;
+ break;
+ case 'm':
+ __needed = _Month;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'M':
+ __needed = _TimeOfDay;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'p':
+ case 'r':
+ case 'R':
+ case 'T':
+ __needed = _TimeOfDay;
+ break;
+ case 'q':
+ case 'Q':
+ __needed = _Duration;
+ break;
+ case 'S':
+ __needed = _TimeOfDay;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'u':
+ case 'w':
+ __needed = _Weekday;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'U':
+ case 'V':
+ case 'W':
+ __needed = _Date;
+ __allowed_mods = _Mod_O;
+ break;
+ case 'x':
+ __needed = _Date;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'X':
+ __needed = _TimeOfDay;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'y':
+ __needed = _Year;
+ __allowed_mods = _Mod_E_O;
+ break;
+ case 'Y':
+ __needed = _Year;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'z':
+ __needed = _TimeZone;
+ __allowed_mods = _Mod_E;
+ break;
+ case 'Z':
+ __needed = _TimeZone;
+ __allowed_mods = _Mod_E_O;
+ break;
+ case 'n':
+ case 't':
+ case '%':
+ break;
+ case 'O':
+ case 'E':
+ __mod = __c;
+ continue;
+ default:
+ __throw_format_error("chrono format error: invalid "
+ " specifier in chrono-specs");
+ }
+
+ if ((__mod == 'E' && !(__allowed_mods & _Mod_E))
+ || __mod == 'O' && !(__allowed_mods & _Mod_O))
+ __throw_format_error("chrono format error: invalid "
+ " modifier in chrono-specs");
+ __mod = _CharT();
+
+ if ((__parts & __needed) != __needed)
+ __throw_format_error("chrono format error: format argument "
+ "does not contain the information "
+ "required by the chrono-specs");
+
+ // Scan for next '%', ignoring literal-chars before it.
+ size_t __pos = __string_view(__first, __last - __first).find('%');
+ if (__pos == 0)
+ ++__first;
+ else
+ {
+ if (__pos == __string_view::npos)
+ {
+ __first = __last;
+ __conv = false;
+ }
+ else
+ __first += __pos + 1;
+ }
+ }
+
+ // Check for a '%' conversion-spec without a type.
+ if (__conv || __mod != _CharT())
+ __throw_format_error("chrono format error: unescaped '%' in "
+ "chrono-specs");
+
+ _M_spec = __spec;
+ _M_spec._M_chrono_specs = {__chrono_specs, __first - __chrono_specs};
+
+ return __first;
+ }
+
+ // TODO this function template is instantiated for every different _Tp.
+ // Consider creating a polymorphic interface for calendar types so
+ // that we instantiate fewer different specializations. Similar to
+ // _Sink_iter for std::format. Replace each _S_year, _S_day etc. with
+ // member functions of that type.
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_format(const _Tp& __t, _FormatContext& __fc,
+ bool __is_neg = false) const
+ {
+ if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
+ __is_neg = __t.is_negative();
+ else if constexpr (!chrono::__is_duration_v<_Tp>)
+ __is_neg = false;
+
+ auto __first = _M_spec._M_chrono_specs.begin();
+ const auto __last = _M_spec._M_chrono_specs.end();
+ if (__first == __last)
+ return _M_format_to_ostream(__t, __fc, __is_neg);
+
+ _Sink_iter<_CharT> __out;
+ __format::_Str_sink<_CharT> __sink;
+ bool __write_direct = false;
+ if constexpr (is_same_v<typename _FormatContext::iterator,
+ _Sink_iter<_CharT>>)
+ {
+ if (_M_spec._M_width_kind == __format::_WP_none)
+ {
+ __out = __fc.out();
+ __write_direct = true;
+ }
+ else
+ __out = __sink.out();
+ }
+ else
+ __out = __sink.out();
+
+ auto __print_sign = [&__is_neg, &__out] {
+ if (__is_neg)
+ {
+ *__out++ = _S_plus_minus[1];
+ __is_neg = false;
+ }
+ return std::move(__out);
+ };
+
+ // Characters to output for "%n", "%t" and "%%" specifiers.
+ constexpr const _CharT* __literals = _GLIBCXX_WIDEN("\n\t%");
+
+ ++__first; // Skip leading '%' at start of chrono-specs.
+
+ _CharT __mod{};
+ do
+ {
+ _CharT __c = *__first++;
+ switch (__c)
+ {
+ case 'a':
+ case 'A':
+ __out = _M_a_A(__t, std::move(__out), __fc, __c == 'A');
+ break;
+ case 'b':
+ case 'h':
+ case 'B':
+ __out = _M_b_B(__t, std::move(__out), __fc, __c == 'B');
+ break;
+ case 'c':
+ __out = _M_c(__t, std::move(__out), __fc, __mod == 'E');
+ break;
+ case 'C':
+ case 'y':
+ case 'Y':
+ __out = _M_C_y_Y(__t, std::move(__out), __fc, __c, __mod);
+ break;
+ case 'd':
+ // %d The day of month as a decimal number.
+ // %Od Locale's alternative representation.
+ __out = _S_dd_zero_fill((unsigned)_S_day(__t),
+ std::move(__out),
+ __fc, __mod == 'O');
+ break;
+ case 'D':
+ __out = _M_D(__t, std::move(__out), __fc);
+ break;
+ case 'e':
+ __out = _M_e(__t, std::move(__out), __fc, __mod == 'O');
+ break;
+ case 'F':
+ __out = _M_F(__t, std::move(__out), __fc);
+ break;
+ case 'g':
+ case 'G':
+ __out = _M_g_G(__t, std::move(__out), __fc, __c == 'G');
+ break;
+ case 'H':
+ // %H The hour (24-hour clock) as a decimal number.
+ // %OH Locale's alternative representation.
+ __out = _S_dd_zero_fill(_S_hms(__t).hours().count(),
+ __print_sign(), __fc, __mod == 'O');
+ break;
+ case 'I':
+ __out = _M_I(__t, __print_sign(), __fc, __mod == 'O');
+ break;
+ case 'j':
+ __out = _M_j(__t, __print_sign(), __fc);
+ break;
+ case 'm':
+ // %m month as a decimal number.
+ // %Om Locale's alternative representation.
+ __out = _S_dd_zero_fill((unsigned)_S_month(__t),
+ std::move(__out), __fc,
+ __mod == 'O');
+ break;
+ case 'M':
+ // %M The minute as a decimal number.
+ // %OM Locale's alternative representation.
+ __out = _S_dd_zero_fill(_S_hms(__t).minutes().count(),
+ __print_sign(), __fc, __mod == 'O');
+ break;
+ case 'p':
+ __out = _M_p(__t, std::move(__out), __fc);
+ break;
+ case 'q':
+ __out = _M_q(__t, std::move(__out), __fc);
+ break;
+ case 'Q':
+ // %Q The duration's numeric value.
+ if constexpr (chrono::__is_duration_v<_Tp>)
+ __out = std::format_to(__print_sign(), _S_empty_spec,
+ __t.count());
+ else
+ __throw_format_error("chrono format error: argument is "
+ "not a duration");
+ break;
+ case 'r':
+ __out = _M_r(__t, __print_sign(), __fc);
+ break;
+ case 'R':
+ case 'T':
+ __out = _M_R_T(__t, __print_sign(), __fc, __c == 'T');
+ break;
+ case 'S':
+ __out = _M_S(__t, __print_sign(), __fc, __mod == 'O');
+ break;
+ case 'u':
+ case 'w':
+ __out = _M_u_w(__t, std::move(__out), __fc, __c, __mod == 'O');
+ break;
+ case 'U':
+ case 'V':
+ case 'W':
+ __out = _M_U_V_W(__t, std::move(__out), __fc, __c,
+ __mod == 'O');
+ break;
+ case 'x':
+ __out = _M_x(__t, std::move(__out), __fc, __mod == 'E');
+ break;
+ case 'X':
+ __out = _M_X(__t, __print_sign(), __fc, __mod == 'E');
+ break;
+ case 'z':
+ __out = _M_z(__t, std::move(__out), __fc, (bool)__mod);
+ break;
+ case 'Z':
+ __out = _M_Z(__t, std::move(__out), __fc);
+ break;
+ case 'n':
+ *__out++ = __literals[0];
+ break;
+ case 't':
+ *__out++ = __literals[1];
+ break;
+ case '%':
+ *__out++ = __literals[2];
+ break;
+ case 'O':
+ case 'E':
+ __mod = __c;
+ continue;
+ case '}':
+ __first = __last;
+ break;
+ }
+ __mod = _CharT();
+ // Scan for next '%' and write out everything before it.
+ __string_view __str(__first, __last - __first);
+ size_t __pos = __str.find('%');
+ if (__pos == 0)
+ ++__first;
+ else
+ {
+ if (__pos == __str.npos)
+ __first = __last;
+ else
+ {
+ __str.remove_suffix(__str.length() - __pos);
+ __first += __pos + 1;
+ }
+ __out = __format::__write(std::move(__out), __str);
+ }
+ }
+ while (__first != __last);
+
+ if constexpr (is_same_v<typename _FormatContext::iterator,
+ _Sink_iter<_CharT>>)
+ if (__write_direct)
+ return __out;
+
+ auto __str = std::move(__sink).get();
+ return __format::__write_padded_as_spec(__str, __str.size(),
+ __fc, _M_spec);
+ }
+
+ _ChronoSpec<_CharT> _M_spec;
+
+ private:
+ // Return the formatting locale.
+ template<typename _FormatContext>
+ std::locale
+ _M_locale(_FormatContext& __fc) const
+ {
+ if (!_M_spec._M_localized)
+ return std::locale::classic();
+ else
+ return __fc.locale();
+ }
+
+ // TODO: consider moving body of every operator<< into this function
+ // and use std::format("{}", t) to implement those operators. That
+ // would avoid std::format("{}", t) calling operator<< which calls
+ // std::format again.
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_format_to_ostream(const _Tp& __t, _FormatContext& __fc,
+ bool __is_neg) const
+ {
+ using ::std::chrono::__detail::__utc_leap_second;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ return _M_format_to_ostream(__t._M_time, __fc, false);
+ else
+ {
+ basic_ostringstream<_CharT> __os;
+ __os.imbue(_M_locale(__fc));
+
+ if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+ __os << __t._M_date << ' ' << __t._M_time;
+ else
+ {
+ if (__is_neg) [[unlikely]]
+ __os << _S_plus_minus[1];
+ __os << __t;
+ }
+
+ auto __str = std::move(__os).str();
+ return __format::__write_padded_as_spec(__str, __str.size(),
+ __fc, _M_spec);
+ }
+ }
+
+ 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];
+ static constexpr _CharT _S_space = _S_chars[14];
+ static constexpr const _CharT* _S_empty_spec = _S_chars + 15;
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_a_A(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __full) const
+ {
+ // %a Locale's abbreviated weekday name.
+ // %A Locale's full weekday name.
+ chrono::weekday __wd = _S_weekday(__t);
+ if (!__wd.ok())
+ __throw_format_error("format error: invalid weekday");
+
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __days[7];
+ if (__full)
+ __tp._M_days(__days);
+ else
+ __tp._M_days_abbreviated(__days);
+ __string_view __str(__days[__wd.c_encoding()]);
+ return __format::__write(std::move(__out), __str);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_b_B(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __full) const
+ {
+ // %b Locale's abbreviated month name.
+ // %B Locale's full month name.
+ chrono::month __m = _S_month(__t);
+ if (!__m.ok())
+ __throw_format_error("format error: invalid month");
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __months[12];
+ if (__full)
+ __tp._M_months(__months);
+ else
+ __tp._M_months_abbreviated(__months);
+ __string_view __str(__months[(unsigned)__m - 1]);
+ return __format::__write(std::move(__out), __str);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_c(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ // %c Locale's date and time representation.
+ // %Ec Locale's alternate date and time representation.
+
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __formats[2];
+ __tp._M_date_time_formats(__formats);
+ const _CharT* __rep = __formats[__mod];
+ if (!*__rep)
+ __rep = _GLIBCXX_WIDEN("%a %b %e %H:%M:%S %Y");
+ basic_string<_CharT> __fmt(_S_empty_spec);
+ __fmt.insert(1u, 1u, _S_colon);
+ __fmt.insert(2u, __rep);
+ return std::vformat_to(std::move(__out), __loc, __fmt,
+ std::make_format_args<_FormatContext>(__t));
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_C_y_Y(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, char __conv, char __mod = 0) const
+ {
+ // %C Year divided by 100 using floored division.
+ // %EC Locale's alternative preresentation of the century (era name).
+ // %y Last two decimal digits of the year.
+ // %OY Locale's alternative represenation.
+ // %Ey Locale's alternative representation of offset from %EC.
+ // %Y Year as a decimal number.
+ // %EY Locale's alternative full year represenation.
+
+ chrono::year __y = _S_year(__t);
+
+ if (__mod == 'E')
+ {
+ // TODO: %EC, %Ey or %EY
+ // return __out;
+ }
+
+ basic_string<_CharT> __s;
+ int __yi = (int)__y;
+ const bool __is_neg = __yi < 0;
+ __yi = __builtin_abs(__yi);
+
+ if (__conv == 'Y' || __conv == 'C')
+ {
+ if (__is_neg)
+ __s.assign(1, _S_plus_minus[1]);
+ int __ci = __yi / 100;
+ if (__ci >= 100) [[unlikely]]
+ {
+ __s += std::format(_S_empty_spec, __ci / 100);
+ __ci %= 100;
+ }
+ __s += _S_two_digits(__ci);
+ }
+
+ if (__conv == 'Y' || __conv == 'y')
+ __s += _S_two_digits(__yi % 100);
+
+ if (__mod == 'O') // %OY
+ _S_altnum(_M_locale(__ctx), __s, __is_neg);
+
+ return __format::__write(std::move(__out), __string_view(__s));
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_D(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext&) const
+ {
+ auto __ymd = _S_date(__t);
+ basic_string<_CharT> __s;
+#if ! _GLIBCXX_USE_CXX11_ABI
+ __s.reserve(8);
+#endif
+ __s = _S_two_digits((unsigned)__ymd.month());
+ __s += _S_slash;
+ __s += _S_two_digits((unsigned)__ymd.day());
+ __s += _S_slash;
+ __s += _S_two_digits(__builtin_abs((int)__ymd.year()) % 100);
+ return __format::__write(std::move(__out), __string_view(__s));
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_e(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ // %e Day of month as decimal number, padded with space.
+ // %Oe Locale's alternative digits.
+ chrono::day __d = _S_day(__t);
+ unsigned __i = (unsigned)__d;
+ auto __sv = _S_two_digits(__i);
+ basic_string<_CharT> __s;
+ if (__mod)
+ __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
+ if (__i < 10)
+ __sv = __s = {_S_space, __sv[1]};
+ return __format::__write(std::move(__out), __sv);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_F(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext&) const
+ {
+ auto __ymd = _S_date(__t);
+ basic_string<_CharT> __s;
+#if ! _GLIBCXX_USE_CXX11_ABI
+ __s.reserve(11);
+#endif
+ __s += std::format(_GLIBCXX_WIDEN("{:04d}- - "), (int)__ymd.year());
+ auto __sv = _S_two_digits((unsigned)__ymd.month());
+ __s[__s.size() - 5] = __sv[0];
+ __s[__s.size() - 4] = __sv[1];
+ __sv = _S_two_digits((unsigned)__ymd.day());
+ __s[__s.size() - 2] = __sv[0];
+ __s[__s.size() - 1] = __sv[1];
+ __sv = __s;
+ return __format::__write(std::move(__out), __sv);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_g_G(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __full) const
+ {
+ // %g last two decimal digits of the ISO week-based year.
+ // %G ISO week-based year.
+ using namespace chrono;
+ auto __d = _S_days(__t);
+ // Move to nearest Thursday:
+ __d -= (weekday(__d) - Monday) - days(3);
+ // ISO week-based year is the year that contains that Thursday:
+ year __y = year_month_day(__d).year();
+ return _M_C_y_Y(__y, std::move(__out), __ctx, "yY"[__full]);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_I(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ auto __hms = _S_hms(__t);
+ int __i = __hms.hours().count();
+ if (__i == 0)
+ __i = 12;
+ else if (__i > 12)
+ __i -= 12;
+ auto __sv = _S_two_digits(__i);
+ basic_string<_CharT> __s;
+ if (__mod)
+ __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
+ return __format::__write(std::move(__out), __sv);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_j(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx) const
+ {
+ if constexpr (chrono::__is_duration_v<_Tp>)
+ {
+ // Decimal number of days, without padding.
+ unsigned __d = chrono::duration_cast<chrono::days>(__t).count();
+ return std::format_to(std::move(__out), _S_empty_spec, __d);
+ }
+ else
+ {
+ // Day of the year as a decimal number, padding with zero.
+ using namespace chrono;
+ auto __day = _S_days(__t);
+ auto __ymd = _S_date(__t);
+ days __d;
+ // See "Calculating Ordinal Dates" at
+ // https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes
+ if constexpr (is_same_v<typename decltype(__day)::clock, local_t>)
+ __d = __day - local_days(__ymd.year()/January/0);
+ else
+ __d = __day - sys_days(__ymd.year()/January/0);
+ return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"),
+ __d.count());
+ }
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_p(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx) const
+ {
+ // %p The locale's equivalent of the AM/PM designations.
+ auto __hms = _S_hms(__t);
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __ampm[2];
+ __tp._M_am_pm(__ampm);
+ return std::format_to(std::move(__out), _S_empty_spec,
+ __ampm[__hms.hours().count() >= 12]);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_q(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx) const
+ {
+ // %q The duration's unit suffix
+ if constexpr (!chrono::__is_duration_v<_Tp>)
+ __throw_format_error("format error: argument is not a duration");
+ else
+ {
+ using period = typename _Tp::period;
+ char __buf[sizeof("[/]s") + 2 * numeric_limits<intmax_t>::digits10];
+ constexpr size_t __n = sizeof(__buf);
+ auto __s = chrono::__detail::__units_suffix<period, _CharT>(__buf,
+ __n);
+ if constexpr (is_same_v<decltype(__s), const _CharT*>)
+ return std::format_to(std::move(__out), _S_empty_spec, __s);
+ else
+ {
+ // Suffix was written to __buf as narrow string.
+ _CharT __wbuf[__n];
+ size_t __len = __builtin_strlen(__buf);
+ locale __loc = _M_locale(__ctx);
+ auto& __ct = use_facet<ctype<_CharT>>(__loc);
+ __ct.widen(__buf, __len, __wbuf);
+ __wbuf[__len] = 0;
+ return std::format_to(std::move(__out), _S_empty_spec,
+ __wbuf);
+ }
+ }
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_r(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx) const
+ {
+ // %r locale's 12-hour clock time.
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __ampm_fmt;
+ __tp._M_am_pm_format(&__ampm_fmt);
+ basic_string<_CharT> __fmt(_S_empty_spec);
+ __fmt.insert(1u, 1u, _S_colon);
+ __fmt.insert(2u, __ampm_fmt);
+ return std::vformat_to(std::move(__out), __fmt,
+ std::make_format_args<_FormatContext>(__t));
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_R_T(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __secs) const
+ {
+ // %R Equivalent to %H:%M
+ // %T Equivalent to %H:%M:%S
+ auto __hms = _S_hms(__t);
+
+ basic_string<_CharT> __s;
+#if ! _GLIBCXX_USE_CXX11_ABI
+ __s.reserve(11);
+#endif
+ __s = std::format(_GLIBCXX_WIDEN("{:02d}:00"), __hms.hours().count());
+ auto __sv = _S_two_digits(__hms.minutes().count());
+ __s[__s.size() - 2] = __sv[0];
+ __s[__s.size() - 1] = __sv[1];
+ __sv = __s;
+ __out = __format::__write(std::move(__out), __sv);
+ if (__secs)
+ {
+ *__out++ = _S_colon;
+ __out = _M_S(__hms, std::move(__out), __ctx);
+ }
+ return __out;
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_S(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ // %S Seconds as a decimal number.
+ // %OS (TODO) The locale's alternative representation.
+ auto __hms = _S_hms(__t);
+ __out = _S_dd_zero_fill(__hms.seconds().count(),
+ std::move(__out), __ctx, __mod);
+ using rep = typename decltype(__hms)::precision::rep;
+ if constexpr (__hms.fractional_width != 0)
+ {
+ locale __loc = _M_locale(__ctx);
+ auto __ss = __hms.subseconds();
+ if constexpr (is_floating_point_v<rep>)
+ {
+ __out = std::format_to(__loc, std::move(__out),
+ _GLIBCXX_WIDEN("{:.{}Lg}"),
+ __ss.count(),
+ __hms.fractional_width);
+ }
+ else if constexpr (is_integral_v<rep>)
+ {
+ const auto& __np
+ = use_facet<numpunct<_CharT>>(__loc);
+ __out = std::format_to(std::move(__out),
+ _GLIBCXX_WIDEN("{}{:0{}}"),
+ __np.decimal_point(),
+ __ss.count(),
+ __hms.fractional_width);
+ }
+ else
+ {
+ const auto& __np
+ = use_facet<numpunct<_CharT>>(__loc);
+ *__out++ = __np.decimal_point();
+ auto __str = std::format(_S_empty_spec, __ss.count());
+ __out = std::format_to(_GLIBCXX_WIDEN("{:0>{}s}"),
+ __str,
+ __hms.fractional_width);
+ }
+ }
+ return __out;
+ }
+
+ // %t handled in _M_format
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_u_w(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
+ {
+ // %u ISO weekday as a decimal number (1-7), where Monday is 1.
+ // %Ou Locale's alternative numeric rep.
+ // %w Weekday as a decimal number (0-6), where Sunday is 0.
+ // %Ow Locale's alternative numeric rep.
+ chrono::weekday __wd = _S_weekday(__t);
+ unsigned __wdi = __conv == 'u' ? __wd.iso_encoding()
+ : __wd.c_encoding();
+ basic_string<_CharT> __s(1, _S_digit(__wdi));
+ if (__mod)
+ _S_altnum(_M_locale(__ctx), __s);
+ return __format::__write(std::move(__out), __string_view(__s));
+ return __out;
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_U_V_W(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
+ {
+ // %U Week number of the year as a decimal number, from first Sunday.
+ // %OU Locale's alternative numeric rep.
+ // %V ISO week-based week number as a decimal number.
+ // %OV Locale's alternative numeric rep.
+ // %W Week number of the year as a decimal number, from first Monday.
+ // %OW Locale's alternative numeric rep.
+ using namespace chrono;
+ auto __d = _S_days(__t);
+ using _TDays = decltype(__d); // Either sys_days or local_days.
+
+ _TDays __first; // First day of week 1.
+ if (__conv == 'V') // W01 begins on Monday before first Thursday.
+ {
+ // Move to nearest Thursday:
+ __d -= (weekday(__d) - Monday) - days(3);
+ // ISO week of __t is number of weeks since January 1 of the
+ // same year as that nearest Thursday.
+ __first = _TDays(year_month_day(__d).year()/January/1);
+ }
+ else
+ {
+ year __y;
+ if constexpr (requires { __t.year(); })
+ __y = __t.year();
+ else
+ __y = year_month_day(__d).year();
+ const weekday __weekstart = __conv == 'U' ? Sunday : Monday;
+ __first = _TDays(__y/January/__weekstart[1]);
+ }
+ auto __weeks = chrono::floor<weeks>(__d - __first);
+ __string_view __sv = _S_two_digits(__weeks.count() + 1);
+ basic_string<_CharT> __s;
+ if (__mod)
+ __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
+ return __format::__write(std::move(__out), __sv);
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_x(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ // %x Locale's date rep
+ // %Ex Locale's alternative date representation.
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __date_reps[2];
+ __tp._M_date_formats(__date_reps);
+ const _CharT* __rep = __date_reps[__mod];
+ if (!*__rep)
+ return _M_D(__t, std::move(__out), __ctx);
+
+ basic_string<_CharT> __fmt(_S_empty_spec);
+ __fmt.insert(1u, 1u, _S_colon);
+ __fmt.insert(2u, __rep);
+ return std::vformat_to(std::move(__out), __fmt,
+ std::make_format_args<_FormatContext>(__t));
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_X(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __mod = false) const
+ {
+ // %X Locale's time rep
+ // %EX Locale's alternative time representation.
+ locale __loc = _M_locale(__ctx);
+ const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
+ const _CharT* __time_reps[2];
+ __tp._M_time_formats(__time_reps);
+ const _CharT* __rep = __time_reps[__mod];
+ if (!*__rep)
+ return _M_R_T(__t, std::move(__out), __ctx, true);
+
+ basic_string<_CharT> __fmt(_S_empty_spec);
+ __fmt.insert(1u, 1u, _S_colon);
+ __fmt.insert(2u, __rep);
+ return std::vformat_to(std::move(__out), __fmt,
+ std::make_format_args<_FormatContext>(__t));
+ }
+
+ 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::__utc_leap_second;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ auto __utc = __mod ? __string_view(_GLIBCXX_WIDEN("+00:00"), 6)
+ : __string_view(_GLIBCXX_WIDEN("+0000"), 5);
+
+ if constexpr (chrono::__is_time_point_v<_Tp>)
+ {
+ if constexpr (is_same_v<typename _Tp::clock,
+ chrono::system_clock>)
+ return __format::__write(std::move(__out), __utc);
+ }
+ else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ {
+ if (__t._M_offset_sec)
+ {
+ auto __sv = __utc;
+ basic_string<_CharT> __s;
+ if (*__t._M_offset_sec != 0s)
+ {
+ chrono:: hh_mm_ss __hms(*__t._M_offset_sec);
+ __s = _S_plus_minus[__hms.is_negative()];
+ __s += _S_two_digits(__hms.hours().count());
+ if (__mod)
+ __s += _S_colon;
+ __s += _S_two_digits(__hms.minutes().count());
+ __sv = __s;
+ }
+ return __format::__write(std::move(__out), __sv);
+ }
+ }
+ else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+ return __format::__write(std::move(__out), __utc);
+
+ __no_timezone_available();
+ }
+
+ template<typename _Tp, typename _FormatContext>
+ typename _FormatContext::iterator
+ _M_Z(const _Tp& __t, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx) const
+ {
+ using ::std::chrono::__detail::__utc_leap_second;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ __string_view __utc(_GLIBCXX_WIDEN("UTC"), 3);
+ if constexpr (chrono::__is_time_point_v<_Tp>)
+ {
+ if constexpr (is_same_v<typename _Tp::clock,
+ chrono::system_clock>)
+ return __format::__write(std::move(__out), __utc);
+ }
+ else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ {
+ if (__t._M_abbrev)
+ {
+ __string_view __wsv;
+ if constexpr (is_same_v<_CharT, char>)
+ __wsv = *__t._M_abbrev;
+ else
+ {
+ string_view __sv = *__t._M_abbrev;
+ basic_string<_CharT> __ws(__sv.size());
+ auto& __ct = use_facet<ctype<_CharT>>(_M_locale(__ctx));
+ __ct.widen(__sv.data(), __sv.size(), __ws.data());
+ __wsv = __ws;
+ }
+ return __format::__write(std::move(__out), __wsv);
+ }
+ }
+ else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+ return __format::__write(std::move(__out), __utc);
+
+ __no_timezone_available();
+ }
+
+ // %% handled in _M_format
+
+ // A single digit character in the range '0'..'9'.
+ static _CharT
+ _S_digit(int __n) noexcept
+ {
+ // Extra 9s avoid past-the-end read on bad input.
+ return _GLIBCXX_WIDEN("0123456789999999")[__n & 0xf];
+ }
+
+ // A string view of two digit characters, "00".."99".
+ static basic_string_view<_CharT>
+ _S_two_digits(int __n) noexcept
+ {
+ return {
+ _GLIBCXX_WIDEN("0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899"
+ "9999999999999999999999999999999999999999"
+ "9999999999999999") + 2 * (__n & 0x7f),
+ 2
+ };
+ }
+
+ // Convert a numeric string to the locale's alternative numeric symbols.
+ static basic_string_view<_CharT>
+ _S_altnum(const locale& __loc, basic_string<_CharT>& __s,
+ bool __is_neg = false)
+ {
+ if (__loc == locale::classic())
+ return __s;
+
+#if 0 // TODO how can we access numpunct_cache?! Need to go via std::time_put?
+ auto& __np = use_facet<__numpunct_cache<_CharT>>(__loc);
+ auto __nums = __np._M_atoms_out; // alts for "-+xX01234..."
+ if (__is_neg)
+ __s[0] = __nums[0];
+ __nums += 4; // now points to alternate digits
+ for (int __i = __is_neg; __i < __s.size(); ++__i)
+ __s[__i] = __nums[__s[__i] - '0'];
+#endif
+ return __s;
+ }
+
+ // Write two digits, zero-filled.
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ _S_dd_zero_fill(int __val, typename _FormatContext::iterator __out,
+ _FormatContext& __ctx, bool __alt_num) const
+ {
+ auto __sv = _S_two_digits(__val);
+ basic_string<_CharT> __s;
+ if (__alt_num)
+ __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
+ return __format::__write(std::move(__out), __sv);
+ }
+
+ // Accessors for the components of chrono types:
+
+ // Returns a hh_mm_ss.
+ template<typename _Tp>
+ static decltype(auto)
+ _S_hms(const _Tp& __t)
+ {
+ using ::std::chrono::__detail::__utc_leap_second;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
+ return __t;
+ else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+ return __t._M_time;
+ else if constexpr (chrono::__is_duration_v<_Tp>)
+ return chrono::hh_mm_ss<_Tp>(__t);
+ else if constexpr (chrono::__is_time_point_v<_Tp>)
+ return chrono::hh_mm_ss(__t - chrono::floor<chrono::days>(__t));
+ else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ return _S_hms(__t._M_time);
+ else
+ {
+ __invalid_chrono_spec();
+ return chrono::hh_mm_ss<chrono::seconds>();
+ }
+ }
+
+ // Returns a sys_days or local_days.
+ template<typename _Tp>
+ static auto
+ _S_days(const _Tp& __t)
+ {
+ using namespace chrono;
+ using ::std::chrono::__detail::__utc_leap_second;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ if constexpr (__is_time_point_v<_Tp>)
+ return chrono::floor<days>(__t);
+ else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+ return __t._M_date;
+ else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+ return chrono::floor<days>(__t._M_time);
+ else if constexpr (is_same_v<_Tp, year_month_day>
+ || is_same_v<_Tp, year_month_day_last>
+ || is_same_v<_Tp, year_month_weekday>
+ || is_same_v<_Tp, year_month_weekday_last>)
+ return sys_days(__t);
+ else
+ {
+ if constexpr (__is_duration_v<_Tp>)
+ __not_valid_for_duration();
+ else
+ __invalid_chrono_spec();
+ return chrono::sys_days();
+ }
+ }
+
+ // Returns a year_month_day.
+ template<typename _Tp>
+ static chrono::year_month_day
+ _S_date(const _Tp& __t)
+ {
+ if constexpr (is_same_v<_Tp, chrono::year_month_day>)
+ return __t;
+ else
+ return chrono::year_month_day(_S_days(__t));
+ }
+
+ template<typename _Tp>
+ static chrono::day
+ _S_day(const _Tp& __t)
+ {
+ using namespace chrono;
+
+ if constexpr (is_same_v<_Tp, day>)
+ return __t;
+ else if constexpr (requires { __t.day(); })
+ return __t.day();
+ else
+ return _S_date(__t).day();
+ }
+
+ template<typename _Tp>
+ static chrono::month
+ _S_month(const _Tp& __t)
+ {
+ using namespace chrono;
+
+ if constexpr (is_same_v<_Tp, month>)
+ return __t;
+ else if constexpr (requires { __t.month(); })
+ return __t.month();
+ else
+ return _S_date(__t).month();
+ }
+
+ template<typename _Tp>
+ static chrono::year
+ _S_year(const _Tp& __t)
+ {
+ using namespace chrono;
+
+ if constexpr (is_same_v<_Tp, year>)
+ return __t;
+ else if constexpr (requires { __t.year(); })
+ return __t.year();
+ else
+ return _S_date(__t).year();
+ }
+
+ template<typename _Tp>
+ static chrono::weekday
+ _S_weekday(const _Tp& __t)
+ {
+ using namespace ::std::chrono;
+ using ::std::chrono::__detail::__local_time_fmt;
+
+ if constexpr (is_same_v<_Tp, weekday>)
+ return __t;
+ else if constexpr (requires { __t.weekday(); })
+ return __t.weekday();
+ else if constexpr (is_same_v<_Tp, month_weekday>)
+ return __t.weekday_indexed().weekday();
+ else if constexpr (is_same_v<_Tp, month_weekday_last>)
+ return __t.weekday_last().weekday();
+ else
+ return weekday(_S_days(__t));
+ }
+ };
+
+} // 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)
+ {
+ using namespace __format;
+ auto __it = _M_f._M_parse(__pc, _Duration|_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(chrono::abs(__d), __fc, __d < __d.zero());
+ }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::day, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Day); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::day& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::month, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Month); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::month& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Year); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::weekday, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Weekday); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::weekday& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::weekday_indexed, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Weekday); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::weekday_indexed& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::weekday_last, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Weekday); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::weekday_last& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::month_day, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Month|__format::_Day); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::month_day& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::month_day_last, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Month|__format::_Day); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::month_day_last& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::month_weekday, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::month_weekday& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::month_weekday_last, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::month_weekday_last& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year_month, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Year|__format::_Month); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year_month& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year_month_day, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Date); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year_month_day& __t, _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year_month_day_last, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Date); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year_month_day_last& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year_month_weekday, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Date); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year_month_weekday& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::year_month_weekday_last, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_Date); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::year_month_weekday_last& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _Rep, typename _Period, typename _CharT>
+ struct formatter<chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_TimeOfDay); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+ template<typename _CharT>
+ struct formatter<chrono::sys_info, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_ChronoParts{}); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::sys_info& __i, _FormatContext& __fc) const
+ { return _M_f._M_format(__i, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+
+ template<typename _CharT>
+ struct formatter<chrono::local_info, _CharT>
+ {
+ template<typename _ParseContext>
+ constexpr typename _ParseContext::iterator
+ parse(_ParseContext& __pc)
+ { return _M_f._M_parse(__pc, __format::_ChronoParts{}); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::local_info& __i, _FormatContext& __fc) const
+ { return _M_f._M_format(__i, __fc); }
+
+ private:
+ __format::__formatter_chrono<_CharT> _M_f;
+ };
+#endif
+
+ 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::_ZonedDateTime); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::sys_time<_Duration>& __t,
+ _FormatContext& __fc) const
+ { return _M_f._M_format(__t, __fc); }
+
+ 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::_ZonedDateTime); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::utc_time<_Duration>& __t,
+ _FormatContext& __fc) const
+ {
+ // Adjust by removing leap seconds to get equivalent sys_time.
+ // We can't just use clock_cast because we want to know if the time
+ // falls within a leap second insertion, and format seconds as "60".
+ using chrono::__detail::__utc_leap_second;
+ using chrono::seconds;
+ using chrono::sys_time;
+ using _CDur = common_type_t<_Duration, seconds>;
+ const auto __li = chrono::get_leap_second_info(__t);
+ sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed};
+ if (!__li.is_leap_second) [[likely]]
+ return _M_f._M_format(__s, __fc);
+ else
+ return _M_f._M_format(__utc_leap_second(__s), __fc);
+ }
+
+ private:
+ friend formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>;
+
+ __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::_ZonedDateTime); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::tai_time<_Duration>& __t,
+ _FormatContext& __fc) const
+ {
+ // Convert to __local_time_fmt with abbrev "TAI" and offset 0s.
+
+ // Offset is 1970y/January/1 - 1958y/January/1
+ constexpr chrono::days __tai_offset = chrono::days(4383);
+ using _CDur = common_type_t<_Duration, chrono::days>;
+ chrono::local_time<_CDur> __lt(__t.time_since_epoch() - __tai_offset);
+ const string __abbrev("TAI", 3);
+ const chrono::seconds __off = 0s;
+ const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off);
+ return _M_f._M_format(__lf, __fc);
+ }
+
+ 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::_ZonedDateTime); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::gps_time<_Duration>& __t,
+ _FormatContext& __fc) const
+ {
+ // Convert to __local_time_fmt with abbrev "GPS" and offset 0s.
+
+ // Offset is 1980y/January/Sunday[1] - 1970y/January/1
+ constexpr chrono::days __gps_offset = chrono::days(3657);
+ using _CDur = common_type_t<_Duration, chrono::days>;
+ chrono::local_time<_CDur> __lt(__t.time_since_epoch() + __gps_offset);
+ const string __abbrev("GPS", 3);
+ const chrono::seconds __off = 0s;
+ const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off);
+ return _M_f._M_format(__lf, __fc);
+ }
+
+ 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::_ZonedDateTime); }
+
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::file_time<_Duration>& __t,
+ _FormatContext& __ctx) const
+ {
+ using namespace chrono;
+ return _M_f._M_format(chrono::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::_DateTime); }
+
+ 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::_ZonedDateTime); }
+
+ 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;
+ };
+
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+ 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();
+ const auto __lf = chrono::local_time_format(__tp.get_local_time(),
+ &__info.abbrev,
+ &__info.offset);
+ return _Base::format(__lf, __ctx);
+ }
+ };
+#endif
+
+ // Partial specialization needed for %c formatting of __utc_leap_second.
+ template<typename _Duration, typename _CharT>
+ struct formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>
+ : formatter<chrono::utc_time<_Duration>, _CharT>
+ {
+ template<typename _FormatContext>
+ typename _FormatContext::iterator
+ format(const chrono::__detail::__utc_leap_second<_Duration>& __t,
+ _FormatContext& __fc) const
+ { return this->_M_f._M_format(__t, __fc); }
+ };
+
+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, 6);
+ __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);
+ int __i = (int)__y;
+ if (__i >= 0) [[likely]]
+ __s.remove_prefix(1);
+ else
+ __i = -__i;
+ __os << std::vformat(__s, make_format_args<_Ctx>(__i));
+ 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>(__wd.c_encoding()));
+ 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,
+ 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);
+ }
+
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+ /// Writes a sys_info object to an ostream in an unspecified format.
+ template<typename _CharT, typename _Traits>
+ basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i)
+ {
+ __os << '[' << __i.begin << ',' << __i.end
+ << ',' << hh_mm_ss(__i.offset) << ',' << __i.save
+ << ',' << __i.abbrev << ']';
+ return __os;
+ }
+
+ /// Writes a local_info object to an ostream in an unspecified format.
+ template<typename _CharT, typename _Traits>
+ basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __li)
+ {
+ __os << '[';
+ if (__li.result == local_info::unique)
+ __os << __li.first;
+ else
+ {
+ if (__li.result == local_info::nonexistent)
+ __os << "nonexistent";
+ else
+ __os << "ambiguous";
+ __os << " local time between " << __li.first;
+ __os << " and " << __li.second;
+ }
+ __os << ']';
+ 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;
+ }
+#endif
+
+ 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>
+ 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 aeb8f6f462f..27f391a1455 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -45,7 +45,6 @@
# include <sstream>
# include <string>
# include <vector>
-# include <bits/charconv.h> // __to_chars_len, __to_chars_10_impl
# include <bits/stl_algo.h> // upper_bound
# include <bits/shared_ptr.h>
# include <bits/unique_ptr.h>
@@ -627,8 +626,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
@@ -751,8 +748,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};
@@ -929,8 +924,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
@@ -1052,8 +1045,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};
@@ -1110,8 +1101,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
@@ -1151,8 +1140,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
@@ -1224,8 +1211,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
@@ -1278,8 +1263,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
@@ -1339,8 +1322,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
friend constexpr year_month_weekday
operator/(const month_weekday& __mwd, int __y) noexcept;
-
- // TODO: Implement operator<<.
};
// MONTH_WEEKDAY_LAST
@@ -1401,8 +1382,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
@@ -1544,8 +1523,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
@@ -1697,8 +1674,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.
@@ -1928,8 +1903,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
@@ -2118,8 +2091,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
@@ -2267,8 +2238,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
@@ -2284,6 +2253,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__r *= 10;
return __r;
}
+
+ template<typename _Duration> struct __utc_leap_second;
}
/// @endcond
@@ -2389,8 +2360,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return _M_h + _M_m + _M_s + subseconds();
}
- // TODO: Implement operator<<.
-
private:
static constexpr bool _S_is_unsigned
= __and_v<is_integral<typename _Duration::rep>,
@@ -2459,8 +2428,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__byte_duration<ratio<1>> _M_s{};
bool _M_is_neg{};
__subseconds<precision> _M_ss{};
+
+ template<typename> friend struct __detail::__utc_leap_second;
};
+ /// @cond undocumented
+ namespace __detail
+ {
+ // Represents a time that is within a leap second insertion.
+ template<typename _Duration>
+ struct __utc_leap_second
+ {
+ explicit
+ __utc_leap_second(const sys_time<_Duration>& __s)
+ : _M_date(chrono::floor<days>(__s)), _M_time(__s - _M_date)
+ {
+ ++_M_time._M_s;
+ }
+
+ sys_days _M_date;
+ hh_mm_ss<common_type_t<_Duration, days>> _M_time;
+ };
+ }
+ /// @endcond
+
// 12/24 HOURS FUNCTIONS
constexpr bool
@@ -2540,9 +2531,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_S_make_what_str(const local_time<_Duration>& __tp,
const local_info& __i)
{
-#if 1
- return "local time is non-existent";
-#else
std::ostringstream __os;
__os << __tp << " is in a gap between\n"
<< local_seconds(__i.first.end.time_since_epoch())
@@ -2552,7 +2540,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
<< " which are both equivalent to\n"
<< __i.first.end << " UTC";
return std::move(__os).str();
-#endif
}
};
@@ -2571,9 +2558,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_S_make_what_str(const local_time<_Duration>& __tp,
const local_info& __i)
{
-#if 1
- return "local time is ambiguous";
-#else
std::ostringstream __os;
__os << __tp << " is ambiguous. It could be\n"
<< __tp << ' ' << __i.first.abbrev << " == "
@@ -2581,7 +2565,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
<< __tp << ' ' << __i.second.abbrev << " == "
<< __tp - __i.second.offset << " UTC";
return std::move(__os).str();
-#endif
}
};
@@ -3329,106 +3312,15 @@ namespace __detail
/// @}
} // 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/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
index dcff021d9d4..a0bb03173a9 100644
--- a/libstdc++-v3/src/c++20/tzdb.cc
+++ b/libstdc++-v3/src/c++20/tzdb.cc
@@ -511,27 +511,25 @@ namespace std::chrono
friend ostream& operator<<(ostream& out, const Rule& r)
{
out << "Rule " << r.name << ' ' << (int)r.from << ' ' << (int)r.to
- << ' ' << (unsigned)r.when.day.get_month() << ' ';
+ << ' ' << r.when.day.get_month() << ' ';
switch (r.when.day.kind)
{
case on_day::DayOfMonth:
out << (unsigned)r.when.day.get_day();
break;
case on_day::LastWeekday:
- out << "last" << weekday(r.when.day.day_of_week).c_encoding();
+ out << "last" << weekday(r.when.day.day_of_week);
break;
case on_day::LessEq:
- out << weekday(r.when.day.day_of_week).c_encoding() << " <= "
+ out << weekday(r.when.day.day_of_week) << " <= "
<< r.when.day.day_of_month;
break;
case on_day::GreaterEq:
- out << weekday(r.when.day.day_of_week).c_encoding() << " >= "
+ out << weekday(r.when.day.day_of_week) << " >= "
<< r.when.day.day_of_month;
break;
}
- hh_mm_ss hms(r.when.time);
- out << ' ' << hms.hours().count() << ':' << hms.minutes().count()
- << ':' << hms.seconds().count() << "wusd"[r.when.indicator];
+ out << ' ' << hh_mm_ss(r.when.time) << "wusd"[r.when.indicator];
out << ' ' << r.save.count();
if (!r.letters.empty())
out << ' ' << r.letters;
diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc
index 405e1afa440..27586b54392 100644
--- a/libstdc++-v3/testsuite/20_util/duration/io.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/io.cc
@@ -47,8 +47,56 @@ test02()
#endif
}
+void
+test_format()
+{
+ using namespace std::chrono_literals;
+ auto s = std::format("{} {}", 1h + 23min + 45s, -42min);
+ VERIFY( s == "5025s -42min" );
+ s = std::format("{:%j} {:%j} {:%j}", 1h + 23min + 45s, 75h, -99h);
+ VERIFY( s == "0 3 -4" );
+ s = std::format("{:%T = %H:%M:%S}", 1h + 23min + 45s);
+ VERIFY( s == "01:23:45 = 01:23:45" );
+ s = std::format("{:%Q} {:%q} {:%Q%q}", 6min + 1s, 44min, -22h);
+ VERIFY( s == "361 min -22h" );
+
+ std::wstring ws = std::format(L"{:%Q%q}", 81s);
+ VERIFY( ws == L"81s" );
+
+ // Only print '-' on numeric fields for negative durations:
+ s = std::format("{:%Q} {:%q} {:%q%Q}", -21h, -20h, -19h);
+ VERIFY( s == "-21 h h-19" );
+ s = std::format("{:%p} {:%p%H}", -2h, -13h);
+ VERIFY( s == "AM PM-13" );
+ s = std::format("{:%t} {:%t%M}", -2h, -123s);
+ VERIFY( s == "\t \t-02" );
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "HIjMpqQrRSTX";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(1s));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
int main()
{
test01();
test02();
+ test_format();
+ // TODO: test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/file/io.cc b/libstdc++-v3/testsuite/std/time/clock/file/io.cc
new file mode 100644
index 00000000000..c8e82bb111c
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/file/io.cc
@@ -0,0 +1,23 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using namespace std::chrono;
+
+ file_time<file_clock::duration> t = file_clock::now();
+ std::ostringstream ss1, ss2;
+ ss1 << floor<seconds>(t);
+ ss2 << floor<seconds>(clock_cast<system_clock>(t));
+ VERIFY( ss1.str() == ss2.str() );
+}
+
+int main()
+{
+ test_ostream();
+}
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..e7feebc9a3c
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/system/io.cc
@@ -0,0 +1,72 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ 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; };
+
+// operator<<(ostream&, const sys_time<D>&) is constrained to not
+// allow floating-point types or periods of days or greater.
+using fp_sys_time = std::chrono::sys_time<std::chrono::duration<float>>;
+static_assert( !stream_insertable<fp_sys_time> );
+
+// But there is an overload for sys_days.
+static_assert( stream_insertable<std::chrono::sys_days> );
+
+void
+test_format()
+{
+ using namespace std::chrono_literals;
+ std::chrono::sys_time<std::chrono::milliseconds> t(1671470785708ms);
+
+ // Every conversion specifier is valid for a sys_time except %q and %Q.
+
+ std::string s = std::format("{:%a | %A | %b | %B | %c"
+ " | %C | %d | %D | %e | %F | %g | %G | %h"
+ " | %H | %I | %j | %m | %M | %p | %r | %R"
+ " | %S | %T | %u | %U | %V | %w | %W | %x"
+ " | %X | %y | %Y | %z | %Z}", t);
+ VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+ " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+ " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+ " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+ " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" );
+
+ std::wstring ws = std::format(L"{:%a | %A | %b | %B | %c"
+ " | %C | %d | %D | %e | %F | %g | %G | %h"
+ " | %H | %I | %j | %m | %M | %p | %r | %R"
+ " | %S | %T | %u | %U | %V | %w | %W | %x"
+ " | %X | %y | %Y | %z | %Z}", t);
+ VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+ " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+ " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+ " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+ " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" );
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+}
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..b327c7f50c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
@@ -0,0 +1,120 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ 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;
+ }
+}
+
+void
+test_format()
+{
+ using namespace std::chrono_literals;
+ std::chrono::utc_time<std::chrono::milliseconds> t(1671470812708ms);
+
+ // Every conversion specifier is valid for a utc_time except %q and %Q.
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view badspecs = "qQ";
+
+ std::ostringstream ss;
+ std::wostringstream wss;
+
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ ss << std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(t));
+ ss << " | ";
+ // The call above should throw for any conversion-spec in badspecs:
+ VERIFY(badspecs.find(c) == badspecs.npos);
+
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(badspecs.find(c) != badspecs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+
+ wchar_t wfmt[] = { L'{', L':', L'%', c, L'}' };
+ try
+ {
+ wss << std::vformat(std::wstring_view(wfmt, 5),
+ std::make_wformat_args(t));
+ wss << L" | ";
+ // The call above should throw for any conversion-spec in badspecs:
+ VERIFY(badspecs.find(c) == badspecs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(badspecs.find(c) != badspecs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+
+ std::string s = ss.str();
+ VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+ " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+ " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+ " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+ " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " );
+
+ std::wstring ws = wss.str();
+ VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+ " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+ " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+ " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+ " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " );
+
+ std::chrono::utc_seconds leap(1483228800s + 26s); // 1 Jan 2017
+ s = std::format("{:%T}", leap - 1s);
+ VERIFY( s == "23:59:59" );
+ s = std::format("{:%T}", leap);
+ VERIFY( s == "23:59:60" );
+ s = std::format("{:%T}", leap + 10ms);
+ VERIFY( s == "23:59:60.010" );
+
+ s = std::format("{:%T}", leap + 1s);
+ VERIFY( s == "00:00:00" );
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+}
diff --git a/libstdc++-v3/testsuite/std/time/day/io.cc b/libstdc++-v3/testsuite/std/time/day/io.cc
new file mode 100644
index 00000000000..6158230f288
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/day/io.cc
@@ -0,0 +1,75 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "fr_FR.ISO8859-15" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ ostringstream ss;
+ ss << day(1) << ' ' << day(11) << ' ' << day(21) << ' ' << day(31)
+ << ' ' << day(41);
+ auto s = ss.str();
+ VERIFY( s == "01 11 21 31 41 is not a valid day" );
+
+ ss.str("");
+ ss.imbue(std::locale(ISO_8859(15,fr_FR)));
+ ss << day(1);
+ VERIFY( ss.str() == "01" );
+}
+
+void
+test_format()
+{
+ using std::chrono::day;
+
+ auto s = std::format("{:%d%%%e%t}{:%d%%%e%n}", day(1), day(11));
+ VERIFY( s == "01% 1\t11%11\n" );
+ auto ws = std::format(L"{:%d%%%e%t}{:%d%%%e%n}", day(1), day(11));
+ VERIFY( ws == L"01% 1\t11%11\n" );
+
+ VERIFY( std::format("{} {}", day(8), day(0)) == "08 00 is not a valid day" );
+
+ s = std::format("{:%Od}", day(1));
+ VERIFY( s == "01" );
+ s = std::format(std::locale::classic(), "{:%Od}", day(1));
+ VERIFY( s == "01" );
+ s = std::format(std::locale::classic(), "{:L%Od}", day(1));
+ VERIFY( s == "01" );
+ // TODO test "{:L%Od}" with locale that has alternative numeric rep.
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "de";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(day(1)));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+ // TODO: test_parse();
+}
diff --git a/libstdc++-v3/testsuite/std/time/exceptions.cc b/libstdc++-v3/testsuite/std/time/exceptions.cc
index 1b81d5ee27a..650b1fe7a37 100644
--- a/libstdc++-v3/testsuite/std/time/exceptions.cc
+++ b/libstdc++-v3/testsuite/std/time/exceptions.cc
@@ -20,7 +20,7 @@ test_nonexistent()
local_days{Sunday[2]/March/2016} + 2h + 30min};
VERIFY(false);
} catch (const nonexistent_local_time& e) {
- // VERIFY( e.what() == expected );
+ VERIFY( e.what() == expected );
}
}
@@ -38,7 +38,7 @@ test_ambiguous()
local_days{Sunday[1]/November/2016} + 1h + 30min};
VERIFY(false);
} catch (const ambiguous_local_time& e) {
- // VERIFY( e.what() == expected );
+ VERIFY( e.what() == expected );
}
}
diff --git a/libstdc++-v3/testsuite/std/time/format.cc b/libstdc++-v3/testsuite/std/time/format.cc
new file mode 100644
index 00000000000..b05e5da1af8
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format.cc
@@ -0,0 +1,117 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test_format_strings()
+{
+ using namespace std::chrono_literals;
+
+ // valid format strings
+ VERIFY( std::format("{}", 1s) == "1s" );
+ VERIFY( std::format("{:}", 1s) == "1s" );
+ VERIFY( std::format("{:L}", 1s) == "1s" );
+ VERIFY( std::format("{:%%%n%t}", 1s) == "%\n\t" );
+ VERIFY( std::format("{:L%%%n%t}", 1s) == "%\n\t" );
+ VERIFY( std::format("{:4%%}", 1s) == "% " );
+ VERIFY( std::format("{:4L%%}", 1s) == "% " );
+ VERIFY( std::format("{: >4}", 1s) == " 1s" );
+ VERIFY( std::format("{: <4}", 1s) == "1s " );
+ VERIFY( std::format("{: <4L}", 1s) == "1s " );
+ VERIFY( std::format("{: >4%%}", 1s) == " %" );
+ VERIFY( std::format("{: >4L%%}", 1s) == " %" );
+ VERIFY( std::format("{: ^4%%}", 1s) == " % " );
+}
+
+template<typename... Args>
+bool
+is_format_string_for(const char* str, Args&&... args)
+{
+ try {
+ (void) std::vformat(str, std::make_format_args(args...));
+ return true;
+ } catch (const std::format_error&) {
+ return false;
+ }
+}
+
+void
+test_bad_format_strings()
+{
+ std::chrono::sys_seconds t{};
+
+ // literal '%' must be formatted as "%%"
+ VERIFY( not is_format_string_for("{:%}", t) );
+
+ // chrono-specs must start with '%'
+ VERIFY( not is_format_string_for("{:a%}", t) );
+ VERIFY( not is_format_string_for("{:La%}", t) );
+
+ // '{' not valid in chrono-specs
+ VERIFY( not is_format_string_for("{:%%{{%%}", t) );
+
+ // padding with leading zero not valid for chrono types
+ VERIFY( not is_format_string_for("{:04%T}", t) );
+
+ // precision only valid for chrono::duration types with floating-point rep.
+ VERIFY( not is_format_string_for("{:.4}", t) );
+
+ // unfinished format string
+ VERIFY( not is_format_string_for("{:", t) );
+
+ // dangling modifiers
+ VERIFY( not is_format_string_for("{:%E}", t) );
+ VERIFY( not is_format_string_for("{:%O}", t) );
+
+ // modifier not valid for conversion specifier
+ VERIFY( not is_format_string_for("{:%Ea}", t) );
+ VERIFY( not is_format_string_for("{:%Oa}", t) );
+}
+
+template<typename I>
+struct move_only_iterator
+{
+ using iterator = I;
+ using value_type = iterator::value_type;
+ using difference_type = iterator::difference_type;
+ using iterator_category = std::output_iterator_tag;
+
+ move_only_iterator(iterator b) : base_(b) { }
+ move_only_iterator(move_only_iterator&&) = default;
+ move_only_iterator& operator=(move_only_iterator&&) = default;
+
+ move_only_iterator& operator++() { ++base_; return *this; }
+ move_only_iterator operator++(int) { auto tmp = *this; ++base_; return tmp; }
+
+ decltype(auto) operator*() { return *base_; }
+
+private:
+ iterator base_;
+};
+
+void
+test_move_only_iterator()
+{
+ using namespace std::chrono;
+ utc_seconds ut(1671543754s);
+ sys_seconds st(1671543727s);
+
+ std::string str;
+ move_only_iterator mo(std::back_inserter(str));
+ std::format_to(std::move(mo), "{:%F} {:%T} {:%Q}", ut, st, 1s);
+ VERIFY( str == "2022-12-20 13:42:07 1" );
+
+ std::vector<wchar_t> vec;
+ move_only_iterator wmo(std::back_inserter(vec));
+ std::format_to(std::move(wmo), L"{:%F} {:%T} {:%Q}", ut, st, 2s);
+ VERIFY( std::wstring_view(vec.data(), vec.size()) == L"2022-12-20 13:42:07 2" );
+}
+
+int main()
+{
+ test_format_strings();
+ test_bad_format_strings();
+ test_move_only_iterator();
+}
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();
+}
diff --git a/libstdc++-v3/testsuite/std/time/month/io.cc b/libstdc++-v3/testsuite/std/time/month/io.cc
new file mode 100644
index 00000000000..7ceeafd725a
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/month/io.cc
@@ -0,0 +1,98 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "fr_FR.ISO8859-15" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ ostringstream ss;
+ for (int i = 1; i <= 12; ++i)
+ ss << month(i);
+ VERIFY( ss.str() == "JanFebMarAprMayJunJulAugSepOctNovDec" );
+ ss.str("");
+ ss << month(0) << '|' << month(13);
+ VERIFY( ss.str() == "0 is not a valid month|13 is not a valid month" );
+
+ ss.str("");
+ ss.imbue(std::locale(ISO_8859(15,fr_FR)));
+ ss << month(1);
+ VERIFY( ss.str() == "janv." );
+}
+
+void
+test_format()
+{
+ using std::chrono::month;
+
+ auto s = std::format("{:%b%%%B%t%m%n}", month(1));
+ VERIFY( s == "Jan%January\t01\n" );
+ auto ws = std::format(L"{:%b%%%B%t%m%n}", month(12));
+ VERIFY( ws == L"Dec%December\t12\n" );
+
+ try
+ {
+ (void) std::format("{:%b}", month(13));
+ VERIFY(false);
+ }
+ catch (const std::format_error&)
+ {
+ }
+
+ s = std::format("{} is OK, but {:L}", month(2), month(13));
+ VERIFY( s == "Feb is OK, but 13 is not a valid month" );
+
+ std::locale loc_fr(ISO_8859(15,fr_FR));
+
+ s = std::format("{:%Om}", month(1));
+ VERIFY( s == "01" );
+ s = std::format(std::locale::classic(), "{:%Om}", month(1));
+ VERIFY( s == "01" );
+ s = std::format(std::locale::classic(), "{:L%Om}", month(1));
+ VERIFY( s == "01" );
+ s = std::format(loc_fr, "{:%Om}", month(1));
+ VERIFY( s == "01" );
+ s = std::format(loc_fr, "{:L%Om}", month(1));
+ VERIFY( s == "01" );
+ // TODO test "{:L%Om}" with locale that has alternative numeric rep.
+
+ s = std::format(loc_fr, "{:%b}", month(1));
+ VERIFY( s == "Jan" );
+ s = std::format(loc_fr, "{:L%b}", month(1));
+ VERIFY( s == "janv." );
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "bBhm";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(month(1)));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+ // TODO: test_parse();
+}
diff --git a/libstdc++-v3/testsuite/std/time/syn_c++20.cc b/libstdc++-v3/testsuite/std/time/syn_c++20.cc
index c91723bebd6..ad6f58bc9a1 100644
--- a/libstdc++-v3/testsuite/std/time/syn_c++20.cc
+++ b/libstdc++-v3/testsuite/std/time/syn_c++20.cc
@@ -124,8 +124,7 @@ namespace __gnu_test
using std::chrono::time_zone_link;
#endif
- // FIXME
- // using std::chrono::local_time_format;
+ using std::chrono::local_time_format;
// FIXME
// using std::chrono::parse;
diff --git a/libstdc++-v3/testsuite/std/time/time_zone/get_info_local.cc b/libstdc++-v3/testsuite/std/time/time_zone/get_info_local.cc
index d15e1c7036e..46ca34b71fd 100644
--- a/libstdc++-v3/testsuite/std/time/time_zone/get_info_local.cc
+++ b/libstdc++-v3/testsuite/std/time/time_zone/get_info_local.cc
@@ -148,7 +148,6 @@ test_egypt()
VERIFY( info.second.save == 1h );
VERIFY( info.second.abbrev == "EEST" );
-#if 0
std::ostringstream out;
local_seconds lt(local_days(2001y/January/1));
const local_days end(2021y/January/1);
@@ -209,7 +208,6 @@ test_egypt()
[[2014-09-25 21:00:00,32767-12-31 00:00:00,02:00:00,0min,EET]]
)";
VERIFY( out.str() == expected );
-#endif
}
int main()
diff --git a/libstdc++-v3/testsuite/std/time/weekday/io.cc b/libstdc++-v3/testsuite/std/time/weekday/io.cc
new file mode 100644
index 00000000000..6cdb98467b1
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/weekday/io.cc
@@ -0,0 +1,101 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "fr_FR.ISO8859-15" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ ostringstream ss;
+ for (int i = 0; i <= 7; ++i)
+ ss << weekday(i);
+ VERIFY( ss.str() == "SunMonTueWedThuFriSatSun" );
+ ss.str("");
+ ss << weekday(8) << '|' << weekday(99);
+ VERIFY( ss.str() == "8 is not a valid weekday|99 is not a valid weekday" );
+
+ ss.str("");
+ ss.imbue(std::locale(ISO_8859(15,fr_FR)));
+ ss << weekday(6);
+ VERIFY( ss.str() == "sam." );
+}
+
+void
+test_format()
+{
+ using std::chrono::weekday;
+
+ auto s = std::format("{:%a%%%A%t%u%n%w}", std::chrono::Monday);
+ VERIFY( s == "Mon%Monday\t1\n1" );
+ auto ws = std::format(L"{:%a%%%A%t%u%n%w}", weekday(7));
+ VERIFY( ws == L"Sun%Sunday\t7\n0" );
+
+ s = std::format("{:%w}", weekday(8));
+ VERIFY( s == "8" );
+
+ try
+ {
+ (void) std::format("{:%a}", weekday(8));
+ VERIFY(false);
+ }
+ catch (const std::format_error&)
+ {
+ }
+
+ s = std::format("{} is OK, but {:L}", weekday(2), weekday(13));
+ VERIFY( s == "Tue is OK, but 13 is not a valid weekday" );
+
+ std::locale loc_fr(ISO_8859(15,fr_FR));
+
+ s = std::format("{:%Ow}", weekday(1));
+ VERIFY( s == "1" );
+ s = std::format(std::locale::classic(), "{:%Ow}", weekday(1));
+ VERIFY( s == "1" );
+ s = std::format(std::locale::classic(), "{:L%Ow}", weekday(1));
+ VERIFY( s == "1" );
+ s = std::format(loc_fr, "{:%Ow}", weekday(1));
+ VERIFY( s == "1" );
+ s = std::format(loc_fr, "{:L%Ow}", weekday(1));
+ VERIFY( s == "1" );
+ // TODO test "{:L%Ow}" with locale that has alternative numeric rep.
+
+ s = std::format(loc_fr, "{:%a}", weekday(1));
+ VERIFY( s == "Mon" );
+ s = std::format(loc_fr, "{:L%a}", weekday(1));
+ VERIFY( s == "lun." );
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "aAuw";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(weekday(1)));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+ // TODO: test_parse();
+}
diff --git a/libstdc++-v3/testsuite/std/time/year/io.cc b/libstdc++-v3/testsuite/std/time/year/io.cc
new file mode 100644
index 00000000000..07316e98aa5
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/year/io.cc
@@ -0,0 +1,89 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "fr_FR.ISO8859-15" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ ostringstream ss;
+ for (int y : {-1234, -44, -1, 0, 5, 32, 325, 1066, 2022})
+ ss << year(y) << ' ';
+ VERIFY( ss.str() == "-1234 -0044 -0001 0000 0005 0032 0325 1066 2022 " );
+ ss.str("");
+ ss << year::min() << ' ' << year::max() << ' ' << --year::min();
+ VERIFY( ss.str() == "-32767 32767 -32768 is not a valid year" );
+
+ ss.str("");
+ ss.imbue(std::locale(ISO_8859(15,fr_FR)));
+ ss << 1789y;
+ VERIFY( ss.str() == "1789" );
+}
+
+void
+test_format()
+{
+ using std::chrono::year;
+ using namespace std::chrono_literals;
+
+ auto s = std::format("{:%y%%%Y%t%C%n}", 2022y);
+ VERIFY( s == "22%2022\t20\n" );
+ auto ws = std::format(L"{:%y%%%Y%t%C%n}", 2023y);
+ VERIFY( ws == L"23%2023\t20\n" );
+
+ s = std::format("{:%Y}", --year::min());
+ VERIFY( s == "-32768" );
+
+ s = std::format("{}", --year::min()); // formatted via ostream
+ VERIFY( s == "-32768 is not a valid year" );
+
+ s = std::format("{:%y} {:%y}", 1976y, -1976y);
+ VERIFY( s == "76 76" ); // LWG 3831
+
+ s = std::format("{0:%EC}{0:%Ey} = {0:%EY}", 1642y);
+ VERIFY( s == "1642 = 1642" );
+ s = std::format("{0:L%EC}{0:L%Ey} = {0:L%EY}", 1642y);
+ VERIFY( s == "1642 = 1642" );
+ s = std::format(std::locale::classic(), "{0:L%EC}{0:L%Ey} = {0:L%EY}", 1642y);
+ VERIFY( s == "1642 = 1642" );
+
+ // TODO test "{:L%EC}" with locale that has alternative era rep.
+ // TODO test "{:L%Ey}" with locale that has alternative year rep.
+ // TODO test "{:L%EY}" with locale that has alternative year rep.
+ // TODO test "{:L%Oy}" with locale that has alternative numeric rep.
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "CyY";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(year(2022)));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+ // TODO: test_parse();
+}
diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
new file mode 100644
index 00000000000..688885b37a1
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
@@ -0,0 +1,121 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "fr_FR.ISO8859-15" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::ostringstream;
+ using namespace std::chrono;
+
+ ostringstream ss;
+ ss << 2022y/December/19 << ' ' << 2022y/November/31;
+ VERIFY( ss.str() == "2022-12-19 2022-11-31 is not a valid date" );
+
+ ss.str("");
+ ss.imbue(std::locale(ISO_8859(15,fr_FR)));
+ ss << 1789y/July/14;
+ VERIFY( ss.str() == "1789-07-14" );
+}
+
+void
+test_format()
+{
+ using std::chrono::year_month_day;
+ using std::chrono::December;
+ using std::chrono::January;
+ using namespace std::chrono_literals;
+
+ auto s = std::format("{:%y%%%Y%t%C%n%j %a %b}", 2022y/December/19);
+ VERIFY( s == "22%2022\t20\n353 Mon Dec" );
+ auto ws = std::format(L"{:%y%%%Y%t%C%n%d}", 2023y/January/32);
+ VERIFY( ws == L"23%2023\t20\n32" );
+
+ s = std::format("{:%F} {}", 2023y/January/32, 2023y/January/32);
+ VERIFY( s == "2023-01-32 2023-01-32 is not a valid date" );
+
+ s = std::format("{:%C%g-W%V-%u}", 2022y/January/1);
+ VERIFY( s == "2021-W52-6" );
+ s = std::format("{:%G-W%V-%u}", 2022y/January/3);
+ VERIFY( s == "2022-W01-1" );
+
+ // %U: Week number for weeks starting on Sunday
+ s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/January/1);
+ VERIFY( s == "Day 6 (Sat) of Week 00 of 2022" );
+ s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/January/2);
+ VERIFY( s == "Day 0 (Sun) of Week 01 of 2022" );
+ // %W: Week number for weeks starting on Monday
+ s = std::format("Day {:%u (%a) of Week %W of %Y}", 2022y/January/2);
+ VERIFY( s == "Day 7 (Sun) of Week 00 of 2022" );
+ s = std::format("Day {:%u (%a) of Week %W of %Y}", 2022y/January/3);
+ VERIFY( s == "Day 1 (Mon) of Week 01 of 2022" );
+
+ // %V: ISO week number (ISO 8601).
+ s = std::format("W{:%V}", 1977y/1/1);
+ VERIFY( s == "W53" );
+ s = std::format("W{:%V}", 1977y/1/2);
+ VERIFY( s == "W53" );
+ s = std::format("W{:%V}", 1977y/12/31);
+ VERIFY( s == "W52" );
+ s = std::format("W{:%V}", 1978y/1/1);
+ VERIFY( s == "W52" );
+ s = std::format("W{:%V}", 1978y/1/2);
+ VERIFY( s == "W01" );
+ s = std::format("W{:%V}", 1978y/12/31);
+ VERIFY( s == "W52" );
+ s = std::format("W{:%V}", 1979y/1/1);
+ VERIFY( s == "W01" );
+ s = std::format("W{:%V}", 1979y/12/30);
+ VERIFY( s == "W52" );
+ s = std::format("W{:%V}", 1979y/12/31);
+ VERIFY( s == "W01" );
+ s = std::format("W{:%V}", 1980y/1/1);
+ VERIFY( s == "W01" );
+
+ s = std::format("{:%x}", 2022y/December/19);
+ VERIFY( s == "12/19/22" );
+ s = std::format("{:L%x}", 2022y/December/19);
+ VERIFY( s == "12/19/22" );
+ std::locale loc_fr(ISO_8859(15,fr_FR));
+ s = std::format(loc_fr, "{:%x}", 2022y/December/19);
+ VERIFY( s == "12/19/22" );
+ s = std::format(loc_fr, "{:L%x}", 2022y/December/19);
+ VERIFY( s == "19/12/2022" );
+ s = std::format(loc_fr, "{}", 2022y/December/19);
+ VERIFY( s == "2022-12-19" );
+ s = std::format(loc_fr, "{:L%F}", 2022y/December/19);
+ VERIFY( s == "2022-12-19" );
+
+ std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
+ std::string_view my_specs = "aAbBCdDeFgGhjmuUVwWxyY";
+ for (char c : specs)
+ {
+ char fmt[] = { '{', ':', '%', c, '}' };
+ try
+ {
+ (void) std::vformat(std::string_view(fmt, 5),
+ std::make_format_args(2022y/December/19));
+ // The call above should throw for any conversion-spec not in my_specs:
+ VERIFY(my_specs.find(c) != my_specs.npos);
+ }
+ catch (const std::format_error& e)
+ {
+ VERIFY(my_specs.find(c) == my_specs.npos);
+ std::string_view s = e.what();
+ // Libstdc++-specific message:
+ VERIFY(s.find("format argument does not contain the information "
+ "required by the chrono-specs") != s.npos);
+ }
+ }
+}
+
+int main()
+{
+ test_ostream();
+ test_format();
+ // TODO: test_parse();
+}
More information about the Libstdc++-cvs
mailing list