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