xref: /llvm-project/libcxx/include/__chrono/formatter.h (revision 3b30f20c60d020e43f5700dae68cf1080158b725)
1e5d2d3eaSMark de Wever // -*- C++ -*-
2e5d2d3eaSMark de Wever //===----------------------------------------------------------------------===//
3e5d2d3eaSMark de Wever //
4e5d2d3eaSMark de Wever // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5e5d2d3eaSMark de Wever // See https://llvm.org/LICENSE.txt for license information.
6e5d2d3eaSMark de Wever // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7e5d2d3eaSMark de Wever //
8e5d2d3eaSMark de Wever //===----------------------------------------------------------------------===//
9e5d2d3eaSMark de Wever 
10e5d2d3eaSMark de Wever #ifndef _LIBCPP___CHRONO_FORMATTER_H
11e5d2d3eaSMark de Wever #define _LIBCPP___CHRONO_FORMATTER_H
12e5d2d3eaSMark de Wever 
1387d56c59SLouis Dionne #include <__config>
1487d56c59SLouis Dionne 
15c6f3b7bcSNikolas Klauser #if _LIBCPP_HAS_LOCALIZATION
1687d56c59SLouis Dionne 
17a6fcbcceSMark de Wever #  include <__algorithm/ranges_copy.h>
18105fef5dSMark de Wever #  include <__chrono/calendar.h>
197f5d130aSMark de Wever #  include <__chrono/concepts.h>
20e5d2d3eaSMark de Wever #  include <__chrono/convert_to_tm.h>
21e5d2d3eaSMark de Wever #  include <__chrono/day.h>
22719c3dc6SMark de Wever #  include <__chrono/duration.h>
2396f30332SMark de Wever #  include <__chrono/file_clock.h>
24719c3dc6SMark de Wever #  include <__chrono/hh_mm_ss.h>
258a21d59fSMark de Wever #  include <__chrono/local_info.h>
261522f190SMark de Wever #  include <__chrono/month.h>
27105fef5dSMark de Wever #  include <__chrono/month_weekday.h>
28105fef5dSMark de Wever #  include <__chrono/monthday.h>
29719c3dc6SMark de Wever #  include <__chrono/ostream.h>
30e5d2d3eaSMark de Wever #  include <__chrono/parser_std_format_spec.h>
313eb4f16bSMark de Wever #  include <__chrono/statically_widen.h>
326f7976c8SMark de Wever #  include <__chrono/sys_info.h>
332c1d7959SMark de Wever #  include <__chrono/system_clock.h>
34719c3dc6SMark de Wever #  include <__chrono/time_point.h>
350cd794d4SMark de Wever #  include <__chrono/utc_clock.h>
36566868cdSMark de Wever #  include <__chrono/weekday.h>
373eb4f16bSMark de Wever #  include <__chrono/year.h>
38105fef5dSMark de Wever #  include <__chrono/year_month.h>
39105fef5dSMark de Wever #  include <__chrono/year_month_day.h>
40105fef5dSMark de Wever #  include <__chrono/year_month_weekday.h>
41afbfb16dSMark de Wever #  include <__chrono/zoned_time.h>
42719c3dc6SMark de Wever #  include <__concepts/arithmetic.h>
431522f190SMark de Wever #  include <__concepts/same_as.h>
44e5d2d3eaSMark de Wever #  include <__format/concepts.h>
451522f190SMark de Wever #  include <__format/format_error.h>
463eb4f16bSMark de Wever #  include <__format/format_functions.h>
47e5d2d3eaSMark de Wever #  include <__format/format_parse_context.h>
48e5d2d3eaSMark de Wever #  include <__format/formatter.h>
49e5d2d3eaSMark de Wever #  include <__format/parser_std_format_spec.h>
503ab20c68SMark de Wever #  include <__format/write_escaped.h>
513eb4f16bSMark de Wever #  include <__memory/addressof.h>
52afbfb16dSMark de Wever #  include <__type_traits/is_specialization.h>
533eb4f16bSMark de Wever #  include <cmath>
54e5d2d3eaSMark de Wever #  include <ctime>
556c97ad4eSMark de Wever #  include <limits>
5609e3a360SLouis Dionne #  include <locale>
57e5d2d3eaSMark de Wever #  include <sstream>
58e5d2d3eaSMark de Wever #  include <string_view>
59e5d2d3eaSMark de Wever 
60e5d2d3eaSMark de Wever #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
61e5d2d3eaSMark de Wever #    pragma GCC system_header
62e5d2d3eaSMark de Wever #  endif
63e5d2d3eaSMark de Wever 
64e5d2d3eaSMark de Wever _LIBCPP_BEGIN_NAMESPACE_STD
65e5d2d3eaSMark de Wever 
66dff62f52SMark de Wever #  if _LIBCPP_STD_VER >= 20
67e5d2d3eaSMark de Wever 
68e5d2d3eaSMark de Wever namespace __formatter {
69e5d2d3eaSMark de Wever 
70e5d2d3eaSMark de Wever /// Formats a time based on a tm struct.
71e5d2d3eaSMark de Wever ///
72e5d2d3eaSMark de Wever /// This formatter passes the formatting to time_put which uses strftime. When
73e5d2d3eaSMark de Wever /// the value is outside the valid range it's unspecified what strftime will
74e5d2d3eaSMark de Wever /// output. For example weekday 8 can print 1 when the day is processed modulo
75e5d2d3eaSMark de Wever /// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if
76e5d2d3eaSMark de Wever /// 7 is handled as a special case.
77e5d2d3eaSMark de Wever ///
78e5d2d3eaSMark de Wever /// The Standard doesn't specify what to do in this case so the result depends
79e5d2d3eaSMark de Wever /// on the result of the underlying code.
80e5d2d3eaSMark de Wever ///
81e5d2d3eaSMark de Wever /// \pre When the (abbreviated) weekday or month name are used, the caller
82e5d2d3eaSMark de Wever ///      validates whether the value is valid. So the caller handles that
83e5d2d3eaSMark de Wever ///      requirement of Table 97: Meaning of conversion specifiers
84e5d2d3eaSMark de Wever ///      [tab:time.format.spec].
85e5d2d3eaSMark de Wever ///
86e5d2d3eaSMark de Wever /// When no chrono-specs are provided it uses the stream formatter.
87e5d2d3eaSMark de Wever 
88719c3dc6SMark de Wever // For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This
89719c3dc6SMark de Wever // fails compile-time due to the limited precision of the ratio (64-bit is too
90719c3dc6SMark de Wever // small). Therefore a duration uses its own conversion.
917f5d130aSMark de Wever template <class _CharT, class _Rep, class _Period>
927f5d130aSMark de Wever _LIBCPP_HIDE_FROM_ABI void
935462b270SMark de Wever __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::duration<_Rep, _Period>& __value) {
94719c3dc6SMark de Wever   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
95719c3dc6SMark de Wever 
967f5d130aSMark de Wever   using __duration = chrono::duration<_Rep, _Period>;
977f5d130aSMark de Wever 
98719c3dc6SMark de Wever   auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
99579d3010SMark de Wever   // Converts a negative fraction to its positive value.
100579d3010SMark de Wever   if (__value < chrono::seconds{0} && __fraction != __duration{0})
101579d3010SMark de Wever     __fraction += chrono::seconds{1};
1027f5d130aSMark de Wever   if constexpr (chrono::treat_as_floating_point_v<_Rep>)
103719c3dc6SMark de Wever     // When the floating-point value has digits itself they are ignored based
104719c3dc6SMark de Wever     // on the wording in [tab:time.format.spec]
105719c3dc6SMark de Wever     //   If the precision of the input cannot be exactly represented with
106719c3dc6SMark de Wever     //   seconds, then the format is a decimal floating-point number with a
107719c3dc6SMark de Wever     //   fixed format and a precision matching that of the precision of the
108719c3dc6SMark de Wever     //   input (or to a microseconds precision if the conversion to
109719c3dc6SMark de Wever     //   floating-point decimal seconds cannot be made within 18 fractional
110719c3dc6SMark de Wever     //   digits).
111719c3dc6SMark de Wever     //
112719c3dc6SMark de Wever     // This matches the behaviour of MSVC STL, fmtlib interprets this
113719c3dc6SMark de Wever     // differently and uses 3 decimals.
114719c3dc6SMark de Wever     // https://godbolt.org/z/6dsbnW8ba
115719c3dc6SMark de Wever     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
116719c3dc6SMark de Wever                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
11728584755SMark de Wever                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
1187f5d130aSMark de Wever                    chrono::hh_mm_ss<__duration>::fractional_width);
119719c3dc6SMark de Wever   else
120719c3dc6SMark de Wever     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
121719c3dc6SMark de Wever                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
12228584755SMark de Wever                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
1237f5d130aSMark de Wever                    chrono::hh_mm_ss<__duration>::fractional_width);
1247f5d130aSMark de Wever }
1257f5d130aSMark de Wever 
1262c1d7959SMark de Wever template <class _CharT, __is_time_point _Tp>
1275462b270SMark de Wever _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const _Tp& __value) {
1285462b270SMark de Wever   __formatter::__format_sub_seconds(__sstr, __value.time_since_epoch());
1292c1d7959SMark de Wever }
1302c1d7959SMark de Wever 
1317f5d130aSMark de Wever template <class _CharT, class _Duration>
1327f5d130aSMark de Wever _LIBCPP_HIDE_FROM_ABI void
1335462b270SMark de Wever __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<_Duration>& __value) {
1347f5d130aSMark de Wever   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
1357f5d130aSMark de Wever   if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)
1367f5d130aSMark de Wever     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
1377f5d130aSMark de Wever                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
1387f5d130aSMark de Wever                    __value.subseconds().count(),
1397f5d130aSMark de Wever                    __value.fractional_width);
1407f5d130aSMark de Wever   else
1417f5d130aSMark de Wever     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
1427f5d130aSMark de Wever                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
1437f5d130aSMark de Wever                    __value.subseconds().count(),
1447f5d130aSMark de Wever                    __value.fractional_width);
145719c3dc6SMark de Wever }
146719c3dc6SMark de Wever 
14724e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
148afbfb16dSMark de Wever template <class _CharT, class _Duration, class _TimeZonePtr>
149afbfb16dSMark de Wever _LIBCPP_HIDE_FROM_ABI void
150afbfb16dSMark de Wever __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
151afbfb16dSMark de Wever   __formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
152afbfb16dSMark de Wever }
153afbfb16dSMark de Wever #    endif
154afbfb16dSMark de Wever 
155719c3dc6SMark de Wever template <class _Tp>
156719c3dc6SMark de Wever consteval bool __use_fraction() {
1572c1d7959SMark de Wever   if constexpr (__is_time_point<_Tp>)
1582c1d7959SMark de Wever     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
15924e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
160afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
161afbfb16dSMark de Wever     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
162afbfb16dSMark de Wever #    endif
1637c010bfdSNikolas Klauser   else if constexpr (chrono::__is_duration_v<_Tp>)
164719c3dc6SMark de Wever     return chrono::hh_mm_ss<_Tp>::fractional_width;
1657f5d130aSMark de Wever   else if constexpr (__is_hh_mm_ss<_Tp>)
1667f5d130aSMark de Wever     return _Tp::fractional_width;
167719c3dc6SMark de Wever   else
168719c3dc6SMark de Wever     return false;
169719c3dc6SMark de Wever }
170719c3dc6SMark de Wever 
1713eb4f16bSMark de Wever template <class _CharT>
1725462b270SMark de Wever _LIBCPP_HIDE_FROM_ABI void __format_year(basic_stringstream<_CharT>& __sstr, int __year) {
1733eb4f16bSMark de Wever   if (__year < 0) {
1743eb4f16bSMark de Wever     __sstr << _CharT('-');
1753eb4f16bSMark de Wever     __year = -__year;
1763eb4f16bSMark de Wever   }
1773eb4f16bSMark de Wever 
1783eb4f16bSMark de Wever   // TODO FMT Write an issue
1793eb4f16bSMark de Wever   //   If the result has less than four digits it is zero-padded with 0 to two digits.
1803eb4f16bSMark de Wever   // is less -> has less
1813eb4f16bSMark de Wever   // left-padded -> zero-padded, otherwise the proper value would be 000-0.
1823eb4f16bSMark de Wever 
1833eb4f16bSMark de Wever   // Note according to the wording it should be left padded, which is odd.
1843eb4f16bSMark de Wever   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);
1853eb4f16bSMark de Wever }
1863eb4f16bSMark de Wever 
1873eb4f16bSMark de Wever template <class _CharT>
1885462b270SMark de Wever _LIBCPP_HIDE_FROM_ABI void __format_century(basic_stringstream<_CharT>& __sstr, int __year) {
1893eb4f16bSMark de Wever   // TODO FMT Write an issue
1903eb4f16bSMark de Wever   // [tab:time.format.spec]
1913eb4f16bSMark de Wever   //   %C The year divided by 100 using floored division. If the result is a
1923eb4f16bSMark de Wever   //   single decimal digit, it is prefixed with 0.
1933eb4f16bSMark de Wever 
1943eb4f16bSMark de Wever   bool __negative = __year < 0;
1953eb4f16bSMark de Wever   int __century   = (__year - (99 * __negative)) / 100; // floored division
1963eb4f16bSMark de Wever   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);
1973eb4f16bSMark de Wever }
1983eb4f16bSMark de Wever 
199a6fcbcceSMark de Wever // Implements the %z format specifier according to [tab:time.format.spec], where
200a6fcbcceSMark de Wever // '__modifier' signals %Oz or %Ez were used. (Both modifiers behave the same,
201a6fcbcceSMark de Wever // so there is no need to distinguish between them.)
202a6fcbcceSMark de Wever template <class _CharT>
203a6fcbcceSMark de Wever _LIBCPP_HIDE_FROM_ABI void
204a6fcbcceSMark de Wever __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offset, bool __modifier) {
205a6fcbcceSMark de Wever   if (__offset < 0s) {
206a6fcbcceSMark de Wever     __sstr << _CharT('-');
207a6fcbcceSMark de Wever     __offset = -__offset;
208a6fcbcceSMark de Wever   } else {
209a6fcbcceSMark de Wever     __sstr << _CharT('+');
210a6fcbcceSMark de Wever   }
211a6fcbcceSMark de Wever 
212a6fcbcceSMark de Wever   chrono::hh_mm_ss __hms{__offset};
213a6fcbcceSMark de Wever   std::ostreambuf_iterator<_CharT> __out_it{__sstr};
2146f7976c8SMark de Wever   // Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.
2156f7976c8SMark de Wever   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());
216a6fcbcceSMark de Wever   if (__modifier)
2176f7976c8SMark de Wever     __sstr << _CharT(':');
2186f7976c8SMark de Wever   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());
219a6fcbcceSMark de Wever }
220a6fcbcceSMark de Wever 
221a6fcbcceSMark de Wever // Helper to store the time zone information needed for formatting.
222a6fcbcceSMark de Wever struct _LIBCPP_HIDE_FROM_ABI __time_zone {
223a6fcbcceSMark de Wever   // Typically these abbreviations are short and fit in the string's internal
224a6fcbcceSMark de Wever   // buffer.
225a6fcbcceSMark de Wever   string __abbrev;
226a6fcbcceSMark de Wever   chrono::seconds __offset;
227a6fcbcceSMark de Wever };
228a6fcbcceSMark de Wever 
229a6fcbcceSMark de Wever template <class _Tp>
230a6fcbcceSMark de Wever _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
23124e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
2326f7976c8SMark de Wever   if constexpr (same_as<_Tp, chrono::sys_info>)
2336f7976c8SMark de Wever     return {__value.abbrev, __value.offset};
234c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
235afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
236afbfb16dSMark de Wever     return __formatter::__convert_to_time_zone(__value.get_info());
2376f7976c8SMark de Wever #      endif
238afbfb16dSMark de Wever   else
23924e70e39SNikolas Klauser #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
240a6fcbcceSMark de Wever     return {"UTC", chrono::seconds{0}};
241a6fcbcceSMark de Wever }
242a6fcbcceSMark de Wever 
243e5d2d3eaSMark de Wever template <class _CharT, class _Tp>
244e5d2d3eaSMark de Wever _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
2455462b270SMark de Wever     basic_stringstream<_CharT>& __sstr, const _Tp& __value, basic_string_view<_CharT> __chrono_specs) {
246d529e811SLouis Dionne   tm __t              = std::__convert_to_tm<tm>(__value);
247a6fcbcceSMark de Wever   __time_zone __z     = __formatter::__convert_to_time_zone(__value);
248e5d2d3eaSMark de Wever   const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc());
249e5d2d3eaSMark de Wever   for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) {
250e5d2d3eaSMark de Wever     if (*__it == _CharT('%')) {
251e5d2d3eaSMark de Wever       auto __s = __it;
252e5d2d3eaSMark de Wever       ++__it;
253e5d2d3eaSMark de Wever       // We only handle the types that can't be directly handled by time_put.
254e5d2d3eaSMark de Wever       // (as an optimization n, t, and % are also handled directly.)
255e5d2d3eaSMark de Wever       switch (*__it) {
256e5d2d3eaSMark de Wever       case _CharT('n'):
257e5d2d3eaSMark de Wever         __sstr << _CharT('\n');
258e5d2d3eaSMark de Wever         break;
259e5d2d3eaSMark de Wever       case _CharT('t'):
260e5d2d3eaSMark de Wever         __sstr << _CharT('\t');
261e5d2d3eaSMark de Wever         break;
262e5d2d3eaSMark de Wever       case _CharT('%'):
263e5d2d3eaSMark de Wever         __sstr << *__it;
264e5d2d3eaSMark de Wever         break;
265e5d2d3eaSMark de Wever 
2663eb4f16bSMark de Wever       case _CharT('C'): {
2673eb4f16bSMark de Wever         // strftime's output is only defined in the range [00, 99].
2683eb4f16bSMark de Wever         int __year = __t.tm_year + 1900;
2693eb4f16bSMark de Wever         if (__year < 1000 || __year > 9999)
2705462b270SMark de Wever           __formatter::__format_century(__sstr, __year);
2713eb4f16bSMark de Wever         else
2729783f28cSLouis Dionne           __facet.put(
2739783f28cSLouis Dionne               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
2743eb4f16bSMark de Wever       } break;
2753eb4f16bSMark de Wever 
276719c3dc6SMark de Wever       case _CharT('j'):
2777c010bfdSNikolas Klauser         if constexpr (chrono::__is_duration_v<_Tp>)
278719c3dc6SMark de Wever           // Converting a duration where the period has a small ratio to days
279719c3dc6SMark de Wever           // may fail to compile. This due to loss of precision in the
280719c3dc6SMark de Wever           // conversion. In order to avoid that issue convert to seconds as
281719c3dc6SMark de Wever           // an intemediate step.
282719c3dc6SMark de Wever           __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();
283719c3dc6SMark de Wever         else
2849783f28cSLouis Dionne           __facet.put(
2859783f28cSLouis Dionne               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
286719c3dc6SMark de Wever         break;
287719c3dc6SMark de Wever 
288719c3dc6SMark de Wever       case _CharT('q'):
2897c010bfdSNikolas Klauser         if constexpr (chrono::__is_duration_v<_Tp>) {
290719c3dc6SMark de Wever           __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();
291719c3dc6SMark de Wever           break;
292719c3dc6SMark de Wever         }
293719c3dc6SMark de Wever         __builtin_unreachable();
294719c3dc6SMark de Wever 
295719c3dc6SMark de Wever       case _CharT('Q'):
296719c3dc6SMark de Wever         // TODO FMT Determine the proper ideas
297719c3dc6SMark de Wever         // - Should it honour the precision?
298719c3dc6SMark de Wever         // - Shoult it honour the locale setting for the separators?
299719c3dc6SMark de Wever         // The wording for Q doesn't use the word locale and the effect of
300719c3dc6SMark de Wever         // precision is unspecified.
301719c3dc6SMark de Wever         //
302719c3dc6SMark de Wever         // MSVC STL ignores precision but uses separator
303719c3dc6SMark de Wever         // FMT honours precision and has a bug for separator
304719c3dc6SMark de Wever         // https://godbolt.org/z/78b7sMxns
3057c010bfdSNikolas Klauser         if constexpr (chrono::__is_duration_v<_Tp>) {
306841399a2SNikolas Klauser           __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());
307719c3dc6SMark de Wever           break;
308719c3dc6SMark de Wever         }
309719c3dc6SMark de Wever         __builtin_unreachable();
310719c3dc6SMark de Wever 
311719c3dc6SMark de Wever       case _CharT('S'):
312719c3dc6SMark de Wever       case _CharT('T'):
3139783f28cSLouis Dionne         __facet.put(
3149783f28cSLouis Dionne             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
315719c3dc6SMark de Wever         if constexpr (__use_fraction<_Tp>())
3165462b270SMark de Wever           __formatter::__format_sub_seconds(__sstr, __value);
317719c3dc6SMark de Wever         break;
318719c3dc6SMark de Wever 
3193eb4f16bSMark de Wever         // Unlike time_put and strftime the formatting library requires %Y
3203eb4f16bSMark de Wever         //
3213eb4f16bSMark de Wever         // [tab:time.format.spec]
3223eb4f16bSMark de Wever         //   The year as a decimal number. If the result is less than four digits
3233eb4f16bSMark de Wever         //   it is left-padded with 0 to four digits.
3243eb4f16bSMark de Wever         //
3253eb4f16bSMark de Wever         // This means years in the range (-1000, 1000) need manual formatting.
3263eb4f16bSMark de Wever         // It's unclear whether %EY needs the same treatment. For example the
3273eb4f16bSMark de Wever         // Japanese EY contains the era name and year. This is zero-padded to 2
3283eb4f16bSMark de Wever         // digits in time_put (note that older glibc versions didn't do
3293eb4f16bSMark de Wever         // padding.) However most eras won't reach 100 years, let alone 1000.
3303eb4f16bSMark de Wever         // So padding to 4 digits seems unwanted for Japanese.
3313eb4f16bSMark de Wever         //
3323eb4f16bSMark de Wever         // The same applies to %Ex since that too depends on the era.
3333eb4f16bSMark de Wever         //
3343eb4f16bSMark de Wever         // %x the locale's date representation is currently doesn't handle the
3353eb4f16bSMark de Wever         // zero-padding too.
3363eb4f16bSMark de Wever         //
3373eb4f16bSMark de Wever         // The 4 digits can be implemented better at a later time. On POSIX
3383eb4f16bSMark de Wever         // systems the required information can be extracted by nl_langinfo
3393eb4f16bSMark de Wever         // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
3403eb4f16bSMark de Wever         //
3413eb4f16bSMark de Wever         // Note since year < -1000 is expected to be rare it uses the more
3423eb4f16bSMark de Wever         // expensive year routine.
3433eb4f16bSMark de Wever         //
3443eb4f16bSMark de Wever         // TODO FMT evaluate the comment above.
3453eb4f16bSMark de Wever 
3460ee73debSMark de Wever #    if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
3473eb4f16bSMark de Wever       case _CharT('y'):
3483eb4f16bSMark de Wever         // Glibc fails for negative values, AIX for positive values too.
3493eb4f16bSMark de Wever         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);
3503eb4f16bSMark de Wever         break;
3510ee73debSMark de Wever #    endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
3523eb4f16bSMark de Wever 
3530ee73debSMark de Wever       case _CharT('Y'):
3540ee73debSMark de Wever         // Depending on the platform's libc the range of supported years is
355d6fc7d3aSJay Foad         // limited. Instead of of testing all conditions use the internal
3560ee73debSMark de Wever         // implementation unconditionally.
3575462b270SMark de Wever         __formatter::__format_year(__sstr, __t.tm_year + 1900);
3580ee73debSMark de Wever         break;
3593eb4f16bSMark de Wever 
360812963f6SMark de Wever       case _CharT('F'):
361812963f6SMark de Wever         // Depending on the platform's libc the range of supported years is
362812963f6SMark de Wever         // limited. Instead of testing all conditions use the internal
363812963f6SMark de Wever         // implementation unconditionally.
364812963f6SMark de Wever         __formatter::__format_year(__sstr, __t.tm_year + 1900);
365105fef5dSMark de Wever         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
366812963f6SMark de Wever         break;
367105fef5dSMark de Wever 
368a6fcbcceSMark de Wever       case _CharT('z'):
369a6fcbcceSMark de Wever         __formatter::__format_zone_offset(__sstr, __z.__offset, false);
370a6fcbcceSMark de Wever         break;
371a6fcbcceSMark de Wever 
3722c1d7959SMark de Wever       case _CharT('Z'):
373a6fcbcceSMark de Wever         // __abbrev is always a char so the copy may convert.
374a6fcbcceSMark de Wever         ranges::copy(__z.__abbrev, std::ostreambuf_iterator<_CharT>{__sstr});
3752c1d7959SMark de Wever         break;
3762c1d7959SMark de Wever 
377e5d2d3eaSMark de Wever       case _CharT('O'):
378719c3dc6SMark de Wever         if constexpr (__use_fraction<_Tp>()) {
379719c3dc6SMark de Wever           // Handle OS using the normal representation for the non-fractional
380719c3dc6SMark de Wever           // part. There seems to be no locale information regarding how the
381719c3dc6SMark de Wever           // fractional part should be formatted.
382719c3dc6SMark de Wever           if (*(__it + 1) == 'S') {
383719c3dc6SMark de Wever             ++__it;
3849783f28cSLouis Dionne             __facet.put(
3859783f28cSLouis Dionne                 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
3865462b270SMark de Wever             __formatter::__format_sub_seconds(__sstr, __value);
387719c3dc6SMark de Wever             break;
388719c3dc6SMark de Wever           }
389719c3dc6SMark de Wever         }
390a6fcbcceSMark de Wever 
391a6fcbcceSMark de Wever         // Oz produces the same output as Ez below.
392719c3dc6SMark de Wever         [[fallthrough]];
3933eb4f16bSMark de Wever       case _CharT('E'):
394e5d2d3eaSMark de Wever         ++__it;
395a6fcbcceSMark de Wever         if (*__it == 'z') {
396a6fcbcceSMark de Wever           __formatter::__format_zone_offset(__sstr, __z.__offset, true);
397a6fcbcceSMark de Wever           break;
398a6fcbcceSMark de Wever         }
399e5d2d3eaSMark de Wever         [[fallthrough]];
400e5d2d3eaSMark de Wever       default:
4019783f28cSLouis Dionne         __facet.put(
4029783f28cSLouis Dionne             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
403e5d2d3eaSMark de Wever         break;
404e5d2d3eaSMark de Wever       }
405e5d2d3eaSMark de Wever     } else {
406e5d2d3eaSMark de Wever       __sstr << *__it;
407e5d2d3eaSMark de Wever     }
408e5d2d3eaSMark de Wever   }
409e5d2d3eaSMark de Wever }
410e5d2d3eaSMark de Wever 
4111522f190SMark de Wever template <class _Tp>
412105fef5dSMark de Wever _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
4132c1d7959SMark de Wever   if constexpr (__is_time_point<_Tp>)
4142c1d7959SMark de Wever     return true;
4152c1d7959SMark de Wever   else if constexpr (same_as<_Tp, chrono::day>)
4161522f190SMark de Wever     return true;
4171522f190SMark de Wever   else if constexpr (same_as<_Tp, chrono::month>)
4181522f190SMark de Wever     return __value.ok();
4191522f190SMark de Wever   else if constexpr (same_as<_Tp, chrono::year>)
4201522f190SMark de Wever     return true;
421566868cdSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday>)
422566868cdSMark de Wever     return true;
423105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
424105fef5dSMark de Wever     return true;
425105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_last>)
426105fef5dSMark de Wever     return true;
427105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day>)
428105fef5dSMark de Wever     return true;
429105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day_last>)
430105fef5dSMark de Wever     return true;
431105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday>)
432105fef5dSMark de Wever     return true;
433105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
434105fef5dSMark de Wever     return true;
435105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month>)
436105fef5dSMark de Wever     return true;
437105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day>)
438105fef5dSMark de Wever     return __value.ok();
439105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
440105fef5dSMark de Wever     return __value.ok();
441105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
442105fef5dSMark de Wever     return __value.weekday().ok();
443105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
444105fef5dSMark de Wever     return __value.weekday().ok();
4457f5d130aSMark de Wever   else if constexpr (__is_hh_mm_ss<_Tp>)
4467f5d130aSMark de Wever     return true;
44724e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
4486f7976c8SMark de Wever   else if constexpr (same_as<_Tp, chrono::sys_info>)
4496f7976c8SMark de Wever     return true;
4508a21d59fSMark de Wever   else if constexpr (same_as<_Tp, chrono::local_info>)
4518a21d59fSMark de Wever     return true;
452c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
453afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
454afbfb16dSMark de Wever     return true;
4556f7976c8SMark de Wever #      endif
45624e70e39SNikolas Klauser #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
457566868cdSMark de Wever   else
458566868cdSMark de Wever     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
459566868cdSMark de Wever }
460566868cdSMark de Wever 
461566868cdSMark de Wever template <class _Tp>
462566868cdSMark de Wever _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
4632c1d7959SMark de Wever   if constexpr (__is_time_point<_Tp>)
4642c1d7959SMark de Wever     return true;
4652c1d7959SMark de Wever   else if constexpr (same_as<_Tp, chrono::day>)
466566868cdSMark de Wever     return true;
467566868cdSMark de Wever   else if constexpr (same_as<_Tp, chrono::month>)
468566868cdSMark de Wever     return __value.ok();
469566868cdSMark de Wever   else if constexpr (same_as<_Tp, chrono::year>)
470566868cdSMark de Wever     return true;
471566868cdSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday>)
472566868cdSMark de Wever     return __value.ok();
473105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
474105fef5dSMark de Wever     return __value.weekday().ok();
475105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_last>)
476105fef5dSMark de Wever     return __value.weekday().ok();
477105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day>)
478105fef5dSMark de Wever     return true;
479105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day_last>)
480105fef5dSMark de Wever     return true;
481105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday>)
482105fef5dSMark de Wever     return __value.weekday_indexed().ok();
483105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
484105fef5dSMark de Wever     return __value.weekday_indexed().ok();
485105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month>)
486105fef5dSMark de Wever     return true;
487105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day>)
488105fef5dSMark de Wever     return __value.ok();
489105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
490105fef5dSMark de Wever     return __value.ok();
491105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
492105fef5dSMark de Wever     return __value.weekday().ok();
493105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
494105fef5dSMark de Wever     return __value.weekday().ok();
4957f5d130aSMark de Wever   else if constexpr (__is_hh_mm_ss<_Tp>)
4967f5d130aSMark de Wever     return true;
49724e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
4986f7976c8SMark de Wever   else if constexpr (same_as<_Tp, chrono::sys_info>)
4996f7976c8SMark de Wever     return true;
5008a21d59fSMark de Wever   else if constexpr (same_as<_Tp, chrono::local_info>)
5018a21d59fSMark de Wever     return true;
502c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
503afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
504afbfb16dSMark de Wever     return true;
5056f7976c8SMark de Wever #      endif
50624e70e39SNikolas Klauser #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
507105fef5dSMark de Wever   else
508105fef5dSMark de Wever     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
509105fef5dSMark de Wever }
510105fef5dSMark de Wever 
511105fef5dSMark de Wever template <class _Tp>
512105fef5dSMark de Wever _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
5132c1d7959SMark de Wever   if constexpr (__is_time_point<_Tp>)
5142c1d7959SMark de Wever     return true;
5152c1d7959SMark de Wever   else if constexpr (same_as<_Tp, chrono::day>)
516105fef5dSMark de Wever     return true;
517105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month>)
518105fef5dSMark de Wever     return __value.ok();
519105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year>)
520105fef5dSMark de Wever     return true;
521105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday>)
522105fef5dSMark de Wever     return true;
523105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
524105fef5dSMark de Wever     return true;
525105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_last>)
526105fef5dSMark de Wever     return true;
527105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day>)
528105fef5dSMark de Wever     return true;
529105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day_last>)
530105fef5dSMark de Wever     return true;
531105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday>)
532105fef5dSMark de Wever     return true;
533105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
534105fef5dSMark de Wever     return true;
535105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month>)
536105fef5dSMark de Wever     return true;
537105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day>)
538105fef5dSMark de Wever     return __value.ok();
539105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
540105fef5dSMark de Wever     return __value.ok();
541105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
542105fef5dSMark de Wever     return __value.ok();
543105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
544105fef5dSMark de Wever     return __value.ok();
5457f5d130aSMark de Wever   else if constexpr (__is_hh_mm_ss<_Tp>)
5467f5d130aSMark de Wever     return true;
54724e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
5486f7976c8SMark de Wever   else if constexpr (same_as<_Tp, chrono::sys_info>)
5496f7976c8SMark de Wever     return true;
5508a21d59fSMark de Wever   else if constexpr (same_as<_Tp, chrono::local_info>)
5518a21d59fSMark de Wever     return true;
552c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
553afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
554afbfb16dSMark de Wever     return true;
5556f7976c8SMark de Wever #      endif
55624e70e39SNikolas Klauser #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
557105fef5dSMark de Wever   else
558105fef5dSMark de Wever     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
559105fef5dSMark de Wever }
560105fef5dSMark de Wever 
561105fef5dSMark de Wever template <class _Tp>
562105fef5dSMark de Wever _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
5632c1d7959SMark de Wever   if constexpr (__is_time_point<_Tp>)
5642c1d7959SMark de Wever     return true;
5652c1d7959SMark de Wever   else if constexpr (same_as<_Tp, chrono::day>)
566105fef5dSMark de Wever     return true;
567105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month>)
568105fef5dSMark de Wever     return __value.ok();
569105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year>)
570105fef5dSMark de Wever     return true;
571105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday>)
572105fef5dSMark de Wever     return true;
573105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
574105fef5dSMark de Wever     return true;
575105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::weekday_last>)
576105fef5dSMark de Wever     return true;
577105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day>)
578105fef5dSMark de Wever     return __value.month().ok();
579105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_day_last>)
580105fef5dSMark de Wever     return __value.month().ok();
581105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday>)
582105fef5dSMark de Wever     return __value.month().ok();
583105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
584105fef5dSMark de Wever     return __value.month().ok();
585105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month>)
586105fef5dSMark de Wever     return __value.month().ok();
587105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day>)
588105fef5dSMark de Wever     return __value.month().ok();
589105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
590105fef5dSMark de Wever     return __value.month().ok();
591105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
592105fef5dSMark de Wever     return __value.month().ok();
593105fef5dSMark de Wever   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
594105fef5dSMark de Wever     return __value.month().ok();
5957f5d130aSMark de Wever   else if constexpr (__is_hh_mm_ss<_Tp>)
5967f5d130aSMark de Wever     return true;
59724e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
5986f7976c8SMark de Wever   else if constexpr (same_as<_Tp, chrono::sys_info>)
5996f7976c8SMark de Wever     return true;
6008a21d59fSMark de Wever   else if constexpr (same_as<_Tp, chrono::local_info>)
6018a21d59fSMark de Wever     return true;
602c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
603afbfb16dSMark de Wever   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
604afbfb16dSMark de Wever     return true;
6056f7976c8SMark de Wever #      endif
60624e70e39SNikolas Klauser #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
6071522f190SMark de Wever   else
6081522f190SMark de Wever     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
6091522f190SMark de Wever }
6101522f190SMark de Wever 
611b51e8acdSMark de Wever template <class _CharT, class _Tp, class _FormatContext>
612e5d2d3eaSMark de Wever _LIBCPP_HIDE_FROM_ABI auto
613e5d2d3eaSMark de Wever __format_chrono(const _Tp& __value,
614b51e8acdSMark de Wever                 _FormatContext& __ctx,
615e5d2d3eaSMark de Wever                 __format_spec::__parsed_specifications<_CharT> __specs,
616b51e8acdSMark de Wever                 basic_string_view<_CharT> __chrono_specs) {
617e5d2d3eaSMark de Wever   basic_stringstream<_CharT> __sstr;
618e5d2d3eaSMark de Wever   // [time.format]/2
619e5d2d3eaSMark de Wever   // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise
620e5d2d3eaSMark de Wever   // 2.2 - the locale passed to the formatting function if any, otherwise
621e5d2d3eaSMark de Wever   // 2.3 - the global locale.
622e5d2d3eaSMark de Wever   // Note that the __ctx's locale() call does 2.2 and 2.3.
623e5d2d3eaSMark de Wever   if (__specs.__chrono_.__locale_specific_form_)
624e5d2d3eaSMark de Wever     __sstr.imbue(__ctx.locale());
625e5d2d3eaSMark de Wever   else
626e5d2d3eaSMark de Wever     __sstr.imbue(locale::classic());
627e5d2d3eaSMark de Wever 
628e5d2d3eaSMark de Wever   if (__chrono_specs.empty())
629e5d2d3eaSMark de Wever     __sstr << __value;
6301522f190SMark de Wever   else {
6317c010bfdSNikolas Klauser     if constexpr (chrono::__is_duration_v<_Tp>) {
6326c97ad4eSMark de Wever       // A duration can be a user defined arithmetic type. Users may specialize
6336c97ad4eSMark de Wever       // numeric_limits, but they may not specialize is_signed.
6346c97ad4eSMark de Wever       if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {
6356c97ad4eSMark de Wever         if (__value < __value.zero()) {
636719c3dc6SMark de Wever           __sstr << _CharT('-');
6376c97ad4eSMark de Wever           __formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);
6386c97ad4eSMark de Wever         } else
6396c97ad4eSMark de Wever           __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
6406c97ad4eSMark de Wever       } else
6416c97ad4eSMark de Wever         __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
642719c3dc6SMark de Wever       // TODO FMT When keeping the precision it will truncate the string.
643719c3dc6SMark de Wever       // Note that the behaviour what the precision does isn't specified.
644719c3dc6SMark de Wever       __specs.__precision_ = -1;
645719c3dc6SMark de Wever     } else {
646105fef5dSMark de Wever       // Test __weekday_name_ before __weekday_ to give a better error.
647566868cdSMark de Wever       if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value))
648402eb2efSMark de Wever         std::__throw_format_error("Formatting a weekday name needs a valid weekday");
649566868cdSMark de Wever 
650105fef5dSMark de Wever       if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))
651402eb2efSMark de Wever         std::__throw_format_error("Formatting a weekday needs a valid weekday");
652105fef5dSMark de Wever 
653105fef5dSMark de Wever       if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))
654402eb2efSMark de Wever         std::__throw_format_error("Formatting a day of year needs a valid date");
655105fef5dSMark de Wever 
656105fef5dSMark de Wever       if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value))
657402eb2efSMark de Wever         std::__throw_format_error("Formatting a week of year needs a valid date");
658105fef5dSMark de Wever 
6591522f190SMark de Wever       if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
660402eb2efSMark de Wever         std::__throw_format_error("Formatting a month name from an invalid month number");
6611522f190SMark de Wever 
6627f5d130aSMark de Wever       if constexpr (__is_hh_mm_ss<_Tp>) {
6637f5d130aSMark de Wever         // Note this is a pedantic intepretation of the Standard. A hh_mm_ss
6647f5d130aSMark de Wever         // is no longer a time_of_day and can store an arbitrary number of
6657f5d130aSMark de Wever         // hours. A number of hours in a 12 or 24 hour clock can't represent
6667f5d130aSMark de Wever         // 24 hours or more. The functions std::chrono::make12 and
6677f5d130aSMark de Wever         // std::chrono::make24 reaffirm this view point.
6687f5d130aSMark de Wever         //
6697f5d130aSMark de Wever         // Interestingly this will be the only output stream function that
6707f5d130aSMark de Wever         // throws.
6717f5d130aSMark de Wever         //
6727f5d130aSMark de Wever         // TODO FMT The wording probably needs to be adapted to
6737f5d130aSMark de Wever         // - The displayed hours is hh_mm_ss.hours() % 24
6747f5d130aSMark de Wever         // - It should probably allow %j in the same fashion as duration.
6757f5d130aSMark de Wever         // - The stream formatter should change its output when hours >= 24
6767f5d130aSMark de Wever         //   - Write it as not valid,
6777f5d130aSMark de Wever         //   - or write the number of days.
6787f5d130aSMark de Wever         if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)
679402eb2efSMark de Wever           std::__throw_format_error("Formatting a hour needs a valid value");
6807f5d130aSMark de Wever 
6817f5d130aSMark de Wever         if (__value.is_negative())
6827f5d130aSMark de Wever           __sstr << _CharT('-');
6837f5d130aSMark de Wever       }
6847f5d130aSMark de Wever 
6855462b270SMark de Wever       __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
6861522f190SMark de Wever     }
687719c3dc6SMark de Wever   }
688e5d2d3eaSMark de Wever 
689ac834983SMark de Wever   return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs);
690e5d2d3eaSMark de Wever }
691e5d2d3eaSMark de Wever 
692e5d2d3eaSMark de Wever } // namespace __formatter
693e5d2d3eaSMark de Wever 
694e5d2d3eaSMark de Wever template <__fmt_char_type _CharT>
6953d334df5SLouis Dionne struct _LIBCPP_TEMPLATE_VIS __formatter_chrono {
696e5d2d3eaSMark de Wever public:
697b51e8acdSMark de Wever   template <class _ParseContext>
698b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator
699b51e8acdSMark de Wever   __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) {
700b51e8acdSMark de Wever     return __parser_.__parse(__ctx, __fields, __flags);
701e5d2d3eaSMark de Wever   }
702e5d2d3eaSMark de Wever 
703b51e8acdSMark de Wever   template <class _Tp, class _FormatContext>
704b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const {
705e5d2d3eaSMark de Wever     return __formatter::__format_chrono(
706e5d2d3eaSMark de Wever         __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_);
707e5d2d3eaSMark de Wever   }
708e5d2d3eaSMark de Wever 
709e5d2d3eaSMark de Wever   __format_spec::__parser_chrono<_CharT> __parser_;
710e5d2d3eaSMark de Wever };
711e5d2d3eaSMark de Wever 
7122c1d7959SMark de Wever template <class _Duration, __fmt_char_type _CharT>
7132c1d7959SMark de Wever struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
7142c1d7959SMark de Wever public:
715f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
7162c1d7959SMark de Wever 
717b51e8acdSMark de Wever   template <class _ParseContext>
718b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
719b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
7202c1d7959SMark de Wever   }
7212c1d7959SMark de Wever };
7222c1d7959SMark de Wever 
7230cd794d4SMark de Wever #    if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
724*3b30f20cSMark de Wever #      if _LIBCPP_HAS_EXPERIMENTAL_TZDB
7250cd794d4SMark de Wever 
7260cd794d4SMark de Wever template <class _Duration, __fmt_char_type _CharT>
7270cd794d4SMark de Wever struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
7280cd794d4SMark de Wever public:
7290cd794d4SMark de Wever   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
7300cd794d4SMark de Wever 
7310cd794d4SMark de Wever   template <class _ParseContext>
7320cd794d4SMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
7330cd794d4SMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
7340cd794d4SMark de Wever   }
7350cd794d4SMark de Wever };
7360cd794d4SMark de Wever 
737*3b30f20cSMark de Wever #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
7380cd794d4SMark de Wever #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
7390cd794d4SMark de Wever 
74096f30332SMark de Wever template <class _Duration, __fmt_char_type _CharT>
74196f30332SMark de Wever struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
74296f30332SMark de Wever public:
743f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
74496f30332SMark de Wever 
745b51e8acdSMark de Wever   template <class _ParseContext>
746b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
747b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
74896f30332SMark de Wever   }
74996f30332SMark de Wever };
75096f30332SMark de Wever 
751bc2cf420SMark de Wever template <class _Duration, __fmt_char_type _CharT>
752bc2cf420SMark de Wever struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
753bc2cf420SMark de Wever public:
754f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
755bc2cf420SMark de Wever 
756b51e8acdSMark de Wever   template <class _ParseContext>
757b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
758bc2cf420SMark de Wever     // The flags are not __clock since there is no associated time-zone.
759b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time);
760bc2cf420SMark de Wever   }
761bc2cf420SMark de Wever };
762bc2cf420SMark de Wever 
763719c3dc6SMark de Wever template <class _Rep, class _Period, __fmt_char_type _CharT>
764719c3dc6SMark de Wever struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {
765719c3dc6SMark de Wever public:
766f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
767719c3dc6SMark de Wever 
768b51e8acdSMark de Wever   template <class _ParseContext>
769b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
770719c3dc6SMark de Wever     // [time.format]/1
771719c3dc6SMark de Wever     // Giving a precision specification in the chrono-format-spec is valid only
772719c3dc6SMark de Wever     // for std::chrono::duration types where the representation type Rep is a
773719c3dc6SMark de Wever     // floating-point type. For all other Rep types, an exception of type
774719c3dc6SMark de Wever     // format_error is thrown if the chrono-format-spec contains a precision
775719c3dc6SMark de Wever     // specification.
776719c3dc6SMark de Wever     //
777719c3dc6SMark de Wever     // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.
778719c3dc6SMark de Wever     if constexpr (std::floating_point<_Rep>)
779b51e8acdSMark de Wever       return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);
780719c3dc6SMark de Wever     else
781b51e8acdSMark de Wever       return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);
782719c3dc6SMark de Wever   }
783719c3dc6SMark de Wever };
784719c3dc6SMark de Wever 
785e5d2d3eaSMark de Wever template <__fmt_char_type _CharT>
7869783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> {
787e5d2d3eaSMark de Wever public:
788f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
789e5d2d3eaSMark de Wever 
790b51e8acdSMark de Wever   template <class _ParseContext>
791b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
792b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day);
793e5d2d3eaSMark de Wever   }
794e5d2d3eaSMark de Wever };
795e5d2d3eaSMark de Wever 
7963eb4f16bSMark de Wever template <__fmt_char_type _CharT>
7979783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> {
7981522f190SMark de Wever public:
799f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
8001522f190SMark de Wever 
801b51e8acdSMark de Wever   template <class _ParseContext>
802b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
803b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
8041522f190SMark de Wever   }
8051522f190SMark de Wever };
8061522f190SMark de Wever 
8071522f190SMark de Wever template <__fmt_char_type _CharT>
8089783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> {
8093eb4f16bSMark de Wever public:
810f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
8113eb4f16bSMark de Wever 
812b51e8acdSMark de Wever   template <class _ParseContext>
813b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
814b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);
8153eb4f16bSMark de Wever   }
8163eb4f16bSMark de Wever };
8173eb4f16bSMark de Wever 
818566868cdSMark de Wever template <__fmt_char_type _CharT>
8199783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> {
820566868cdSMark de Wever public:
821f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
822566868cdSMark de Wever 
823b51e8acdSMark de Wever   template <class _ParseContext>
824b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
825b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
826566868cdSMark de Wever   }
827566868cdSMark de Wever };
828566868cdSMark de Wever 
829105fef5dSMark de Wever template <__fmt_char_type _CharT>
8309783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> {
831105fef5dSMark de Wever public:
832f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
833105fef5dSMark de Wever 
834b51e8acdSMark de Wever   template <class _ParseContext>
835b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
836b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
837105fef5dSMark de Wever   }
838105fef5dSMark de Wever };
839105fef5dSMark de Wever 
840105fef5dSMark de Wever template <__fmt_char_type _CharT>
8419783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> {
842105fef5dSMark de Wever public:
843f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
844105fef5dSMark de Wever 
845b51e8acdSMark de Wever   template <class _ParseContext>
846b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
847b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
848105fef5dSMark de Wever   }
849105fef5dSMark de Wever };
850105fef5dSMark de Wever 
851105fef5dSMark de Wever template <__fmt_char_type _CharT>
8529783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> {
853105fef5dSMark de Wever public:
854f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
855105fef5dSMark de Wever 
856b51e8acdSMark de Wever   template <class _ParseContext>
857b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
858b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day);
859105fef5dSMark de Wever   }
860105fef5dSMark de Wever };
861105fef5dSMark de Wever 
862105fef5dSMark de Wever template <__fmt_char_type _CharT>
8639783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> {
864105fef5dSMark de Wever public:
865f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
866105fef5dSMark de Wever 
867b51e8acdSMark de Wever   template <class _ParseContext>
868b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
869b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
870105fef5dSMark de Wever   }
871105fef5dSMark de Wever };
872105fef5dSMark de Wever 
873105fef5dSMark de Wever template <__fmt_char_type _CharT>
8749783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> {
875105fef5dSMark de Wever public:
876f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
877105fef5dSMark de Wever 
878b51e8acdSMark de Wever   template <class _ParseContext>
879b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
880b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
881105fef5dSMark de Wever   }
882105fef5dSMark de Wever };
883105fef5dSMark de Wever 
884105fef5dSMark de Wever template <__fmt_char_type _CharT>
8859783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
886105fef5dSMark de Wever public:
887f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
888105fef5dSMark de Wever 
889b51e8acdSMark de Wever   template <class _ParseContext>
890b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
891b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
892105fef5dSMark de Wever   }
893105fef5dSMark de Wever };
894105fef5dSMark de Wever 
895105fef5dSMark de Wever template <__fmt_char_type _CharT>
8969783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> {
897105fef5dSMark de Wever public:
898f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
899105fef5dSMark de Wever 
900b51e8acdSMark de Wever   template <class _ParseContext>
901b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
902b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month);
903105fef5dSMark de Wever   }
904105fef5dSMark de Wever };
905105fef5dSMark de Wever 
906105fef5dSMark de Wever template <__fmt_char_type _CharT>
9079783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> {
908105fef5dSMark de Wever public:
909f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
910105fef5dSMark de Wever 
911b51e8acdSMark de Wever   template <class _ParseContext>
912b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
913b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
914105fef5dSMark de Wever   }
915105fef5dSMark de Wever };
916105fef5dSMark de Wever 
917105fef5dSMark de Wever template <__fmt_char_type _CharT>
9189783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> {
919105fef5dSMark de Wever public:
920f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
921105fef5dSMark de Wever 
922b51e8acdSMark de Wever   template <class _ParseContext>
923b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
924b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
925105fef5dSMark de Wever   }
926105fef5dSMark de Wever };
927105fef5dSMark de Wever 
928105fef5dSMark de Wever template <__fmt_char_type _CharT>
9299783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> {
930105fef5dSMark de Wever public:
931f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
932105fef5dSMark de Wever 
933b51e8acdSMark de Wever   template <class _ParseContext>
934b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
935b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
936105fef5dSMark de Wever   }
937105fef5dSMark de Wever };
938105fef5dSMark de Wever 
939105fef5dSMark de Wever template <__fmt_char_type _CharT>
9409783f28cSLouis Dionne struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
941105fef5dSMark de Wever public:
942f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
943105fef5dSMark de Wever 
944b51e8acdSMark de Wever   template <class _ParseContext>
945b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
946b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
947105fef5dSMark de Wever   }
948105fef5dSMark de Wever };
949105fef5dSMark de Wever 
9507f5d130aSMark de Wever template <class _Duration, __fmt_char_type _CharT>
9517f5d130aSMark de Wever struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
9527f5d130aSMark de Wever public:
953f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
9547f5d130aSMark de Wever 
955b51e8acdSMark de Wever   template <class _ParseContext>
956b51e8acdSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
957b51e8acdSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
9587f5d130aSMark de Wever   }
9597f5d130aSMark de Wever };
9606f7976c8SMark de Wever 
96124e70e39SNikolas Klauser #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
9626f7976c8SMark de Wever template <__fmt_char_type _CharT>
9636f7976c8SMark de Wever struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
9646f7976c8SMark de Wever public:
965f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
9666f7976c8SMark de Wever 
9676f7976c8SMark de Wever   template <class _ParseContext>
9686f7976c8SMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
9696f7976c8SMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
9706f7976c8SMark de Wever   }
9716f7976c8SMark de Wever };
9728a21d59fSMark de Wever 
9738a21d59fSMark de Wever template <__fmt_char_type _CharT>
9748a21d59fSMark de Wever struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {
9758a21d59fSMark de Wever public:
976f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
9778a21d59fSMark de Wever 
9788a21d59fSMark de Wever   template <class _ParseContext>
9798a21d59fSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
9808a21d59fSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
9818a21d59fSMark de Wever   }
9828a21d59fSMark de Wever };
983c6f3b7bcSNikolas Klauser #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
984afbfb16dSMark de Wever // Note due to how libc++'s formatters are implemented there is no need to add
985afbfb16dSMark de Wever // the exposition only local-time-format-t abstraction.
986afbfb16dSMark de Wever template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
987afbfb16dSMark de Wever struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
988afbfb16dSMark de Wever public:
989f6958523SNikolas Klauser   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
990afbfb16dSMark de Wever 
991afbfb16dSMark de Wever   template <class _ParseContext>
992afbfb16dSMark de Wever   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
993afbfb16dSMark de Wever     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
994afbfb16dSMark de Wever   }
995afbfb16dSMark de Wever };
996c6f3b7bcSNikolas Klauser #      endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
99724e70e39SNikolas Klauser #    endif   // _LIBCPP_HAS_EXPERIMENTAL_TZDB
9986f7976c8SMark de Wever 
999dff62f52SMark de Wever #  endif // if _LIBCPP_STD_VER >= 20
1000e5d2d3eaSMark de Wever 
1001e5d2d3eaSMark de Wever _LIBCPP_END_NAMESPACE_STD
1002e5d2d3eaSMark de Wever 
1003c6f3b7bcSNikolas Klauser #endif // _LIBCPP_HAS_LOCALIZATION
100487d56c59SLouis Dionne 
1005e5d2d3eaSMark de Wever #endif //  _LIBCPP___CHRONO_FORMATTER_H
1006