Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/libcxx/include/__chrono/formatter.h
35262 views
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(__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(__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(__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(__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(__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(__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("Formatting a weekday name needs a valid weekday");
651
652
if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))
653
std::__throw_format_error("Formatting a weekday needs a valid weekday");
654
655
if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))
656
std::__throw_format_error("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("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("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("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
992