Back to home page

EIC code displayed by LXR

 
 

    


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

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