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