2 //===----------------------------------------------------------------------===//
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
8 //===----------------------------------------------------------------------===//
10 #ifndef _LIBCPP___CHRONO_FORMATTER_H
11 #define _LIBCPP___CHRONO_FORMATTER_H
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>
57 # include <string_view>
59 # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
60 # pragma GCC system_header
63 _LIBCPP_BEGIN_NAMESPACE_STD
65 # if _LIBCPP_STD_VER >= 20
67 namespace __formatter
{
69 /// Formats a time based on a tm struct.
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.
77 /// The Standard doesn't specify what to do in this case so the result depends
78 /// on the result of the underlying code.
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].
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
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
);
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
);
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());
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
;
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
;
170 template <class _CharT
>
171 _LIBCPP_HIDE_FROM_ABI
void __format_year(basic_stringstream
<_CharT
>& __sstr
, int __year
) {
173 __sstr
<< _CharT('-');
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
) {
205 __sstr
<< _CharT('-');
206 __offset
= -__offset
;
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());
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
225 chrono::seconds __offset
;
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());
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('%')) {
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.)
256 __sstr
<< _CharT('\n');
259 __sstr
<< _CharT('\t');
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
);
272 {__sstr
}, __sstr
, _CharT(' '), std::addressof(__t
), std::to_address(__s
), std::to_address(__it
+ 1));
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();
284 {__sstr
}, __sstr
, _CharT(' '), std::addressof(__t
), std::to_address(__s
), std::to_address(__it
+ 1));
288 if constexpr (chrono::__is_duration_v
<_Tp
>) {
289 __sstr
<< chrono::__units_suffix
<_CharT
, typename
_Tp::period
>();
292 __builtin_unreachable();
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());
308 __builtin_unreachable();
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
);
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
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)
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);
350 # endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
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);
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
);
368 __formatter::__format_zone_offset(__sstr
, __z
.__offset
, false);
372 // __abbrev is always a char so the copy may convert.
373 ranges::copy(__z
.__abbrev
, std::ostreambuf_iterator
<_CharT
>{__sstr
});
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') {
384 {__sstr
}, __sstr
, _CharT(' '), std::addressof(__t
), std::to_address(__s
), std::to_address(__it
+ 1));
385 __formatter::__format_sub_seconds(__sstr
, __value
);
390 // Oz produces the same output as Ez below.
395 __formatter::__format_zone_offset(__sstr
, __z
.__offset
, true);
401 {__sstr
}, __sstr
, _CharT(' '), std::addressof(__t
), std::to_address(__s
), std::to_address(__it
+ 1));
411 _LIBCPP_HIDE_FROM_ABI
constexpr bool __weekday_ok(const _Tp
& __value
) {
412 if constexpr (__is_time_point
<_Tp
>)
414 else if constexpr (same_as
<_Tp
, chrono::day
>)
416 else if constexpr (same_as
<_Tp
, chrono::month
>)
418 else if constexpr (same_as
<_Tp
, chrono::year
>)
420 else if constexpr (same_as
<_Tp
, chrono::weekday
>)
422 else if constexpr (same_as
<_Tp
, chrono::weekday_indexed
>)
424 else if constexpr (same_as
<_Tp
, chrono::weekday_last
>)
426 else if constexpr (same_as
<_Tp
, chrono::month_day
>)
428 else if constexpr (same_as
<_Tp
, chrono::month_day_last
>)
430 else if constexpr (same_as
<_Tp
, chrono::month_weekday
>)
432 else if constexpr (same_as
<_Tp
, chrono::month_weekday_last
>)
434 else if constexpr (same_as
<_Tp
, chrono::year_month
>)
436 else if constexpr (same_as
<_Tp
, chrono::year_month_day
>)
438 else if constexpr (same_as
<_Tp
, chrono::year_month_day_last
>)
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
>)
446 # if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
447 else if constexpr (same_as
<_Tp
, chrono::sys_info
>)
449 else if constexpr (same_as
<_Tp
, chrono::local_info
>)
451 # if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
452 else if constexpr (__is_specialization_v
<_Tp
, chrono::zoned_time
>)
455 # endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
457 static_assert(sizeof(_Tp
) == 0, "Add the missing type specialization");
461 _LIBCPP_HIDE_FROM_ABI
constexpr bool __weekday_name_ok(const _Tp
& __value
) {
462 if constexpr (__is_time_point
<_Tp
>)
464 else if constexpr (same_as
<_Tp
, chrono::day
>)
466 else if constexpr (same_as
<_Tp
, chrono::month
>)
468 else if constexpr (same_as
<_Tp
, chrono::year
>)
470 else if constexpr (same_as
<_Tp
, chrono::weekday
>)
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
>)
478 else if constexpr (same_as
<_Tp
, chrono::month_day_last
>)
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
>)
486 else if constexpr (same_as
<_Tp
, chrono::year_month_day
>)
488 else if constexpr (same_as
<_Tp
, chrono::year_month_day_last
>)
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
>)
496 # if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
497 else if constexpr (same_as
<_Tp
, chrono::sys_info
>)
499 else if constexpr (same_as
<_Tp
, chrono::local_info
>)
501 # if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
502 else if constexpr (__is_specialization_v
<_Tp
, chrono::zoned_time
>)
505 # endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
507 static_assert(sizeof(_Tp
) == 0, "Add the missing type specialization");
511 _LIBCPP_HIDE_FROM_ABI
constexpr bool __date_ok(const _Tp
& __value
) {
512 if constexpr (__is_time_point
<_Tp
>)
514 else if constexpr (same_as
<_Tp
, chrono::day
>)
516 else if constexpr (same_as
<_Tp
, chrono::month
>)
518 else if constexpr (same_as
<_Tp
, chrono::year
>)
520 else if constexpr (same_as
<_Tp
, chrono::weekday
>)
522 else if constexpr (same_as
<_Tp
, chrono::weekday_indexed
>)
524 else if constexpr (same_as
<_Tp
, chrono::weekday_last
>)
526 else if constexpr (same_as
<_Tp
, chrono::month_day
>)
528 else if constexpr (same_as
<_Tp
, chrono::month_day_last
>)
530 else if constexpr (same_as
<_Tp
, chrono::month_weekday
>)
532 else if constexpr (same_as
<_Tp
, chrono::month_weekday_last
>)
534 else if constexpr (same_as
<_Tp
, chrono::year_month
>)
536 else if constexpr (same_as
<_Tp
, chrono::year_month_day
>)
538 else if constexpr (same_as
<_Tp
, chrono::year_month_day_last
>)
540 else if constexpr (same_as
<_Tp
, chrono::year_month_weekday
>)
542 else if constexpr (same_as
<_Tp
, chrono::year_month_weekday_last
>)
544 else if constexpr (__is_hh_mm_ss
<_Tp
>)
546 # if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
547 else if constexpr (same_as
<_Tp
, chrono::sys_info
>)
549 else if constexpr (same_as
<_Tp
, chrono::local_info
>)
551 # if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
552 else if constexpr (__is_specialization_v
<_Tp
, chrono::zoned_time
>)
555 # endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
557 static_assert(sizeof(_Tp
) == 0, "Add the missing type specialization");
561 _LIBCPP_HIDE_FROM_ABI
constexpr bool __month_name_ok(const _Tp
& __value
) {
562 if constexpr (__is_time_point
<_Tp
>)
564 else if constexpr (same_as
<_Tp
, chrono::day
>)
566 else if constexpr (same_as
<_Tp
, chrono::month
>)
568 else if constexpr (same_as
<_Tp
, chrono::year
>)
570 else if constexpr (same_as
<_Tp
, chrono::weekday
>)
572 else if constexpr (same_as
<_Tp
, chrono::weekday_indexed
>)
574 else if constexpr (same_as
<_Tp
, chrono::weekday_last
>)
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
>)
596 # if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
597 else if constexpr (same_as
<_Tp
, chrono::sys_info
>)
599 else if constexpr (same_as
<_Tp
, chrono::local_info
>)
601 # if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
602 else if constexpr (__is_specialization_v
<_Tp
, chrono::zoned_time
>)
605 # endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
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
;
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());
625 __sstr
.imbue(locale::classic());
627 if (__chrono_specs
.empty())
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
);
638 __formatter::__format_chrono_using_chrono_specs(__sstr
, __value
, __chrono_specs
);
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;
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
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
{
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
> {
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
> {
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
> {
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
> {
748 using _Base
= __formatter_chrono
<_CharT
>;
750 template <class _ParseContext
>
751 _LIBCPP_HIDE_FROM_ABI
constexpr typename
_ParseContext::iterator
parse(_ParseContext
& __ctx
) {
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
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
);
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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
> {
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