Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-03 08:13:24

0001 // -*- C++ -*-
0002 //===----------------------------------------------------------------------===//
0003 //
0004 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0005 // See https://llvm.org/LICENSE.txt for license information.
0006 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0007 //
0008 //===----------------------------------------------------------------------===//
0009 
0010 #ifndef _LIBCPP___CXX03___CHRONO_FORMATTER_H
0011 #define _LIBCPP___CXX03___CHRONO_FORMATTER_H
0012 
0013 #include <__cxx03/__algorithm/ranges_copy.h>
0014 #include <__cxx03/__chrono/calendar.h>
0015 #include <__cxx03/__chrono/concepts.h>
0016 #include <__cxx03/__chrono/convert_to_tm.h>
0017 #include <__cxx03/__chrono/day.h>
0018 #include <__cxx03/__chrono/duration.h>
0019 #include <__cxx03/__chrono/file_clock.h>
0020 #include <__cxx03/__chrono/hh_mm_ss.h>
0021 #include <__cxx03/__chrono/local_info.h>
0022 #include <__cxx03/__chrono/month.h>
0023 #include <__cxx03/__chrono/month_weekday.h>
0024 #include <__cxx03/__chrono/monthday.h>
0025 #include <__cxx03/__chrono/ostream.h>
0026 #include <__cxx03/__chrono/parser_std_format_spec.h>
0027 #include <__cxx03/__chrono/statically_widen.h>
0028 #include <__cxx03/__chrono/sys_info.h>
0029 #include <__cxx03/__chrono/system_clock.h>
0030 #include <__cxx03/__chrono/time_point.h>
0031 #include <__cxx03/__chrono/weekday.h>
0032 #include <__cxx03/__chrono/year.h>
0033 #include <__cxx03/__chrono/year_month.h>
0034 #include <__cxx03/__chrono/year_month_day.h>
0035 #include <__cxx03/__chrono/year_month_weekday.h>
0036 #include <__cxx03/__chrono/zoned_time.h>
0037 #include <__cxx03/__concepts/arithmetic.h>
0038 #include <__cxx03/__concepts/same_as.h>
0039 #include <__cxx03/__config>
0040 #include <__cxx03/__format/concepts.h>
0041 #include <__cxx03/__format/format_error.h>
0042 #include <__cxx03/__format/format_functions.h>
0043 #include <__cxx03/__format/format_parse_context.h>
0044 #include <__cxx03/__format/formatter.h>
0045 #include <__cxx03/__format/parser_std_format_spec.h>
0046 #include <__cxx03/__format/write_escaped.h>
0047 #include <__cxx03/__memory/addressof.h>
0048 #include <__cxx03/__type_traits/is_specialization.h>
0049 #include <__cxx03/cmath>
0050 #include <__cxx03/ctime>
0051 #include <__cxx03/limits>
0052 #include <__cxx03/sstream>
0053 #include <__cxx03/string_view>
0054 
0055 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
0056 #  pragma GCC system_header
0057 #endif
0058 
0059 _LIBCPP_BEGIN_NAMESPACE_STD
0060 
0061 #if _LIBCPP_STD_VER >= 20
0062 
0063 namespace __formatter {
0064 
0065 /// Formats a time based on a tm struct.
0066 ///
0067 /// This formatter passes the formatting to time_put which uses strftime. When
0068 /// the value is outside the valid range it's unspecified what strftime will
0069 /// output. For example weekday 8 can print 1 when the day is processed modulo
0070 /// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if
0071 /// 7 is handled as a special case.
0072 ///
0073 /// The Standard doesn't specify what to do in this case so the result depends
0074 /// on the result of the underlying code.
0075 ///
0076 /// \pre When the (abbreviated) weekday or month name are used, the caller
0077 ///      validates whether the value is valid. So the caller handles that
0078 ///      requirement of Table 97: Meaning of conversion specifiers
0079 ///      [tab:time.format.spec].
0080 ///
0081 /// When no chrono-specs are provided it uses the stream formatter.
0082 
0083 // For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This
0084 // fails compile-time due to the limited precision of the ratio (64-bit is too
0085 // small). Therefore a duration uses its own conversion.
0086 template <class _CharT, class _Rep, class _Period>
0087 _LIBCPP_HIDE_FROM_ABI void
0088 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::duration<_Rep, _Period>& __value) {
0089   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
0090 
0091   using __duration = chrono::duration<_Rep, _Period>;
0092 
0093   auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
0094   // Converts a negative fraction to its positive value.
0095   if (__value < chrono::seconds{0} && __fraction != __duration{0})
0096     __fraction += chrono::seconds{1};
0097   if constexpr (chrono::treat_as_floating_point_v<_Rep>)
0098     // When the floating-point value has digits itself they are ignored based
0099     // on the wording in [tab:time.format.spec]
0100     //   If the precision of the input cannot be exactly represented with
0101     //   seconds, then the format is a decimal floating-point number with a
0102     //   fixed format and a precision matching that of the precision of the
0103     //   input (or to a microseconds precision if the conversion to
0104     //   floating-point decimal seconds cannot be made within 18 fractional
0105     //   digits).
0106     //
0107     // This matches the behaviour of MSVC STL, fmtlib interprets this
0108     // differently and uses 3 decimals.
0109     // https://godbolt.org/z/6dsbnW8ba
0110     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
0111                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
0112                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
0113                    chrono::hh_mm_ss<__duration>::fractional_width);
0114   else
0115     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
0116                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
0117                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
0118                    chrono::hh_mm_ss<__duration>::fractional_width);
0119 }
0120 
0121 template <class _CharT, __is_time_point _Tp>
0122 _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const _Tp& __value) {
0123   __formatter::__format_sub_seconds(__sstr, __value.time_since_epoch());
0124 }
0125 
0126 template <class _CharT, class _Duration>
0127 _LIBCPP_HIDE_FROM_ABI void
0128 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<_Duration>& __value) {
0129   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
0130   if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)
0131     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
0132                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
0133                    __value.subseconds().count(),
0134                    __value.fractional_width);
0135   else
0136     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
0137                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
0138                    __value.subseconds().count(),
0139                    __value.fractional_width);
0140 }
0141 
0142 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) &&                     \
0143       !defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0144 template <class _CharT, class _Duration, class _TimeZonePtr>
0145 _LIBCPP_HIDE_FROM_ABI void
0146 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
0147   __formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
0148 }
0149 #  endif
0150 
0151 template <class _Tp>
0152 consteval bool __use_fraction() {
0153   if constexpr (__is_time_point<_Tp>)
0154     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
0155 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) &&                     \
0156       !defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0157   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0158     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
0159 #  endif
0160   else if constexpr (chrono::__is_duration<_Tp>::value)
0161     return chrono::hh_mm_ss<_Tp>::fractional_width;
0162   else if constexpr (__is_hh_mm_ss<_Tp>)
0163     return _Tp::fractional_width;
0164   else
0165     return false;
0166 }
0167 
0168 template <class _CharT>
0169 _LIBCPP_HIDE_FROM_ABI void __format_year(basic_stringstream<_CharT>& __sstr, int __year) {
0170   if (__year < 0) {
0171     __sstr << _CharT('-');
0172     __year = -__year;
0173   }
0174 
0175   // TODO FMT Write an issue
0176   //   If the result has less than four digits it is zero-padded with 0 to two digits.
0177   // is less -> has less
0178   // left-padded -> zero-padded, otherwise the proper value would be 000-0.
0179 
0180   // Note according to the wording it should be left padded, which is odd.
0181   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);
0182 }
0183 
0184 template <class _CharT>
0185 _LIBCPP_HIDE_FROM_ABI void __format_century(basic_stringstream<_CharT>& __sstr, int __year) {
0186   // TODO FMT Write an issue
0187   // [tab:time.format.spec]
0188   //   %C The year divided by 100 using floored division. If the result is a
0189   //   single decimal digit, it is prefixed with 0.
0190 
0191   bool __negative = __year < 0;
0192   int __century   = (__year - (99 * __negative)) / 100; // floored division
0193   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);
0194 }
0195 
0196 // Implements the %z format specifier according to [tab:time.format.spec], where
0197 // '__modifier' signals %Oz or %Ez were used. (Both modifiers behave the same,
0198 // so there is no need to distinguish between them.)
0199 template <class _CharT>
0200 _LIBCPP_HIDE_FROM_ABI void
0201 __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offset, bool __modifier) {
0202   if (__offset < 0s) {
0203     __sstr << _CharT('-');
0204     __offset = -__offset;
0205   } else {
0206     __sstr << _CharT('+');
0207   }
0208 
0209   chrono::hh_mm_ss __hms{__offset};
0210   std::ostreambuf_iterator<_CharT> __out_it{__sstr};
0211   // Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.
0212   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());
0213   if (__modifier)
0214     __sstr << _CharT(':');
0215   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());
0216 }
0217 
0218 // Helper to store the time zone information needed for formatting.
0219 struct _LIBCPP_HIDE_FROM_ABI __time_zone {
0220   // Typically these abbreviations are short and fit in the string's internal
0221   // buffer.
0222   string __abbrev;
0223   chrono::seconds __offset;
0224 };
0225 
0226 template <class _Tp>
0227 _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
0228 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0229   if constexpr (same_as<_Tp, chrono::sys_info>)
0230     return {__value.abbrev, __value.offset};
0231 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0232         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0233   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0234     return __formatter::__convert_to_time_zone(__value.get_info());
0235 #    endif
0236   else
0237 #  endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0238     return {"UTC", chrono::seconds{0}};
0239 }
0240 
0241 template <class _CharT, class _Tp>
0242 _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
0243     basic_stringstream<_CharT>& __sstr, const _Tp& __value, basic_string_view<_CharT> __chrono_specs) {
0244   tm __t              = std::__convert_to_tm<tm>(__value);
0245   __time_zone __z     = __formatter::__convert_to_time_zone(__value);
0246   const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc());
0247   for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) {
0248     if (*__it == _CharT('%')) {
0249       auto __s = __it;
0250       ++__it;
0251       // We only handle the types that can't be directly handled by time_put.
0252       // (as an optimization n, t, and % are also handled directly.)
0253       switch (*__it) {
0254       case _CharT('n'):
0255         __sstr << _CharT('\n');
0256         break;
0257       case _CharT('t'):
0258         __sstr << _CharT('\t');
0259         break;
0260       case _CharT('%'):
0261         __sstr << *__it;
0262         break;
0263 
0264       case _CharT('C'): {
0265         // strftime's output is only defined in the range [00, 99].
0266         int __year = __t.tm_year + 1900;
0267         if (__year < 1000 || __year > 9999)
0268           __formatter::__format_century(__sstr, __year);
0269         else
0270           __facet.put(
0271               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
0272       } break;
0273 
0274       case _CharT('j'):
0275         if constexpr (chrono::__is_duration<_Tp>::value)
0276           // Converting a duration where the period has a small ratio to days
0277           // may fail to compile. This due to loss of precision in the
0278           // conversion. In order to avoid that issue convert to seconds as
0279           // an intemediate step.
0280           __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();
0281         else
0282           __facet.put(
0283               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
0284         break;
0285 
0286       case _CharT('q'):
0287         if constexpr (chrono::__is_duration<_Tp>::value) {
0288           __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();
0289           break;
0290         }
0291         __builtin_unreachable();
0292 
0293       case _CharT('Q'):
0294         // TODO FMT Determine the proper ideas
0295         // - Should it honour the precision?
0296         // - Shoult it honour the locale setting for the separators?
0297         // The wording for Q doesn't use the word locale and the effect of
0298         // precision is unspecified.
0299         //
0300         // MSVC STL ignores precision but uses separator
0301         // FMT honours precision and has a bug for separator
0302         // https://godbolt.org/z/78b7sMxns
0303         if constexpr (chrono::__is_duration<_Tp>::value) {
0304           __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());
0305           break;
0306         }
0307         __builtin_unreachable();
0308 
0309       case _CharT('S'):
0310       case _CharT('T'):
0311         __facet.put(
0312             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
0313         if constexpr (__use_fraction<_Tp>())
0314           __formatter::__format_sub_seconds(__sstr, __value);
0315         break;
0316 
0317         // Unlike time_put and strftime the formatting library requires %Y
0318         //
0319         // [tab:time.format.spec]
0320         //   The year as a decimal number. If the result is less than four digits
0321         //   it is left-padded with 0 to four digits.
0322         //
0323         // This means years in the range (-1000, 1000) need manual formatting.
0324         // It's unclear whether %EY needs the same treatment. For example the
0325         // Japanese EY contains the era name and year. This is zero-padded to 2
0326         // digits in time_put (note that older glibc versions didn't do
0327         // padding.) However most eras won't reach 100 years, let alone 1000.
0328         // So padding to 4 digits seems unwanted for Japanese.
0329         //
0330         // The same applies to %Ex since that too depends on the era.
0331         //
0332         // %x the locale's date representation is currently doesn't handle the
0333         // zero-padding too.
0334         //
0335         // The 4 digits can be implemented better at a later time. On POSIX
0336         // systems the required information can be extracted by nl_langinfo
0337         // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
0338         //
0339         // Note since year < -1000 is expected to be rare it uses the more
0340         // expensive year routine.
0341         //
0342         // TODO FMT evaluate the comment above.
0343 
0344 #  if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
0345       case _CharT('y'):
0346         // Glibc fails for negative values, AIX for positive values too.
0347         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);
0348         break;
0349 #  endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
0350 
0351       case _CharT('Y'):
0352         // Depending on the platform's libc the range of supported years is
0353         // limited. Instead of of testing all conditions use the internal
0354         // implementation unconditionally.
0355         __formatter::__format_year(__sstr, __t.tm_year + 1900);
0356         break;
0357 
0358       case _CharT('F'):
0359         // Depending on the platform's libc the range of supported years is
0360         // limited. Instead of testing all conditions use the internal
0361         // implementation unconditionally.
0362         __formatter::__format_year(__sstr, __t.tm_year + 1900);
0363         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
0364         break;
0365 
0366       case _CharT('z'):
0367         __formatter::__format_zone_offset(__sstr, __z.__offset, false);
0368         break;
0369 
0370       case _CharT('Z'):
0371         // __abbrev is always a char so the copy may convert.
0372         ranges::copy(__z.__abbrev, std::ostreambuf_iterator<_CharT>{__sstr});
0373         break;
0374 
0375       case _CharT('O'):
0376         if constexpr (__use_fraction<_Tp>()) {
0377           // Handle OS using the normal representation for the non-fractional
0378           // part. There seems to be no locale information regarding how the
0379           // fractional part should be formatted.
0380           if (*(__it + 1) == 'S') {
0381             ++__it;
0382             __facet.put(
0383                 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
0384             __formatter::__format_sub_seconds(__sstr, __value);
0385             break;
0386           }
0387         }
0388 
0389         // Oz produces the same output as Ez below.
0390         [[fallthrough]];
0391       case _CharT('E'):
0392         ++__it;
0393         if (*__it == 'z') {
0394           __formatter::__format_zone_offset(__sstr, __z.__offset, true);
0395           break;
0396         }
0397         [[fallthrough]];
0398       default:
0399         __facet.put(
0400             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
0401         break;
0402       }
0403     } else {
0404       __sstr << *__it;
0405     }
0406   }
0407 }
0408 
0409 template <class _Tp>
0410 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
0411   if constexpr (__is_time_point<_Tp>)
0412     return true;
0413   else if constexpr (same_as<_Tp, chrono::day>)
0414     return true;
0415   else if constexpr (same_as<_Tp, chrono::month>)
0416     return __value.ok();
0417   else if constexpr (same_as<_Tp, chrono::year>)
0418     return true;
0419   else if constexpr (same_as<_Tp, chrono::weekday>)
0420     return true;
0421   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
0422     return true;
0423   else if constexpr (same_as<_Tp, chrono::weekday_last>)
0424     return true;
0425   else if constexpr (same_as<_Tp, chrono::month_day>)
0426     return true;
0427   else if constexpr (same_as<_Tp, chrono::month_day_last>)
0428     return true;
0429   else if constexpr (same_as<_Tp, chrono::month_weekday>)
0430     return true;
0431   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
0432     return true;
0433   else if constexpr (same_as<_Tp, chrono::year_month>)
0434     return true;
0435   else if constexpr (same_as<_Tp, chrono::year_month_day>)
0436     return __value.ok();
0437   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
0438     return __value.ok();
0439   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
0440     return __value.weekday().ok();
0441   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
0442     return __value.weekday().ok();
0443   else if constexpr (__is_hh_mm_ss<_Tp>)
0444     return true;
0445 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0446   else if constexpr (same_as<_Tp, chrono::sys_info>)
0447     return true;
0448   else if constexpr (same_as<_Tp, chrono::local_info>)
0449     return true;
0450 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0451         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0452   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0453     return true;
0454 #    endif
0455 #  endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0456   else
0457     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
0458 }
0459 
0460 template <class _Tp>
0461 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
0462   if constexpr (__is_time_point<_Tp>)
0463     return true;
0464   else if constexpr (same_as<_Tp, chrono::day>)
0465     return true;
0466   else if constexpr (same_as<_Tp, chrono::month>)
0467     return __value.ok();
0468   else if constexpr (same_as<_Tp, chrono::year>)
0469     return true;
0470   else if constexpr (same_as<_Tp, chrono::weekday>)
0471     return __value.ok();
0472   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
0473     return __value.weekday().ok();
0474   else if constexpr (same_as<_Tp, chrono::weekday_last>)
0475     return __value.weekday().ok();
0476   else if constexpr (same_as<_Tp, chrono::month_day>)
0477     return true;
0478   else if constexpr (same_as<_Tp, chrono::month_day_last>)
0479     return true;
0480   else if constexpr (same_as<_Tp, chrono::month_weekday>)
0481     return __value.weekday_indexed().ok();
0482   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
0483     return __value.weekday_indexed().ok();
0484   else if constexpr (same_as<_Tp, chrono::year_month>)
0485     return true;
0486   else if constexpr (same_as<_Tp, chrono::year_month_day>)
0487     return __value.ok();
0488   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
0489     return __value.ok();
0490   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
0491     return __value.weekday().ok();
0492   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
0493     return __value.weekday().ok();
0494   else if constexpr (__is_hh_mm_ss<_Tp>)
0495     return true;
0496 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0497   else if constexpr (same_as<_Tp, chrono::sys_info>)
0498     return true;
0499   else if constexpr (same_as<_Tp, chrono::local_info>)
0500     return true;
0501 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0502         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0503   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0504     return true;
0505 #    endif
0506 #  endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0507   else
0508     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
0509 }
0510 
0511 template <class _Tp>
0512 _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
0513   if constexpr (__is_time_point<_Tp>)
0514     return true;
0515   else if constexpr (same_as<_Tp, chrono::day>)
0516     return true;
0517   else if constexpr (same_as<_Tp, chrono::month>)
0518     return __value.ok();
0519   else if constexpr (same_as<_Tp, chrono::year>)
0520     return true;
0521   else if constexpr (same_as<_Tp, chrono::weekday>)
0522     return true;
0523   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
0524     return true;
0525   else if constexpr (same_as<_Tp, chrono::weekday_last>)
0526     return true;
0527   else if constexpr (same_as<_Tp, chrono::month_day>)
0528     return true;
0529   else if constexpr (same_as<_Tp, chrono::month_day_last>)
0530     return true;
0531   else if constexpr (same_as<_Tp, chrono::month_weekday>)
0532     return true;
0533   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
0534     return true;
0535   else if constexpr (same_as<_Tp, chrono::year_month>)
0536     return true;
0537   else if constexpr (same_as<_Tp, chrono::year_month_day>)
0538     return __value.ok();
0539   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
0540     return __value.ok();
0541   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
0542     return __value.ok();
0543   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
0544     return __value.ok();
0545   else if constexpr (__is_hh_mm_ss<_Tp>)
0546     return true;
0547 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0548   else if constexpr (same_as<_Tp, chrono::sys_info>)
0549     return true;
0550   else if constexpr (same_as<_Tp, chrono::local_info>)
0551     return true;
0552 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0553         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0554   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0555     return true;
0556 #    endif
0557 #  endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0558   else
0559     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
0560 }
0561 
0562 template <class _Tp>
0563 _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
0564   if constexpr (__is_time_point<_Tp>)
0565     return true;
0566   else if constexpr (same_as<_Tp, chrono::day>)
0567     return true;
0568   else if constexpr (same_as<_Tp, chrono::month>)
0569     return __value.ok();
0570   else if constexpr (same_as<_Tp, chrono::year>)
0571     return true;
0572   else if constexpr (same_as<_Tp, chrono::weekday>)
0573     return true;
0574   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
0575     return true;
0576   else if constexpr (same_as<_Tp, chrono::weekday_last>)
0577     return true;
0578   else if constexpr (same_as<_Tp, chrono::month_day>)
0579     return __value.month().ok();
0580   else if constexpr (same_as<_Tp, chrono::month_day_last>)
0581     return __value.month().ok();
0582   else if constexpr (same_as<_Tp, chrono::month_weekday>)
0583     return __value.month().ok();
0584   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
0585     return __value.month().ok();
0586   else if constexpr (same_as<_Tp, chrono::year_month>)
0587     return __value.month().ok();
0588   else if constexpr (same_as<_Tp, chrono::year_month_day>)
0589     return __value.month().ok();
0590   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
0591     return __value.month().ok();
0592   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
0593     return __value.month().ok();
0594   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
0595     return __value.month().ok();
0596   else if constexpr (__is_hh_mm_ss<_Tp>)
0597     return true;
0598 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0599   else if constexpr (same_as<_Tp, chrono::sys_info>)
0600     return true;
0601   else if constexpr (same_as<_Tp, chrono::local_info>)
0602     return true;
0603 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0604         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0605   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
0606     return true;
0607 #    endif
0608 #  endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0609   else
0610     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
0611 }
0612 
0613 template <class _CharT, class _Tp, class _FormatContext>
0614 _LIBCPP_HIDE_FROM_ABI auto
0615 __format_chrono(const _Tp& __value,
0616                 _FormatContext& __ctx,
0617                 __format_spec::__parsed_specifications<_CharT> __specs,
0618                 basic_string_view<_CharT> __chrono_specs) {
0619   basic_stringstream<_CharT> __sstr;
0620   // [time.format]/2
0621   // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise
0622   // 2.2 - the locale passed to the formatting function if any, otherwise
0623   // 2.3 - the global locale.
0624   // Note that the __ctx's locale() call does 2.2 and 2.3.
0625   if (__specs.__chrono_.__locale_specific_form_)
0626     __sstr.imbue(__ctx.locale());
0627   else
0628     __sstr.imbue(locale::classic());
0629 
0630   if (__chrono_specs.empty())
0631     __sstr << __value;
0632   else {
0633     if constexpr (chrono::__is_duration<_Tp>::value) {
0634       // A duration can be a user defined arithmetic type. Users may specialize
0635       // numeric_limits, but they may not specialize is_signed.
0636       if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {
0637         if (__value < __value.zero()) {
0638           __sstr << _CharT('-');
0639           __formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);
0640         } else
0641           __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
0642       } else
0643         __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
0644       // TODO FMT When keeping the precision it will truncate the string.
0645       // Note that the behaviour what the precision does isn't specified.
0646       __specs.__precision_ = -1;
0647     } else {
0648       // Test __weekday_name_ before __weekday_ to give a better error.
0649       if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value))
0650         std::__throw_format_error("Formatting a weekday name needs a valid weekday");
0651 
0652       if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))
0653         std::__throw_format_error("Formatting a weekday needs a valid weekday");
0654 
0655       if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))
0656         std::__throw_format_error("Formatting a day of year needs a valid date");
0657 
0658       if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value))
0659         std::__throw_format_error("Formatting a week of year needs a valid date");
0660 
0661       if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
0662         std::__throw_format_error("Formatting a month name from an invalid month number");
0663 
0664       if constexpr (__is_hh_mm_ss<_Tp>) {
0665         // Note this is a pedantic intepretation of the Standard. A hh_mm_ss
0666         // is no longer a time_of_day and can store an arbitrary number of
0667         // hours. A number of hours in a 12 or 24 hour clock can't represent
0668         // 24 hours or more. The functions std::chrono::make12 and
0669         // std::chrono::make24 reaffirm this view point.
0670         //
0671         // Interestingly this will be the only output stream function that
0672         // throws.
0673         //
0674         // TODO FMT The wording probably needs to be adapted to
0675         // - The displayed hours is hh_mm_ss.hours() % 24
0676         // - It should probably allow %j in the same fashion as duration.
0677         // - The stream formatter should change its output when hours >= 24
0678         //   - Write it as not valid,
0679         //   - or write the number of days.
0680         if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)
0681           std::__throw_format_error("Formatting a hour needs a valid value");
0682 
0683         if (__value.is_negative())
0684           __sstr << _CharT('-');
0685       }
0686 
0687       __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
0688     }
0689   }
0690 
0691   return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs);
0692 }
0693 
0694 } // namespace __formatter
0695 
0696 template <__fmt_char_type _CharT>
0697 struct _LIBCPP_TEMPLATE_VIS __formatter_chrono {
0698 public:
0699   template <class _ParseContext>
0700   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator
0701   __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) {
0702     return __parser_.__parse(__ctx, __fields, __flags);
0703   }
0704 
0705   template <class _Tp, class _FormatContext>
0706   _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const {
0707     return __formatter::__format_chrono(
0708         __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_);
0709   }
0710 
0711   __format_spec::__parser_chrono<_CharT> __parser_;
0712 };
0713 
0714 template <class _Duration, __fmt_char_type _CharT>
0715 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
0716 public:
0717   using _Base = __formatter_chrono<_CharT>;
0718 
0719   template <class _ParseContext>
0720   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0721     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
0722   }
0723 };
0724 
0725 template <class _Duration, __fmt_char_type _CharT>
0726 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
0727 public:
0728   using _Base = __formatter_chrono<_CharT>;
0729 
0730   template <class _ParseContext>
0731   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0732     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
0733   }
0734 };
0735 
0736 template <class _Duration, __fmt_char_type _CharT>
0737 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
0738 public:
0739   using _Base = __formatter_chrono<_CharT>;
0740 
0741   template <class _ParseContext>
0742   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0743     // The flags are not __clock since there is no associated time-zone.
0744     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time);
0745   }
0746 };
0747 
0748 template <class _Rep, class _Period, __fmt_char_type _CharT>
0749 struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {
0750 public:
0751   using _Base = __formatter_chrono<_CharT>;
0752 
0753   template <class _ParseContext>
0754   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0755     // [time.format]/1
0756     // Giving a precision specification in the chrono-format-spec is valid only
0757     // for std::chrono::duration types where the representation type Rep is a
0758     // floating-point type. For all other Rep types, an exception of type
0759     // format_error is thrown if the chrono-format-spec contains a precision
0760     // specification.
0761     //
0762     // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.
0763     if constexpr (std::floating_point<_Rep>)
0764       return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);
0765     else
0766       return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);
0767   }
0768 };
0769 
0770 template <__fmt_char_type _CharT>
0771 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> {
0772 public:
0773   using _Base = __formatter_chrono<_CharT>;
0774 
0775   template <class _ParseContext>
0776   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0777     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day);
0778   }
0779 };
0780 
0781 template <__fmt_char_type _CharT>
0782 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> {
0783 public:
0784   using _Base = __formatter_chrono<_CharT>;
0785 
0786   template <class _ParseContext>
0787   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0788     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
0789   }
0790 };
0791 
0792 template <__fmt_char_type _CharT>
0793 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> {
0794 public:
0795   using _Base = __formatter_chrono<_CharT>;
0796 
0797   template <class _ParseContext>
0798   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0799     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);
0800   }
0801 };
0802 
0803 template <__fmt_char_type _CharT>
0804 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> {
0805 public:
0806   using _Base = __formatter_chrono<_CharT>;
0807 
0808   template <class _ParseContext>
0809   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0810     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
0811   }
0812 };
0813 
0814 template <__fmt_char_type _CharT>
0815 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> {
0816 public:
0817   using _Base = __formatter_chrono<_CharT>;
0818 
0819   template <class _ParseContext>
0820   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0821     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
0822   }
0823 };
0824 
0825 template <__fmt_char_type _CharT>
0826 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> {
0827 public:
0828   using _Base = __formatter_chrono<_CharT>;
0829 
0830   template <class _ParseContext>
0831   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0832     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
0833   }
0834 };
0835 
0836 template <__fmt_char_type _CharT>
0837 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> {
0838 public:
0839   using _Base = __formatter_chrono<_CharT>;
0840 
0841   template <class _ParseContext>
0842   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0843     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day);
0844   }
0845 };
0846 
0847 template <__fmt_char_type _CharT>
0848 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> {
0849 public:
0850   using _Base = __formatter_chrono<_CharT>;
0851 
0852   template <class _ParseContext>
0853   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0854     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
0855   }
0856 };
0857 
0858 template <__fmt_char_type _CharT>
0859 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> {
0860 public:
0861   using _Base = __formatter_chrono<_CharT>;
0862 
0863   template <class _ParseContext>
0864   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0865     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
0866   }
0867 };
0868 
0869 template <__fmt_char_type _CharT>
0870 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
0871 public:
0872   using _Base = __formatter_chrono<_CharT>;
0873 
0874   template <class _ParseContext>
0875   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0876     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
0877   }
0878 };
0879 
0880 template <__fmt_char_type _CharT>
0881 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> {
0882 public:
0883   using _Base = __formatter_chrono<_CharT>;
0884 
0885   template <class _ParseContext>
0886   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0887     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month);
0888   }
0889 };
0890 
0891 template <__fmt_char_type _CharT>
0892 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> {
0893 public:
0894   using _Base = __formatter_chrono<_CharT>;
0895 
0896   template <class _ParseContext>
0897   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0898     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
0899   }
0900 };
0901 
0902 template <__fmt_char_type _CharT>
0903 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> {
0904 public:
0905   using _Base = __formatter_chrono<_CharT>;
0906 
0907   template <class _ParseContext>
0908   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0909     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
0910   }
0911 };
0912 
0913 template <__fmt_char_type _CharT>
0914 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> {
0915 public:
0916   using _Base = __formatter_chrono<_CharT>;
0917 
0918   template <class _ParseContext>
0919   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0920     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
0921   }
0922 };
0923 
0924 template <__fmt_char_type _CharT>
0925 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
0926 public:
0927   using _Base = __formatter_chrono<_CharT>;
0928 
0929   template <class _ParseContext>
0930   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0931     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
0932   }
0933 };
0934 
0935 template <class _Duration, __fmt_char_type _CharT>
0936 struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
0937 public:
0938   using _Base = __formatter_chrono<_CharT>;
0939 
0940   template <class _ParseContext>
0941   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0942     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
0943   }
0944 };
0945 
0946 #  if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0947 template <__fmt_char_type _CharT>
0948 struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
0949 public:
0950   using _Base = __formatter_chrono<_CharT>;
0951 
0952   template <class _ParseContext>
0953   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0954     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
0955   }
0956 };
0957 
0958 template <__fmt_char_type _CharT>
0959 struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {
0960 public:
0961   using _Base = __formatter_chrono<_CharT>;
0962 
0963   template <class _ParseContext>
0964   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0965     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
0966   }
0967 };
0968 #    if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&                          \
0969         !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0970 // Note due to how libc++'s formatters are implemented there is no need to add
0971 // the exposition only local-time-format-t abstraction.
0972 template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
0973 struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
0974 public:
0975   using _Base = __formatter_chrono<_CharT>;
0976 
0977   template <class _ParseContext>
0978   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
0979     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
0980   }
0981 };
0982 #    endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
0983            // !defined(_LIBCPP_HAS_NO_LOCALIZATION)
0984 #  endif   // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
0985 
0986 #endif // if _LIBCPP_STD_VER >= 20
0987 
0988 _LIBCPP_END_NAMESPACE_STD
0989 
0990 #endif //  _LIBCPP___CXX03___CHRONO_FORMATTER_H