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