xref: /llvm-project/libcxx/include/__chrono/formatter.h (revision 3b30f20c60d020e43f5700dae68cf1080158b725)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___CHRONO_FORMATTER_H
11 #define _LIBCPP___CHRONO_FORMATTER_H
12 
13 #include <__config>
14 
15 #if _LIBCPP_HAS_LOCALIZATION
16 
17 #  include <__algorithm/ranges_copy.h>
18 #  include <__chrono/calendar.h>
19 #  include <__chrono/concepts.h>
20 #  include <__chrono/convert_to_tm.h>
21 #  include <__chrono/day.h>
22 #  include <__chrono/duration.h>
23 #  include <__chrono/file_clock.h>
24 #  include <__chrono/hh_mm_ss.h>
25 #  include <__chrono/local_info.h>
26 #  include <__chrono/month.h>
27 #  include <__chrono/month_weekday.h>
28 #  include <__chrono/monthday.h>
29 #  include <__chrono/ostream.h>
30 #  include <__chrono/parser_std_format_spec.h>
31 #  include <__chrono/statically_widen.h>
32 #  include <__chrono/sys_info.h>
33 #  include <__chrono/system_clock.h>
34 #  include <__chrono/time_point.h>
35 #  include <__chrono/utc_clock.h>
36 #  include <__chrono/weekday.h>
37 #  include <__chrono/year.h>
38 #  include <__chrono/year_month.h>
39 #  include <__chrono/year_month_day.h>
40 #  include <__chrono/year_month_weekday.h>
41 #  include <__chrono/zoned_time.h>
42 #  include <__concepts/arithmetic.h>
43 #  include <__concepts/same_as.h>
44 #  include <__format/concepts.h>
45 #  include <__format/format_error.h>
46 #  include <__format/format_functions.h>
47 #  include <__format/format_parse_context.h>
48 #  include <__format/formatter.h>
49 #  include <__format/parser_std_format_spec.h>
50 #  include <__format/write_escaped.h>
51 #  include <__memory/addressof.h>
52 #  include <__type_traits/is_specialization.h>
53 #  include <cmath>
54 #  include <ctime>
55 #  include <limits>
56 #  include <locale>
57 #  include <sstream>
58 #  include <string_view>
59 
60 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
61 #    pragma GCC system_header
62 #  endif
63 
64 _LIBCPP_BEGIN_NAMESPACE_STD
65 
66 #  if _LIBCPP_STD_VER >= 20
67 
68 namespace __formatter {
69 
70 /// Formats a time based on a tm struct.
71 ///
72 /// This formatter passes the formatting to time_put which uses strftime. When
73 /// the value is outside the valid range it's unspecified what strftime will
74 /// output. For example weekday 8 can print 1 when the day is processed modulo
75 /// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if
76 /// 7 is handled as a special case.
77 ///
78 /// The Standard doesn't specify what to do in this case so the result depends
79 /// on the result of the underlying code.
80 ///
81 /// \pre When the (abbreviated) weekday or month name are used, the caller
82 ///      validates whether the value is valid. So the caller handles that
83 ///      requirement of Table 97: Meaning of conversion specifiers
84 ///      [tab:time.format.spec].
85 ///
86 /// When no chrono-specs are provided it uses the stream formatter.
87 
88 // For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This
89 // fails compile-time due to the limited precision of the ratio (64-bit is too
90 // small). Therefore a duration uses its own conversion.
91 template <class _CharT, class _Rep, class _Period>
92 _LIBCPP_HIDE_FROM_ABI void
93 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::duration<_Rep, _Period>& __value) {
94   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
95 
96   using __duration = chrono::duration<_Rep, _Period>;
97 
98   auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
99   // Converts a negative fraction to its positive value.
100   if (__value < chrono::seconds{0} && __fraction != __duration{0})
101     __fraction += chrono::seconds{1};
102   if constexpr (chrono::treat_as_floating_point_v<_Rep>)
103     // When the floating-point value has digits itself they are ignored based
104     // on the wording in [tab:time.format.spec]
105     //   If the precision of the input cannot be exactly represented with
106     //   seconds, then the format is a decimal floating-point number with a
107     //   fixed format and a precision matching that of the precision of the
108     //   input (or to a microseconds precision if the conversion to
109     //   floating-point decimal seconds cannot be made within 18 fractional
110     //   digits).
111     //
112     // This matches the behaviour of MSVC STL, fmtlib interprets this
113     // differently and uses 3 decimals.
114     // https://godbolt.org/z/6dsbnW8ba
115     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
116                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
117                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
118                    chrono::hh_mm_ss<__duration>::fractional_width);
119   else
120     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
121                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
122                    chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),
123                    chrono::hh_mm_ss<__duration>::fractional_width);
124 }
125 
126 template <class _CharT, __is_time_point _Tp>
127 _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const _Tp& __value) {
128   __formatter::__format_sub_seconds(__sstr, __value.time_since_epoch());
129 }
130 
131 template <class _CharT, class _Duration>
132 _LIBCPP_HIDE_FROM_ABI void
133 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<_Duration>& __value) {
134   __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
135   if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)
136     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
137                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
138                    __value.subseconds().count(),
139                    __value.fractional_width);
140   else
141     std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
142                    _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
143                    __value.subseconds().count(),
144                    __value.fractional_width);
145 }
146 
147 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
148 template <class _CharT, class _Duration, class _TimeZonePtr>
149 _LIBCPP_HIDE_FROM_ABI void
150 __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
151   __formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
152 }
153 #    endif
154 
155 template <class _Tp>
156 consteval bool __use_fraction() {
157   if constexpr (__is_time_point<_Tp>)
158     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
159 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
160   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
161     return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
162 #    endif
163   else if constexpr (chrono::__is_duration_v<_Tp>)
164     return chrono::hh_mm_ss<_Tp>::fractional_width;
165   else if constexpr (__is_hh_mm_ss<_Tp>)
166     return _Tp::fractional_width;
167   else
168     return false;
169 }
170 
171 template <class _CharT>
172 _LIBCPP_HIDE_FROM_ABI void __format_year(basic_stringstream<_CharT>& __sstr, int __year) {
173   if (__year < 0) {
174     __sstr << _CharT('-');
175     __year = -__year;
176   }
177 
178   // TODO FMT Write an issue
179   //   If the result has less than four digits it is zero-padded with 0 to two digits.
180   // is less -> has less
181   // left-padded -> zero-padded, otherwise the proper value would be 000-0.
182 
183   // Note according to the wording it should be left padded, which is odd.
184   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);
185 }
186 
187 template <class _CharT>
188 _LIBCPP_HIDE_FROM_ABI void __format_century(basic_stringstream<_CharT>& __sstr, int __year) {
189   // TODO FMT Write an issue
190   // [tab:time.format.spec]
191   //   %C The year divided by 100 using floored division. If the result is a
192   //   single decimal digit, it is prefixed with 0.
193 
194   bool __negative = __year < 0;
195   int __century   = (__year - (99 * __negative)) / 100; // floored division
196   __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);
197 }
198 
199 // Implements the %z format specifier according to [tab:time.format.spec], where
200 // '__modifier' signals %Oz or %Ez were used. (Both modifiers behave the same,
201 // so there is no need to distinguish between them.)
202 template <class _CharT>
203 _LIBCPP_HIDE_FROM_ABI void
204 __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offset, bool __modifier) {
205   if (__offset < 0s) {
206     __sstr << _CharT('-');
207     __offset = -__offset;
208   } else {
209     __sstr << _CharT('+');
210   }
211 
212   chrono::hh_mm_ss __hms{__offset};
213   std::ostreambuf_iterator<_CharT> __out_it{__sstr};
214   // Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.
215   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());
216   if (__modifier)
217     __sstr << _CharT(':');
218   std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());
219 }
220 
221 // Helper to store the time zone information needed for formatting.
222 struct _LIBCPP_HIDE_FROM_ABI __time_zone {
223   // Typically these abbreviations are short and fit in the string's internal
224   // buffer.
225   string __abbrev;
226   chrono::seconds __offset;
227 };
228 
229 template <class _Tp>
230 _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
231 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
232   if constexpr (same_as<_Tp, chrono::sys_info>)
233     return {__value.abbrev, __value.offset};
234 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
235   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
236     return __formatter::__convert_to_time_zone(__value.get_info());
237 #      endif
238   else
239 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
240     return {"UTC", chrono::seconds{0}};
241 }
242 
243 template <class _CharT, class _Tp>
244 _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
245     basic_stringstream<_CharT>& __sstr, const _Tp& __value, basic_string_view<_CharT> __chrono_specs) {
246   tm __t              = std::__convert_to_tm<tm>(__value);
247   __time_zone __z     = __formatter::__convert_to_time_zone(__value);
248   const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc());
249   for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) {
250     if (*__it == _CharT('%')) {
251       auto __s = __it;
252       ++__it;
253       // We only handle the types that can't be directly handled by time_put.
254       // (as an optimization n, t, and % are also handled directly.)
255       switch (*__it) {
256       case _CharT('n'):
257         __sstr << _CharT('\n');
258         break;
259       case _CharT('t'):
260         __sstr << _CharT('\t');
261         break;
262       case _CharT('%'):
263         __sstr << *__it;
264         break;
265 
266       case _CharT('C'): {
267         // strftime's output is only defined in the range [00, 99].
268         int __year = __t.tm_year + 1900;
269         if (__year < 1000 || __year > 9999)
270           __formatter::__format_century(__sstr, __year);
271         else
272           __facet.put(
273               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
274       } break;
275 
276       case _CharT('j'):
277         if constexpr (chrono::__is_duration_v<_Tp>)
278           // Converting a duration where the period has a small ratio to days
279           // may fail to compile. This due to loss of precision in the
280           // conversion. In order to avoid that issue convert to seconds as
281           // an intemediate step.
282           __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();
283         else
284           __facet.put(
285               {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
286         break;
287 
288       case _CharT('q'):
289         if constexpr (chrono::__is_duration_v<_Tp>) {
290           __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();
291           break;
292         }
293         __builtin_unreachable();
294 
295       case _CharT('Q'):
296         // TODO FMT Determine the proper ideas
297         // - Should it honour the precision?
298         // - Shoult it honour the locale setting for the separators?
299         // The wording for Q doesn't use the word locale and the effect of
300         // precision is unspecified.
301         //
302         // MSVC STL ignores precision but uses separator
303         // FMT honours precision and has a bug for separator
304         // https://godbolt.org/z/78b7sMxns
305         if constexpr (chrono::__is_duration_v<_Tp>) {
306           __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());
307           break;
308         }
309         __builtin_unreachable();
310 
311       case _CharT('S'):
312       case _CharT('T'):
313         __facet.put(
314             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
315         if constexpr (__use_fraction<_Tp>())
316           __formatter::__format_sub_seconds(__sstr, __value);
317         break;
318 
319         // Unlike time_put and strftime the formatting library requires %Y
320         //
321         // [tab:time.format.spec]
322         //   The year as a decimal number. If the result is less than four digits
323         //   it is left-padded with 0 to four digits.
324         //
325         // This means years in the range (-1000, 1000) need manual formatting.
326         // It's unclear whether %EY needs the same treatment. For example the
327         // Japanese EY contains the era name and year. This is zero-padded to 2
328         // digits in time_put (note that older glibc versions didn't do
329         // padding.) However most eras won't reach 100 years, let alone 1000.
330         // So padding to 4 digits seems unwanted for Japanese.
331         //
332         // The same applies to %Ex since that too depends on the era.
333         //
334         // %x the locale's date representation is currently doesn't handle the
335         // zero-padding too.
336         //
337         // The 4 digits can be implemented better at a later time. On POSIX
338         // systems the required information can be extracted by nl_langinfo
339         // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
340         //
341         // Note since year < -1000 is expected to be rare it uses the more
342         // expensive year routine.
343         //
344         // TODO FMT evaluate the comment above.
345 
346 #    if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
347       case _CharT('y'):
348         // Glibc fails for negative values, AIX for positive values too.
349         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);
350         break;
351 #    endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
352 
353       case _CharT('Y'):
354         // Depending on the platform's libc the range of supported years is
355         // limited. Instead of of testing all conditions use the internal
356         // implementation unconditionally.
357         __formatter::__format_year(__sstr, __t.tm_year + 1900);
358         break;
359 
360       case _CharT('F'):
361         // Depending on the platform's libc the range of supported years is
362         // limited. Instead of testing all conditions use the internal
363         // implementation unconditionally.
364         __formatter::__format_year(__sstr, __t.tm_year + 1900);
365         __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
366         break;
367 
368       case _CharT('z'):
369         __formatter::__format_zone_offset(__sstr, __z.__offset, false);
370         break;
371 
372       case _CharT('Z'):
373         // __abbrev is always a char so the copy may convert.
374         ranges::copy(__z.__abbrev, std::ostreambuf_iterator<_CharT>{__sstr});
375         break;
376 
377       case _CharT('O'):
378         if constexpr (__use_fraction<_Tp>()) {
379           // Handle OS using the normal representation for the non-fractional
380           // part. There seems to be no locale information regarding how the
381           // fractional part should be formatted.
382           if (*(__it + 1) == 'S') {
383             ++__it;
384             __facet.put(
385                 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
386             __formatter::__format_sub_seconds(__sstr, __value);
387             break;
388           }
389         }
390 
391         // Oz produces the same output as Ez below.
392         [[fallthrough]];
393       case _CharT('E'):
394         ++__it;
395         if (*__it == 'z') {
396           __formatter::__format_zone_offset(__sstr, __z.__offset, true);
397           break;
398         }
399         [[fallthrough]];
400       default:
401         __facet.put(
402             {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
403         break;
404       }
405     } else {
406       __sstr << *__it;
407     }
408   }
409 }
410 
411 template <class _Tp>
412 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
413   if constexpr (__is_time_point<_Tp>)
414     return true;
415   else if constexpr (same_as<_Tp, chrono::day>)
416     return true;
417   else if constexpr (same_as<_Tp, chrono::month>)
418     return __value.ok();
419   else if constexpr (same_as<_Tp, chrono::year>)
420     return true;
421   else if constexpr (same_as<_Tp, chrono::weekday>)
422     return true;
423   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
424     return true;
425   else if constexpr (same_as<_Tp, chrono::weekday_last>)
426     return true;
427   else if constexpr (same_as<_Tp, chrono::month_day>)
428     return true;
429   else if constexpr (same_as<_Tp, chrono::month_day_last>)
430     return true;
431   else if constexpr (same_as<_Tp, chrono::month_weekday>)
432     return true;
433   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
434     return true;
435   else if constexpr (same_as<_Tp, chrono::year_month>)
436     return true;
437   else if constexpr (same_as<_Tp, chrono::year_month_day>)
438     return __value.ok();
439   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
440     return __value.ok();
441   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
442     return __value.weekday().ok();
443   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
444     return __value.weekday().ok();
445   else if constexpr (__is_hh_mm_ss<_Tp>)
446     return true;
447 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
448   else if constexpr (same_as<_Tp, chrono::sys_info>)
449     return true;
450   else if constexpr (same_as<_Tp, chrono::local_info>)
451     return true;
452 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
453   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
454     return true;
455 #      endif
456 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
457   else
458     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
459 }
460 
461 template <class _Tp>
462 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
463   if constexpr (__is_time_point<_Tp>)
464     return true;
465   else if constexpr (same_as<_Tp, chrono::day>)
466     return true;
467   else if constexpr (same_as<_Tp, chrono::month>)
468     return __value.ok();
469   else if constexpr (same_as<_Tp, chrono::year>)
470     return true;
471   else if constexpr (same_as<_Tp, chrono::weekday>)
472     return __value.ok();
473   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
474     return __value.weekday().ok();
475   else if constexpr (same_as<_Tp, chrono::weekday_last>)
476     return __value.weekday().ok();
477   else if constexpr (same_as<_Tp, chrono::month_day>)
478     return true;
479   else if constexpr (same_as<_Tp, chrono::month_day_last>)
480     return true;
481   else if constexpr (same_as<_Tp, chrono::month_weekday>)
482     return __value.weekday_indexed().ok();
483   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
484     return __value.weekday_indexed().ok();
485   else if constexpr (same_as<_Tp, chrono::year_month>)
486     return true;
487   else if constexpr (same_as<_Tp, chrono::year_month_day>)
488     return __value.ok();
489   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
490     return __value.ok();
491   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
492     return __value.weekday().ok();
493   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
494     return __value.weekday().ok();
495   else if constexpr (__is_hh_mm_ss<_Tp>)
496     return true;
497 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
498   else if constexpr (same_as<_Tp, chrono::sys_info>)
499     return true;
500   else if constexpr (same_as<_Tp, chrono::local_info>)
501     return true;
502 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
503   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
504     return true;
505 #      endif
506 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
507   else
508     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
509 }
510 
511 template <class _Tp>
512 _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
513   if constexpr (__is_time_point<_Tp>)
514     return true;
515   else if constexpr (same_as<_Tp, chrono::day>)
516     return true;
517   else if constexpr (same_as<_Tp, chrono::month>)
518     return __value.ok();
519   else if constexpr (same_as<_Tp, chrono::year>)
520     return true;
521   else if constexpr (same_as<_Tp, chrono::weekday>)
522     return true;
523   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
524     return true;
525   else if constexpr (same_as<_Tp, chrono::weekday_last>)
526     return true;
527   else if constexpr (same_as<_Tp, chrono::month_day>)
528     return true;
529   else if constexpr (same_as<_Tp, chrono::month_day_last>)
530     return true;
531   else if constexpr (same_as<_Tp, chrono::month_weekday>)
532     return true;
533   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
534     return true;
535   else if constexpr (same_as<_Tp, chrono::year_month>)
536     return true;
537   else if constexpr (same_as<_Tp, chrono::year_month_day>)
538     return __value.ok();
539   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
540     return __value.ok();
541   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
542     return __value.ok();
543   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
544     return __value.ok();
545   else if constexpr (__is_hh_mm_ss<_Tp>)
546     return true;
547 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
548   else if constexpr (same_as<_Tp, chrono::sys_info>)
549     return true;
550   else if constexpr (same_as<_Tp, chrono::local_info>)
551     return true;
552 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
553   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
554     return true;
555 #      endif
556 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
557   else
558     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
559 }
560 
561 template <class _Tp>
562 _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
563   if constexpr (__is_time_point<_Tp>)
564     return true;
565   else if constexpr (same_as<_Tp, chrono::day>)
566     return true;
567   else if constexpr (same_as<_Tp, chrono::month>)
568     return __value.ok();
569   else if constexpr (same_as<_Tp, chrono::year>)
570     return true;
571   else if constexpr (same_as<_Tp, chrono::weekday>)
572     return true;
573   else if constexpr (same_as<_Tp, chrono::weekday_indexed>)
574     return true;
575   else if constexpr (same_as<_Tp, chrono::weekday_last>)
576     return true;
577   else if constexpr (same_as<_Tp, chrono::month_day>)
578     return __value.month().ok();
579   else if constexpr (same_as<_Tp, chrono::month_day_last>)
580     return __value.month().ok();
581   else if constexpr (same_as<_Tp, chrono::month_weekday>)
582     return __value.month().ok();
583   else if constexpr (same_as<_Tp, chrono::month_weekday_last>)
584     return __value.month().ok();
585   else if constexpr (same_as<_Tp, chrono::year_month>)
586     return __value.month().ok();
587   else if constexpr (same_as<_Tp, chrono::year_month_day>)
588     return __value.month().ok();
589   else if constexpr (same_as<_Tp, chrono::year_month_day_last>)
590     return __value.month().ok();
591   else if constexpr (same_as<_Tp, chrono::year_month_weekday>)
592     return __value.month().ok();
593   else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
594     return __value.month().ok();
595   else if constexpr (__is_hh_mm_ss<_Tp>)
596     return true;
597 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
598   else if constexpr (same_as<_Tp, chrono::sys_info>)
599     return true;
600   else if constexpr (same_as<_Tp, chrono::local_info>)
601     return true;
602 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
603   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
604     return true;
605 #      endif
606 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
607   else
608     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
609 }
610 
611 template <class _CharT, class _Tp, class _FormatContext>
612 _LIBCPP_HIDE_FROM_ABI auto
613 __format_chrono(const _Tp& __value,
614                 _FormatContext& __ctx,
615                 __format_spec::__parsed_specifications<_CharT> __specs,
616                 basic_string_view<_CharT> __chrono_specs) {
617   basic_stringstream<_CharT> __sstr;
618   // [time.format]/2
619   // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise
620   // 2.2 - the locale passed to the formatting function if any, otherwise
621   // 2.3 - the global locale.
622   // Note that the __ctx's locale() call does 2.2 and 2.3.
623   if (__specs.__chrono_.__locale_specific_form_)
624     __sstr.imbue(__ctx.locale());
625   else
626     __sstr.imbue(locale::classic());
627 
628   if (__chrono_specs.empty())
629     __sstr << __value;
630   else {
631     if constexpr (chrono::__is_duration_v<_Tp>) {
632       // A duration can be a user defined arithmetic type. Users may specialize
633       // numeric_limits, but they may not specialize is_signed.
634       if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {
635         if (__value < __value.zero()) {
636           __sstr << _CharT('-');
637           __formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);
638         } else
639           __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
640       } else
641         __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
642       // TODO FMT When keeping the precision it will truncate the string.
643       // Note that the behaviour what the precision does isn't specified.
644       __specs.__precision_ = -1;
645     } else {
646       // Test __weekday_name_ before __weekday_ to give a better error.
647       if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value))
648         std::__throw_format_error("Formatting a weekday name needs a valid weekday");
649 
650       if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))
651         std::__throw_format_error("Formatting a weekday needs a valid weekday");
652 
653       if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))
654         std::__throw_format_error("Formatting a day of year needs a valid date");
655 
656       if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value))
657         std::__throw_format_error("Formatting a week of year needs a valid date");
658 
659       if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
660         std::__throw_format_error("Formatting a month name from an invalid month number");
661 
662       if constexpr (__is_hh_mm_ss<_Tp>) {
663         // Note this is a pedantic intepretation of the Standard. A hh_mm_ss
664         // is no longer a time_of_day and can store an arbitrary number of
665         // hours. A number of hours in a 12 or 24 hour clock can't represent
666         // 24 hours or more. The functions std::chrono::make12 and
667         // std::chrono::make24 reaffirm this view point.
668         //
669         // Interestingly this will be the only output stream function that
670         // throws.
671         //
672         // TODO FMT The wording probably needs to be adapted to
673         // - The displayed hours is hh_mm_ss.hours() % 24
674         // - It should probably allow %j in the same fashion as duration.
675         // - The stream formatter should change its output when hours >= 24
676         //   - Write it as not valid,
677         //   - or write the number of days.
678         if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)
679           std::__throw_format_error("Formatting a hour needs a valid value");
680 
681         if (__value.is_negative())
682           __sstr << _CharT('-');
683       }
684 
685       __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
686     }
687   }
688 
689   return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs);
690 }
691 
692 } // namespace __formatter
693 
694 template <__fmt_char_type _CharT>
695 struct _LIBCPP_TEMPLATE_VIS __formatter_chrono {
696 public:
697   template <class _ParseContext>
698   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator
699   __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) {
700     return __parser_.__parse(__ctx, __fields, __flags);
701   }
702 
703   template <class _Tp, class _FormatContext>
704   _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const {
705     return __formatter::__format_chrono(
706         __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_);
707   }
708 
709   __format_spec::__parser_chrono<_CharT> __parser_;
710 };
711 
712 template <class _Duration, __fmt_char_type _CharT>
713 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
714 public:
715   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
716 
717   template <class _ParseContext>
718   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
719     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
720   }
721 };
722 
723 #    if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
724 #      if _LIBCPP_HAS_EXPERIMENTAL_TZDB
725 
726 template <class _Duration, __fmt_char_type _CharT>
727 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
728 public:
729   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
730 
731   template <class _ParseContext>
732   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
733     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
734   }
735 };
736 
737 #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
738 #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
739 
740 template <class _Duration, __fmt_char_type _CharT>
741 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
742 public:
743   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
744 
745   template <class _ParseContext>
746   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
747     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
748   }
749 };
750 
751 template <class _Duration, __fmt_char_type _CharT>
752 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
753 public:
754   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
755 
756   template <class _ParseContext>
757   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
758     // The flags are not __clock since there is no associated time-zone.
759     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time);
760   }
761 };
762 
763 template <class _Rep, class _Period, __fmt_char_type _CharT>
764 struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {
765 public:
766   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
767 
768   template <class _ParseContext>
769   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
770     // [time.format]/1
771     // Giving a precision specification in the chrono-format-spec is valid only
772     // for std::chrono::duration types where the representation type Rep is a
773     // floating-point type. For all other Rep types, an exception of type
774     // format_error is thrown if the chrono-format-spec contains a precision
775     // specification.
776     //
777     // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.
778     if constexpr (std::floating_point<_Rep>)
779       return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);
780     else
781       return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);
782   }
783 };
784 
785 template <__fmt_char_type _CharT>
786 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> {
787 public:
788   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
789 
790   template <class _ParseContext>
791   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
792     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day);
793   }
794 };
795 
796 template <__fmt_char_type _CharT>
797 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> {
798 public:
799   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
800 
801   template <class _ParseContext>
802   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
803     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
804   }
805 };
806 
807 template <__fmt_char_type _CharT>
808 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> {
809 public:
810   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
811 
812   template <class _ParseContext>
813   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
814     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);
815   }
816 };
817 
818 template <__fmt_char_type _CharT>
819 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> {
820 public:
821   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
822 
823   template <class _ParseContext>
824   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
825     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
826   }
827 };
828 
829 template <__fmt_char_type _CharT>
830 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> {
831 public:
832   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
833 
834   template <class _ParseContext>
835   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
836     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
837   }
838 };
839 
840 template <__fmt_char_type _CharT>
841 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> {
842 public:
843   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
844 
845   template <class _ParseContext>
846   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
847     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);
848   }
849 };
850 
851 template <__fmt_char_type _CharT>
852 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> {
853 public:
854   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
855 
856   template <class _ParseContext>
857   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
858     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day);
859   }
860 };
861 
862 template <__fmt_char_type _CharT>
863 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> {
864 public:
865   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
866 
867   template <class _ParseContext>
868   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
869     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);
870   }
871 };
872 
873 template <__fmt_char_type _CharT>
874 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> {
875 public:
876   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
877 
878   template <class _ParseContext>
879   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
880     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
881   }
882 };
883 
884 template <__fmt_char_type _CharT>
885 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
886 public:
887   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
888 
889   template <class _ParseContext>
890   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
891     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);
892   }
893 };
894 
895 template <__fmt_char_type _CharT>
896 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> {
897 public:
898   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
899 
900   template <class _ParseContext>
901   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
902     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month);
903   }
904 };
905 
906 template <__fmt_char_type _CharT>
907 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> {
908 public:
909   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
910 
911   template <class _ParseContext>
912   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
913     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
914   }
915 };
916 
917 template <__fmt_char_type _CharT>
918 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> {
919 public:
920   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
921 
922   template <class _ParseContext>
923   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
924     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
925   }
926 };
927 
928 template <__fmt_char_type _CharT>
929 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> {
930 public:
931   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
932 
933   template <class _ParseContext>
934   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
935     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
936   }
937 };
938 
939 template <__fmt_char_type _CharT>
940 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {
941 public:
942   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
943 
944   template <class _ParseContext>
945   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
946     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);
947   }
948 };
949 
950 template <class _Duration, __fmt_char_type _CharT>
951 struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
952 public:
953   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
954 
955   template <class _ParseContext>
956   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
957     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
958   }
959 };
960 
961 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
962 template <__fmt_char_type _CharT>
963 struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
964 public:
965   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
966 
967   template <class _ParseContext>
968   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
969     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
970   }
971 };
972 
973 template <__fmt_char_type _CharT>
974 struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {
975 public:
976   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
977 
978   template <class _ParseContext>
979   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
980     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
981   }
982 };
983 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
984 // Note due to how libc++'s formatters are implemented there is no need to add
985 // the exposition only local-time-format-t abstraction.
986 template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
987 struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
988 public:
989   using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
990 
991   template <class _ParseContext>
992   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
993     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
994   }
995 };
996 #      endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
997 #    endif   // _LIBCPP_HAS_EXPERIMENTAL_TZDB
998 
999 #  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
1006