1 // Formatting library for C++ - chrono support
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
13 #include <cmath> // std::isfinite
14 #include <cstring> // std::memcpy
19 #include <type_traits>
27 // UWP doesn't provide _tzset.
28 # if FMT_HAS_INCLUDE("winapifamily.h")
29 # include <winapifamily.h>
31 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
32 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
33 # define FMT_USE_TZSET 1
35 # define FMT_USE_TZSET 0
39 // Enable safe chrono durations, unless explicitly disabled.
40 #ifndef FMT_SAFE_DURATION_CAST
41 # define FMT_SAFE_DURATION_CAST 1
43 #if FMT_SAFE_DURATION_CAST
45 // For conversion between std::chrono::durations without undefined
46 // behaviour or erroneous results.
47 // This is a stripped down version of duration_cast, for inclusion in fmt.
48 // See https://github.com/pauldreik/safe_duration_cast
50 // Copyright Paul Dreik 2019
51 namespace safe_duration_cast
{
53 template <typename To
, typename From
,
54 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
55 std::numeric_limits
<From
>::is_signed
==
56 std::numeric_limits
<To
>::is_signed
)>
57 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
59 using F
= std::numeric_limits
<From
>;
60 using T
= std::numeric_limits
<To
>;
61 static_assert(F::is_integer
, "From must be integral");
62 static_assert(T::is_integer
, "To must be integral");
64 // A and B are both signed, or both unsigned.
65 if (detail::const_check(F::digits
<= T::digits
)) {
66 // From fits in To without any problem.
68 // From does not always fit in To, resort to a dynamic check.
69 if (from
< (T::min
)() || from
> (T::max
)()) {
75 return static_cast<To
>(from
);
79 * converts From to To, without loss. If the dynamic value of from
80 * can't be converted to To without loss, ec is set.
82 template <typename To
, typename From
,
83 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
84 std::numeric_limits
<From
>::is_signed
!=
85 std::numeric_limits
<To
>::is_signed
)>
86 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
88 using F
= std::numeric_limits
<From
>;
89 using T
= std::numeric_limits
<To
>;
90 static_assert(F::is_integer
, "From must be integral");
91 static_assert(T::is_integer
, "To must be integral");
93 if (detail::const_check(F::is_signed
&& !T::is_signed
)) {
94 // From may be negative, not allowed!
95 if (fmt::detail::is_negative(from
)) {
99 // From is positive. Can it always fit in To?
100 if (detail::const_check(F::digits
> T::digits
) &&
101 from
> static_cast<From
>(detail::max_value
<To
>())) {
107 if (detail::const_check(!F::is_signed
&& T::is_signed
&&
108 F::digits
>= T::digits
) &&
109 from
> static_cast<From
>(detail::max_value
<To
>())) {
113 return static_cast<To
>(from
); // Lossless conversion.
116 template <typename To
, typename From
,
117 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
118 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
125 * converts From to To if possible, otherwise ec is set.
128 * ---------------------------------|---------------
131 * normal, fits in output | converted (possibly lossy)
132 * normal, does not fit in output | ec is set
133 * subnormal | best effort
137 template <typename To
, typename From
,
138 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
)>
139 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
141 using T
= std::numeric_limits
<To
>;
142 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
143 static_assert(std::is_floating_point
<To
>::value
, "To must be floating");
145 // catch the only happy case
146 if (std::isfinite(from
)) {
147 if (from
>= T::lowest() && from
<= (T::max
)()) {
148 return static_cast<To
>(from
);
155 // nan and inf will be preserved
156 return static_cast<To
>(from
);
159 template <typename To
, typename From
,
160 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
161 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
163 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
168 * safe duration cast between integral durations
170 template <typename To
, typename FromRep
, typename FromPeriod
,
171 FMT_ENABLE_IF(std::is_integral
<FromRep
>::value
),
172 FMT_ENABLE_IF(std::is_integral
<typename
To::rep
>::value
)>
173 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
175 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
177 // the basic idea is that we need to convert from count() in the from type
178 // to count() in the To type, by multiplying it with this:
180 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
182 static_assert(Factor::num
> 0, "num must be positive");
183 static_assert(Factor::den
> 0, "den must be positive");
185 // the conversion is like this: multiply from.count() with Factor::num
186 // /Factor::den and convert it to To::rep, all this without
187 // overflow/underflow. let's start by finding a suitable type that can hold
188 // both To, From and Factor::num
189 using IntermediateRep
=
190 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
191 decltype(Factor::num
)>::type
;
193 // safe conversion to IntermediateRep
194 IntermediateRep count
=
195 lossless_integral_conversion
<IntermediateRep
>(from
.count(), ec
);
197 // multiply with Factor::num without overflow or underflow
198 if (detail::const_check(Factor::num
!= 1)) {
199 const auto max1
= detail::max_value
<IntermediateRep
>() / Factor::num
;
205 (std::numeric_limits
<IntermediateRep
>::min
)() / Factor::num
;
206 if (!std::is_unsigned
<IntermediateRep
>::value
&& count
< min1
) {
210 count
*= Factor::num
;
213 if (detail::const_check(Factor::den
!= 1)) count
/= Factor::den
;
214 auto tocount
= lossless_integral_conversion
<typename
To::rep
>(count
, ec
);
215 return ec
? To() : To(tocount
);
219 * safe duration_cast between floating point durations
221 template <typename To
, typename FromRep
, typename FromPeriod
,
222 FMT_ENABLE_IF(std::is_floating_point
<FromRep
>::value
),
223 FMT_ENABLE_IF(std::is_floating_point
<typename
To::rep
>::value
)>
224 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
226 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
228 if (std::isnan(from
.count())) {
229 // nan in, gives nan out. easy.
230 return To
{std::numeric_limits
<typename
To::rep
>::quiet_NaN()};
232 // maybe we should also check if from is denormal, and decide what to do about
235 // +-inf should be preserved.
236 if (std::isinf(from
.count())) {
237 return To
{from
.count()};
240 // the basic idea is that we need to convert from count() in the from type
241 // to count() in the To type, by multiplying it with this:
243 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
245 static_assert(Factor::num
> 0, "num must be positive");
246 static_assert(Factor::den
> 0, "den must be positive");
248 // the conversion is like this: multiply from.count() with Factor::num
249 // /Factor::den and convert it to To::rep, all this without
250 // overflow/underflow. let's start by finding a suitable type that can hold
251 // both To, From and Factor::num
252 using IntermediateRep
=
253 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
254 decltype(Factor::num
)>::type
;
256 // force conversion of From::rep -> IntermediateRep to be safe,
257 // even if it will never happen be narrowing in this context.
258 IntermediateRep count
=
259 safe_float_conversion
<IntermediateRep
>(from
.count(), ec
);
264 // multiply with Factor::num without overflow or underflow
265 if (detail::const_check(Factor::num
!= 1)) {
266 constexpr auto max1
= detail::max_value
<IntermediateRep
>() /
267 static_cast<IntermediateRep
>(Factor::num
);
272 constexpr auto min1
= std::numeric_limits
<IntermediateRep
>::lowest() /
273 static_cast<IntermediateRep
>(Factor::num
);
278 count
*= static_cast<IntermediateRep
>(Factor::num
);
281 // this can't go wrong, right? den>0 is checked earlier.
282 if (detail::const_check(Factor::den
!= 1)) {
283 using common_t
= typename
std::common_type
<IntermediateRep
, intmax_t>::type
;
284 count
/= static_cast<common_t
>(Factor::den
);
287 // convert to the to type, safely
288 using ToRep
= typename
To::rep
;
290 const ToRep tocount
= safe_float_conversion
<ToRep
>(count
, ec
);
296 } // namespace safe_duration_cast
299 // Prevents expansion of a preceding token as a function-style macro.
300 // Usage: f FMT_NOMACRO()
304 template <typename T
= void> struct null
{};
305 inline null
<> localtime_r
FMT_NOMACRO(...) { return null
<>(); }
306 inline null
<> localtime_s(...) { return null
<>(); }
307 inline null
<> gmtime_r(...) { return null
<>(); }
308 inline null
<> gmtime_s(...) { return null
<>(); }
310 inline const std::locale
& get_classic_locale() {
311 static const auto& locale
= std::locale::classic();
315 template <typename CodeUnit
> struct codecvt_result
{
316 static constexpr const size_t max_size
= 32;
317 CodeUnit buf
[max_size
];
320 template <typename CodeUnit
>
321 constexpr const size_t codecvt_result
<CodeUnit
>::max_size
;
323 template <typename CodeUnit
>
324 void write_codecvt(codecvt_result
<CodeUnit
>& out
, string_view in_buf
,
325 const std::locale
& loc
) {
326 #if FMT_CLANG_VERSION
327 # pragma clang diagnostic push
328 # pragma clang diagnostic ignored "-Wdeprecated"
329 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
330 # pragma clang diagnostic pop
332 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
334 auto mb
= std::mbstate_t();
335 const char* from_next
= nullptr;
336 auto result
= f
.in(mb
, in_buf
.begin(), in_buf
.end(), from_next
,
337 std::begin(out
.buf
), std::end(out
.buf
), out
.end
);
338 if (result
!= std::codecvt_base::ok
)
339 FMT_THROW(format_error("failed to format time"));
342 template <typename OutputIt
>
343 auto write_encoded_tm_str(OutputIt out
, string_view in
, const std::locale
& loc
)
345 if (detail::is_utf8() && loc
!= get_classic_locale()) {
346 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
348 #if FMT_MSC_VERSION != 0 || \
349 (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
350 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
352 using code_unit
= wchar_t;
354 using code_unit
= char32_t
;
357 using unit_t
= codecvt_result
<code_unit
>;
359 write_codecvt(unit
, in
, loc
);
360 // In UTF-8 is used one to four one-byte code units.
361 auto&& buf
= basic_memory_buffer
<char, unit_t::max_size
* 4>();
362 for (code_unit
* p
= unit
.buf
; p
!= unit
.end
; ++p
) {
363 uint32_t c
= static_cast<uint32_t>(*p
);
364 if (sizeof(code_unit
) == 2 && c
>= 0xd800 && c
<= 0xdfff) {
367 if (p
== unit
.end
|| (c
& 0xfc00) != 0xd800 ||
368 (*p
& 0xfc00) != 0xdc00) {
369 FMT_THROW(format_error("failed to format time"));
371 c
= (c
<< 10) + static_cast<uint32_t>(*p
) - 0x35fdc00;
374 buf
.push_back(static_cast<char>(c
));
375 } else if (c
< 0x800) {
376 buf
.push_back(static_cast<char>(0xc0 | (c
>> 6)));
377 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
378 } else if ((c
>= 0x800 && c
<= 0xd7ff) || (c
>= 0xe000 && c
<= 0xffff)) {
379 buf
.push_back(static_cast<char>(0xe0 | (c
>> 12)));
380 buf
.push_back(static_cast<char>(0x80 | ((c
& 0xfff) >> 6)));
381 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
382 } else if (c
>= 0x10000 && c
<= 0x10ffff) {
383 buf
.push_back(static_cast<char>(0xf0 | (c
>> 18)));
384 buf
.push_back(static_cast<char>(0x80 | ((c
& 0x3ffff) >> 12)));
385 buf
.push_back(static_cast<char>(0x80 | ((c
& 0xfff) >> 6)));
386 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
388 FMT_THROW(format_error("failed to format time"));
391 return copy_str
<char>(buf
.data(), buf
.data() + buf
.size(), out
);
393 return copy_str
<char>(in
.data(), in
.data() + in
.size(), out
);
396 template <typename Char
, typename OutputIt
,
397 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
398 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
400 codecvt_result
<Char
> unit
;
401 write_codecvt(unit
, sv
, loc
);
402 return copy_str
<Char
>(unit
.buf
, unit
.end
, out
);
405 template <typename Char
, typename OutputIt
,
406 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
407 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
409 return write_encoded_tm_str(out
, sv
, loc
);
412 template <typename Char
>
413 inline void do_write(buffer
<Char
>& buf
, const std::tm
& time
,
414 const std::locale
& loc
, char format
, char modifier
) {
415 auto&& format_buf
= formatbuf
<std::basic_streambuf
<Char
>>(buf
);
416 auto&& os
= std::basic_ostream
<Char
>(&format_buf
);
418 using iterator
= std::ostreambuf_iterator
<Char
>;
419 const auto& facet
= std::use_facet
<std::time_put
<Char
, iterator
>>(loc
);
420 auto end
= facet
.put(os
, os
, Char(' '), &time
, format
, modifier
);
421 if (end
.failed()) FMT_THROW(format_error("failed to format time"));
424 template <typename Char
, typename OutputIt
,
425 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
426 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
427 char format
, char modifier
= 0) -> OutputIt
{
428 auto&& buf
= get_buffer
<Char
>(out
);
429 do_write
<Char
>(buf
, time
, loc
, format
, modifier
);
433 template <typename Char
, typename OutputIt
,
434 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
435 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
436 char format
, char modifier
= 0) -> OutputIt
{
437 auto&& buf
= basic_memory_buffer
<Char
>();
438 do_write
<char>(buf
, time
, loc
, format
, modifier
);
439 return write_encoded_tm_str(out
, string_view(buf
.data(), buf
.size()), loc
);
442 } // namespace detail
444 FMT_MODULE_EXPORT_BEGIN
447 Converts given time since epoch as ``std::time_t`` value into calendar time,
448 expressed in local time. Unlike ``std::localtime``, this function is
449 thread-safe on most platforms.
451 inline std::tm
localtime(std::time_t time
) {
456 dispatcher(std::time_t t
) : time_(t
) {}
459 using namespace fmt::detail
;
460 return handle(localtime_r(&time_
, &tm_
));
463 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
465 bool handle(detail::null
<>) {
466 using namespace fmt::detail
;
467 return fallback(localtime_s(&tm_
, &time_
));
470 bool fallback(int res
) { return res
== 0; }
473 bool fallback(detail::null
<>) {
474 using namespace fmt::detail
;
475 std::tm
* tm
= std::localtime(&time_
);
477 return tm
!= nullptr;
482 // Too big time values may be unsupported.
483 if (!lt
.run()) FMT_THROW(format_error("time_t value out of range"));
487 inline std::tm
localtime(
488 std::chrono::time_point
<std::chrono::system_clock
> time_point
) {
489 return localtime(std::chrono::system_clock::to_time_t(time_point
));
493 Converts given time since epoch as ``std::time_t`` value into calendar time,
494 expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
495 function is thread-safe on most platforms.
497 inline std::tm
gmtime(std::time_t time
) {
502 dispatcher(std::time_t t
) : time_(t
) {}
505 using namespace fmt::detail
;
506 return handle(gmtime_r(&time_
, &tm_
));
509 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
511 bool handle(detail::null
<>) {
512 using namespace fmt::detail
;
513 return fallback(gmtime_s(&tm_
, &time_
));
516 bool fallback(int res
) { return res
== 0; }
519 bool fallback(detail::null
<>) {
520 std::tm
* tm
= std::gmtime(&time_
);
522 return tm
!= nullptr;
527 // Too big time values may be unsupported.
528 if (!gt
.run()) FMT_THROW(format_error("time_t value out of range"));
532 inline std::tm
gmtime(
533 std::chrono::time_point
<std::chrono::system_clock
> time_point
) {
534 return gmtime(std::chrono::system_clock::to_time_t(time_point
));
537 FMT_BEGIN_DETAIL_NAMESPACE
539 // Writes two-digit numbers a, b and c separated by sep to buf.
540 // The method by Pavel Novikov based on
541 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
542 inline void write_digit2_separated(char* buf
, unsigned a
, unsigned b
,
543 unsigned c
, char sep
) {
544 unsigned long long digits
=
545 a
| (b
<< 24) | (static_cast<unsigned long long>(c
) << 48);
546 // Convert each value to BCD.
547 // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
550 // a can be found from x:
553 // y = x + a * 6 = x + floor(x / 10) * 6
554 // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
555 digits
+= (((digits
* 205) >> 11) & 0x000f00000f00000f) * 6;
556 // Put low nibbles to high bytes and high nibbles to low bytes.
557 digits
= ((digits
& 0x00f00000f00000f0) >> 4) |
558 ((digits
& 0x000f00000f00000f) << 8);
559 auto usep
= static_cast<unsigned long long>(sep
);
560 // Add ASCII '0' to each digit byte and insert separators.
561 digits
|= 0x3030003030003030 | (usep
<< 16) | (usep
<< 40);
563 constexpr const size_t len
= 8;
564 if (const_check(is_big_endian())) {
566 std::memcpy(tmp
, &digits
, len
);
567 std::reverse_copy(tmp
, tmp
+ len
, buf
);
569 std::memcpy(buf
, &digits
, len
);
573 template <typename Period
> FMT_CONSTEXPR
inline const char* get_units() {
574 if (std::is_same
<Period
, std::atto
>::value
) return "as";
575 if (std::is_same
<Period
, std::femto
>::value
) return "fs";
576 if (std::is_same
<Period
, std::pico
>::value
) return "ps";
577 if (std::is_same
<Period
, std::nano
>::value
) return "ns";
578 if (std::is_same
<Period
, std::micro
>::value
) return "µs";
579 if (std::is_same
<Period
, std::milli
>::value
) return "ms";
580 if (std::is_same
<Period
, std::centi
>::value
) return "cs";
581 if (std::is_same
<Period
, std::deci
>::value
) return "ds";
582 if (std::is_same
<Period
, std::ratio
<1>>::value
) return "s";
583 if (std::is_same
<Period
, std::deca
>::value
) return "das";
584 if (std::is_same
<Period
, std::hecto
>::value
) return "hs";
585 if (std::is_same
<Period
, std::kilo
>::value
) return "ks";
586 if (std::is_same
<Period
, std::mega
>::value
) return "Ms";
587 if (std::is_same
<Period
, std::giga
>::value
) return "Gs";
588 if (std::is_same
<Period
, std::tera
>::value
) return "Ts";
589 if (std::is_same
<Period
, std::peta
>::value
) return "Ps";
590 if (std::is_same
<Period
, std::exa
>::value
) return "Es";
591 if (std::is_same
<Period
, std::ratio
<60>>::value
) return "m";
592 if (std::is_same
<Period
, std::ratio
<3600>>::value
) return "h";
596 enum class numeric_system
{
598 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
602 // Parses a put_time-like format string and invokes handler actions.
603 template <typename Char
, typename Handler
>
604 FMT_CONSTEXPR
const Char
* parse_chrono_format(const Char
* begin
,
615 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
616 ++ptr
; // consume '%'
617 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
621 handler
.on_text(ptr
- 1, ptr
);
624 const Char newline
[] = {'\n'};
625 handler
.on_text(newline
, newline
+ 1);
629 const Char tab
[] = {'\t'};
630 handler
.on_text(tab
, tab
+ 1);
635 handler
.on_year(numeric_system::standard
);
638 handler
.on_short_year(numeric_system::standard
);
641 handler
.on_century(numeric_system::standard
);
644 handler
.on_iso_week_based_year();
647 handler
.on_iso_week_based_short_year();
651 handler
.on_abbr_weekday();
654 handler
.on_full_weekday();
657 handler
.on_dec0_weekday(numeric_system::standard
);
660 handler
.on_dec1_weekday(numeric_system::standard
);
665 handler
.on_abbr_month();
668 handler
.on_full_month();
671 handler
.on_dec_month(numeric_system::standard
);
673 // Day of the year/month:
675 handler
.on_dec0_week_of_year(numeric_system::standard
);
678 handler
.on_dec1_week_of_year(numeric_system::standard
);
681 handler
.on_iso_week_of_year(numeric_system::standard
);
684 handler
.on_day_of_year();
687 handler
.on_day_of_month(numeric_system::standard
);
690 handler
.on_day_of_month_space(numeric_system::standard
);
692 // Hour, minute, second:
694 handler
.on_24_hour(numeric_system::standard
);
697 handler
.on_12_hour(numeric_system::standard
);
700 handler
.on_minute(numeric_system::standard
);
703 handler
.on_second(numeric_system::standard
);
707 handler
.on_datetime(numeric_system::standard
);
710 handler
.on_loc_date(numeric_system::standard
);
713 handler
.on_loc_time(numeric_system::standard
);
716 handler
.on_us_date();
719 handler
.on_iso_date();
722 handler
.on_12_hour_time();
725 handler
.on_24_hour_time();
728 handler
.on_iso_time();
734 handler
.on_duration_value();
737 handler
.on_duration_unit();
740 handler
.on_utc_offset();
743 handler
.on_tz_name();
745 // Alternative representation:
747 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
751 handler
.on_year(numeric_system::alternative
);
754 handler
.on_offset_year();
757 handler
.on_century(numeric_system::alternative
);
760 handler
.on_datetime(numeric_system::alternative
);
763 handler
.on_loc_date(numeric_system::alternative
);
766 handler
.on_loc_time(numeric_system::alternative
);
769 FMT_THROW(format_error("invalid format"));
774 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
778 handler
.on_short_year(numeric_system::alternative
);
781 handler
.on_dec_month(numeric_system::alternative
);
784 handler
.on_dec0_week_of_year(numeric_system::alternative
);
787 handler
.on_dec1_week_of_year(numeric_system::alternative
);
790 handler
.on_iso_week_of_year(numeric_system::alternative
);
793 handler
.on_day_of_month(numeric_system::alternative
);
796 handler
.on_day_of_month_space(numeric_system::alternative
);
799 handler
.on_dec0_weekday(numeric_system::alternative
);
802 handler
.on_dec1_weekday(numeric_system::alternative
);
805 handler
.on_24_hour(numeric_system::alternative
);
808 handler
.on_12_hour(numeric_system::alternative
);
811 handler
.on_minute(numeric_system::alternative
);
814 handler
.on_second(numeric_system::alternative
);
817 FMT_THROW(format_error("invalid format"));
821 FMT_THROW(format_error("invalid format"));
825 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
829 template <typename Derived
> struct null_chrono_spec_handler
{
830 FMT_CONSTEXPR
void unsupported() {
831 static_cast<Derived
*>(this)->unsupported();
833 FMT_CONSTEXPR
void on_year(numeric_system
) { unsupported(); }
834 FMT_CONSTEXPR
void on_short_year(numeric_system
) { unsupported(); }
835 FMT_CONSTEXPR
void on_offset_year() { unsupported(); }
836 FMT_CONSTEXPR
void on_century(numeric_system
) { unsupported(); }
837 FMT_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
838 FMT_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
839 FMT_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
840 FMT_CONSTEXPR
void on_full_weekday() { unsupported(); }
841 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) { unsupported(); }
842 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) { unsupported(); }
843 FMT_CONSTEXPR
void on_abbr_month() { unsupported(); }
844 FMT_CONSTEXPR
void on_full_month() { unsupported(); }
845 FMT_CONSTEXPR
void on_dec_month(numeric_system
) { unsupported(); }
846 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) { unsupported(); }
847 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) { unsupported(); }
848 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) { unsupported(); }
849 FMT_CONSTEXPR
void on_day_of_year() { unsupported(); }
850 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) { unsupported(); }
851 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) { unsupported(); }
852 FMT_CONSTEXPR
void on_24_hour(numeric_system
) { unsupported(); }
853 FMT_CONSTEXPR
void on_12_hour(numeric_system
) { unsupported(); }
854 FMT_CONSTEXPR
void on_minute(numeric_system
) { unsupported(); }
855 FMT_CONSTEXPR
void on_second(numeric_system
) { unsupported(); }
856 FMT_CONSTEXPR
void on_datetime(numeric_system
) { unsupported(); }
857 FMT_CONSTEXPR
void on_loc_date(numeric_system
) { unsupported(); }
858 FMT_CONSTEXPR
void on_loc_time(numeric_system
) { unsupported(); }
859 FMT_CONSTEXPR
void on_us_date() { unsupported(); }
860 FMT_CONSTEXPR
void on_iso_date() { unsupported(); }
861 FMT_CONSTEXPR
void on_12_hour_time() { unsupported(); }
862 FMT_CONSTEXPR
void on_24_hour_time() { unsupported(); }
863 FMT_CONSTEXPR
void on_iso_time() { unsupported(); }
864 FMT_CONSTEXPR
void on_am_pm() { unsupported(); }
865 FMT_CONSTEXPR
void on_duration_value() { unsupported(); }
866 FMT_CONSTEXPR
void on_duration_unit() { unsupported(); }
867 FMT_CONSTEXPR
void on_utc_offset() { unsupported(); }
868 FMT_CONSTEXPR
void on_tz_name() { unsupported(); }
871 struct tm_format_checker
: null_chrono_spec_handler
<tm_format_checker
> {
872 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no format")); }
874 template <typename Char
>
875 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
876 FMT_CONSTEXPR
void on_year(numeric_system
) {}
877 FMT_CONSTEXPR
void on_short_year(numeric_system
) {}
878 FMT_CONSTEXPR
void on_offset_year() {}
879 FMT_CONSTEXPR
void on_century(numeric_system
) {}
880 FMT_CONSTEXPR
void on_iso_week_based_year() {}
881 FMT_CONSTEXPR
void on_iso_week_based_short_year() {}
882 FMT_CONSTEXPR
void on_abbr_weekday() {}
883 FMT_CONSTEXPR
void on_full_weekday() {}
884 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) {}
885 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) {}
886 FMT_CONSTEXPR
void on_abbr_month() {}
887 FMT_CONSTEXPR
void on_full_month() {}
888 FMT_CONSTEXPR
void on_dec_month(numeric_system
) {}
889 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) {}
890 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) {}
891 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) {}
892 FMT_CONSTEXPR
void on_day_of_year() {}
893 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) {}
894 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) {}
895 FMT_CONSTEXPR
void on_24_hour(numeric_system
) {}
896 FMT_CONSTEXPR
void on_12_hour(numeric_system
) {}
897 FMT_CONSTEXPR
void on_minute(numeric_system
) {}
898 FMT_CONSTEXPR
void on_second(numeric_system
) {}
899 FMT_CONSTEXPR
void on_datetime(numeric_system
) {}
900 FMT_CONSTEXPR
void on_loc_date(numeric_system
) {}
901 FMT_CONSTEXPR
void on_loc_time(numeric_system
) {}
902 FMT_CONSTEXPR
void on_us_date() {}
903 FMT_CONSTEXPR
void on_iso_date() {}
904 FMT_CONSTEXPR
void on_12_hour_time() {}
905 FMT_CONSTEXPR
void on_24_hour_time() {}
906 FMT_CONSTEXPR
void on_iso_time() {}
907 FMT_CONSTEXPR
void on_am_pm() {}
908 FMT_CONSTEXPR
void on_utc_offset() {}
909 FMT_CONSTEXPR
void on_tz_name() {}
912 inline const char* tm_wday_full_name(int wday
) {
913 static constexpr const char* full_name_list
[] = {
914 "Sunday", "Monday", "Tuesday", "Wednesday",
915 "Thursday", "Friday", "Saturday"};
916 return wday
>= 0 && wday
<= 6 ? full_name_list
[wday
] : "?";
918 inline const char* tm_wday_short_name(int wday
) {
919 static constexpr const char* short_name_list
[] = {"Sun", "Mon", "Tue", "Wed",
920 "Thu", "Fri", "Sat"};
921 return wday
>= 0 && wday
<= 6 ? short_name_list
[wday
] : "???";
924 inline const char* tm_mon_full_name(int mon
) {
925 static constexpr const char* full_name_list
[] = {
926 "January", "February", "March", "April", "May", "June",
927 "July", "August", "September", "October", "November", "December"};
928 return mon
>= 0 && mon
<= 11 ? full_name_list
[mon
] : "?";
930 inline const char* tm_mon_short_name(int mon
) {
931 static constexpr const char* short_name_list
[] = {
932 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
933 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
935 return mon
>= 0 && mon
<= 11 ? short_name_list
[mon
] : "???";
938 template <typename T
, typename
= void>
939 struct has_member_data_tm_gmtoff
: std::false_type
{};
940 template <typename T
>
941 struct has_member_data_tm_gmtoff
<T
, void_t
<decltype(T::tm_gmtoff
)>>
944 template <typename T
, typename
= void>
945 struct has_member_data_tm_zone
: std::false_type
{};
946 template <typename T
>
947 struct has_member_data_tm_zone
<T
, void_t
<decltype(T::tm_zone
)>>
951 inline void tzset_once() {
952 static bool init
= []() -> bool {
960 template <typename OutputIt
, typename Char
> class tm_writer
{
962 static constexpr int days_per_week
= 7;
964 const std::locale
& loc_
;
965 const bool is_classic_
;
969 auto tm_sec() const noexcept
-> int {
970 FMT_ASSERT(tm_
.tm_sec
>= 0 && tm_
.tm_sec
<= 61, "");
973 auto tm_min() const noexcept
-> int {
974 FMT_ASSERT(tm_
.tm_min
>= 0 && tm_
.tm_min
<= 59, "");
977 auto tm_hour() const noexcept
-> int {
978 FMT_ASSERT(tm_
.tm_hour
>= 0 && tm_
.tm_hour
<= 23, "");
981 auto tm_mday() const noexcept
-> int {
982 FMT_ASSERT(tm_
.tm_mday
>= 1 && tm_
.tm_mday
<= 31, "");
985 auto tm_mon() const noexcept
-> int {
986 FMT_ASSERT(tm_
.tm_mon
>= 0 && tm_
.tm_mon
<= 11, "");
989 auto tm_year() const noexcept
-> long long { return 1900ll + tm_
.tm_year
; }
990 auto tm_wday() const noexcept
-> int {
991 FMT_ASSERT(tm_
.tm_wday
>= 0 && tm_
.tm_wday
<= 6, "");
994 auto tm_yday() const noexcept
-> int {
995 FMT_ASSERT(tm_
.tm_yday
>= 0 && tm_
.tm_yday
<= 365, "");
999 auto tm_hour12() const noexcept
-> int {
1000 const auto h
= tm_hour();
1001 const auto z
= h
< 12 ? h
: h
- 12;
1002 return z
== 0 ? 12 : z
;
1005 // POSIX and the C Standard are unclear or inconsistent about what %C and %y
1006 // do if the year is negative or exceeds 9999. Use the convention that %C
1007 // concatenated with %y yields the same output as %Y, and that %Y contains at
1008 // least 4 characters, with more only if necessary.
1009 auto split_year_lower(long long year
) const noexcept
-> int {
1010 auto l
= year
% 100;
1011 if (l
< 0) l
= -l
; // l in [0, 99]
1012 return static_cast<int>(l
);
1016 // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
1017 auto iso_year_weeks(long long curr_year
) const noexcept
-> int {
1018 const auto prev_year
= curr_year
- 1;
1020 (curr_year
+ curr_year
/ 4 - curr_year
/ 100 + curr_year
/ 400) %
1023 (prev_year
+ prev_year
/ 4 - prev_year
/ 100 + prev_year
/ 400) %
1025 return 52 + ((curr_p
== 4 || prev_p
== 3) ? 1 : 0);
1027 auto iso_week_num(int tm_yday
, int tm_wday
) const noexcept
-> int {
1028 return (tm_yday
+ 11 - (tm_wday
== 0 ? days_per_week
: tm_wday
)) /
1031 auto tm_iso_week_year() const noexcept
-> long long {
1032 const auto year
= tm_year();
1033 const auto w
= iso_week_num(tm_yday(), tm_wday());
1034 if (w
< 1) return year
- 1;
1035 if (w
> iso_year_weeks(year
)) return year
+ 1;
1038 auto tm_iso_week_of_year() const noexcept
-> int {
1039 const auto year
= tm_year();
1040 const auto w
= iso_week_num(tm_yday(), tm_wday());
1041 if (w
< 1) return iso_year_weeks(year
- 1);
1042 if (w
> iso_year_weeks(year
)) return 1;
1046 void write1(int value
) {
1047 *out_
++ = static_cast<char>('0' + to_unsigned(value
) % 10);
1049 void write2(int value
) {
1050 const char* d
= digits2(to_unsigned(value
) % 100);
1055 void write_year_extended(long long year
) {
1056 // At least 4 characters.
1063 uint32_or_64_or_128_t
<long long> n
= to_unsigned(year
);
1064 const int num_digits
= count_digits(n
);
1065 if (width
> num_digits
) out_
= std::fill_n(out_
, width
- num_digits
, '0');
1066 out_
= format_decimal
<Char
>(out_
, n
, num_digits
).end
;
1068 void write_year(long long year
) {
1069 if (year
>= 0 && year
< 10000) {
1070 write2(static_cast<int>(year
/ 100));
1071 write2(static_cast<int>(year
% 100));
1073 write_year_extended(year
);
1077 void write_utc_offset(long offset
) {
1085 write2(static_cast<int>(offset
/ 60));
1086 write2(static_cast<int>(offset
% 60));
1088 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_gmtoff
<T
>::value
)>
1089 void format_utc_offset_impl(const T
& tm
) {
1090 write_utc_offset(tm
.tm_gmtoff
);
1092 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_gmtoff
<T
>::value
)>
1093 void format_utc_offset_impl(const T
& tm
) {
1094 #if defined(_WIN32) && defined(_UCRT)
1099 _get_timezone(&offset
);
1102 _get_dstbias(&dstbias
);
1105 write_utc_offset(-offset
);
1108 format_localized('z');
1112 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_zone
<T
>::value
)>
1113 void format_tz_name_impl(const T
& tm
) {
1115 out_
= write_tm_str
<Char
>(out_
, tm
.tm_zone
, loc_
);
1117 format_localized('Z');
1119 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_zone
<T
>::value
)>
1120 void format_tz_name_impl(const T
&) {
1121 format_localized('Z');
1124 void format_localized(char format
, char modifier
= 0) {
1125 out_
= write
<Char
>(out_
, tm_
, loc_
, format
, modifier
);
1129 tm_writer(const std::locale
& loc
, OutputIt out
, const std::tm
& tm
)
1131 is_classic_(loc_
== get_classic_locale()),
1135 OutputIt
out() const { return out_
; }
1137 FMT_CONSTEXPR
void on_text(const Char
* begin
, const Char
* end
) {
1138 out_
= copy_str
<Char
>(begin
, end
, out_
);
1141 void on_abbr_weekday() {
1143 out_
= write(out_
, tm_wday_short_name(tm_wday()));
1145 format_localized('a');
1147 void on_full_weekday() {
1149 out_
= write(out_
, tm_wday_full_name(tm_wday()));
1151 format_localized('A');
1153 void on_dec0_weekday(numeric_system ns
) {
1154 if (is_classic_
|| ns
== numeric_system::standard
) return write1(tm_wday());
1155 format_localized('w', 'O');
1157 void on_dec1_weekday(numeric_system ns
) {
1158 if (is_classic_
|| ns
== numeric_system::standard
) {
1159 auto wday
= tm_wday();
1160 write1(wday
== 0 ? days_per_week
: wday
);
1162 format_localized('u', 'O');
1166 void on_abbr_month() {
1168 out_
= write(out_
, tm_mon_short_name(tm_mon()));
1170 format_localized('b');
1172 void on_full_month() {
1174 out_
= write(out_
, tm_mon_full_name(tm_mon()));
1176 format_localized('B');
1179 void on_datetime(numeric_system ns
) {
1185 on_day_of_month_space(numeric_system::standard
);
1189 on_year(numeric_system::standard
);
1191 format_localized('c', ns
== numeric_system::standard
? '\0' : 'E');
1194 void on_loc_date(numeric_system ns
) {
1198 format_localized('x', ns
== numeric_system::standard
? '\0' : 'E');
1200 void on_loc_time(numeric_system ns
) {
1204 format_localized('X', ns
== numeric_system::standard
? '\0' : 'E');
1208 write_digit2_separated(buf
, to_unsigned(tm_mon() + 1),
1209 to_unsigned(tm_mday()),
1210 to_unsigned(split_year_lower(tm_year())), '/');
1211 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1213 void on_iso_date() {
1214 auto year
= tm_year();
1217 if (year
>= 0 && year
< 10000) {
1218 copy2(buf
, digits2(static_cast<size_t>(year
/ 100)));
1221 write_year_extended(year
);
1224 write_digit2_separated(buf
+ 2, static_cast<unsigned>(year
% 100),
1225 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1227 out_
= copy_str
<Char
>(std::begin(buf
) + offset
, std::end(buf
), out_
);
1230 void on_utc_offset() { format_utc_offset_impl(tm_
); }
1231 void on_tz_name() { format_tz_name_impl(tm_
); }
1233 void on_year(numeric_system ns
) {
1234 if (is_classic_
|| ns
== numeric_system::standard
)
1235 return write_year(tm_year());
1236 format_localized('Y', 'E');
1238 void on_short_year(numeric_system ns
) {
1239 if (is_classic_
|| ns
== numeric_system::standard
)
1240 return write2(split_year_lower(tm_year()));
1241 format_localized('y', 'O');
1243 void on_offset_year() {
1244 if (is_classic_
) return write2(split_year_lower(tm_year()));
1245 format_localized('y', 'E');
1248 void on_century(numeric_system ns
) {
1249 if (is_classic_
|| ns
== numeric_system::standard
) {
1250 auto year
= tm_year();
1251 auto upper
= year
/ 100;
1252 if (year
>= -99 && year
< 0) {
1253 // Zero upper on negative year.
1256 } else if (upper
>= 0 && upper
< 100) {
1257 write2(static_cast<int>(upper
));
1259 out_
= write
<Char
>(out_
, upper
);
1262 format_localized('C', 'E');
1266 void on_dec_month(numeric_system ns
) {
1267 if (is_classic_
|| ns
== numeric_system::standard
)
1268 return write2(tm_mon() + 1);
1269 format_localized('m', 'O');
1272 void on_dec0_week_of_year(numeric_system ns
) {
1273 if (is_classic_
|| ns
== numeric_system::standard
)
1274 return write2((tm_yday() + days_per_week
- tm_wday()) / days_per_week
);
1275 format_localized('U', 'O');
1277 void on_dec1_week_of_year(numeric_system ns
) {
1278 if (is_classic_
|| ns
== numeric_system::standard
) {
1279 auto wday
= tm_wday();
1280 write2((tm_yday() + days_per_week
-
1281 (wday
== 0 ? (days_per_week
- 1) : (wday
- 1))) /
1284 format_localized('W', 'O');
1287 void on_iso_week_of_year(numeric_system ns
) {
1288 if (is_classic_
|| ns
== numeric_system::standard
)
1289 return write2(tm_iso_week_of_year());
1290 format_localized('V', 'O');
1293 void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
1294 void on_iso_week_based_short_year() {
1295 write2(split_year_lower(tm_iso_week_year()));
1298 void on_day_of_year() {
1299 auto yday
= tm_yday() + 1;
1303 void on_day_of_month(numeric_system ns
) {
1304 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_mday());
1305 format_localized('d', 'O');
1307 void on_day_of_month_space(numeric_system ns
) {
1308 if (is_classic_
|| ns
== numeric_system::standard
) {
1309 auto mday
= to_unsigned(tm_mday()) % 100;
1310 const char* d2
= digits2(mday
);
1311 *out_
++ = mday
< 10 ? ' ' : d2
[0];
1314 format_localized('e', 'O');
1318 void on_24_hour(numeric_system ns
) {
1319 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_hour());
1320 format_localized('H', 'O');
1322 void on_12_hour(numeric_system ns
) {
1323 if (is_classic_
|| ns
== numeric_system::standard
)
1324 return write2(tm_hour12());
1325 format_localized('I', 'O');
1327 void on_minute(numeric_system ns
) {
1328 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_min());
1329 format_localized('M', 'O');
1331 void on_second(numeric_system ns
) {
1332 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_sec());
1333 format_localized('S', 'O');
1336 void on_12_hour_time() {
1339 write_digit2_separated(buf
, to_unsigned(tm_hour12()),
1340 to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
1341 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1345 format_localized('r');
1348 void on_24_hour_time() {
1353 void on_iso_time() {
1355 write_digit2_separated(buf
, to_unsigned(tm_hour()), to_unsigned(tm_min()),
1356 to_unsigned(tm_sec()), ':');
1357 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1362 *out_
++ = tm_hour() < 12 ? 'A' : 'P';
1365 format_localized('p');
1369 // These apply to chrono durations but not tm.
1370 void on_duration_value() {}
1371 void on_duration_unit() {}
1374 struct chrono_format_checker
: null_chrono_spec_handler
<chrono_format_checker
> {
1375 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no date")); }
1377 template <typename Char
>
1378 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
1379 FMT_CONSTEXPR
void on_24_hour(numeric_system
) {}
1380 FMT_CONSTEXPR
void on_12_hour(numeric_system
) {}
1381 FMT_CONSTEXPR
void on_minute(numeric_system
) {}
1382 FMT_CONSTEXPR
void on_second(numeric_system
) {}
1383 FMT_CONSTEXPR
void on_12_hour_time() {}
1384 FMT_CONSTEXPR
void on_24_hour_time() {}
1385 FMT_CONSTEXPR
void on_iso_time() {}
1386 FMT_CONSTEXPR
void on_am_pm() {}
1387 FMT_CONSTEXPR
void on_duration_value() {}
1388 FMT_CONSTEXPR
void on_duration_unit() {}
1391 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1392 inline bool isfinite(T
) {
1396 // Converts value to Int and checks that it's in the range [0, upper).
1397 template <typename T
, typename Int
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1398 inline Int
to_nonnegative_int(T value
, Int upper
) {
1399 FMT_ASSERT(std::is_unsigned
<Int
>::value
||
1400 (value
>= 0 && to_unsigned(value
) <= to_unsigned(upper
)),
1403 return static_cast<Int
>(value
);
1405 template <typename T
, typename Int
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
1406 inline Int
to_nonnegative_int(T value
, Int upper
) {
1407 if (value
< 0 || value
> static_cast<T
>(upper
))
1408 FMT_THROW(format_error("invalid value"));
1409 return static_cast<Int
>(value
);
1412 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1413 inline T
mod(T x
, int y
) {
1414 return x
% static_cast<T
>(y
);
1416 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
1417 inline T
mod(T x
, int y
) {
1418 return std::fmod(x
, static_cast<T
>(y
));
1421 // If T is an integral type, maps T to its unsigned counterpart, otherwise
1422 // leaves it unchanged (unlike std::make_unsigned).
1423 template <typename T
, bool INTEGRAL
= std::is_integral
<T
>::value
>
1424 struct make_unsigned_or_unchanged
{
1428 template <typename T
> struct make_unsigned_or_unchanged
<T
, true> {
1429 using type
= typename
std::make_unsigned
<T
>::type
;
1432 #if FMT_SAFE_DURATION_CAST
1433 // throwing version of safe_duration_cast
1434 template <typename To
, typename FromRep
, typename FromPeriod
>
1435 To
fmt_safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
) {
1437 To to
= safe_duration_cast::safe_duration_cast
<To
>(from
, ec
);
1438 if (ec
) FMT_THROW(format_error("cannot format duration"));
1443 template <typename Rep
, typename Period
,
1444 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1445 inline std::chrono::duration
<Rep
, std::milli
> get_milliseconds(
1446 std::chrono::duration
<Rep
, Period
> d
) {
1447 // this may overflow and/or the result may not fit in the
1449 #if FMT_SAFE_DURATION_CAST
1450 using CommonSecondsType
=
1451 typename
std::common_type
<decltype(d
), std::chrono::seconds
>::type
;
1452 const auto d_as_common
= fmt_safe_duration_cast
<CommonSecondsType
>(d
);
1453 const auto d_as_whole_seconds
=
1454 fmt_safe_duration_cast
<std::chrono::seconds
>(d_as_common
);
1455 // this conversion should be nonproblematic
1456 const auto diff
= d_as_common
- d_as_whole_seconds
;
1458 fmt_safe_duration_cast
<std::chrono::duration
<Rep
, std::milli
>>(diff
);
1461 auto s
= std::chrono::duration_cast
<std::chrono::seconds
>(d
);
1462 return std::chrono::duration_cast
<std::chrono::milliseconds
>(d
- s
);
1466 // Counts the number of fractional digits in the range [0, 18] according to the
1467 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
1468 // microseconds precision.
1469 template <long long Num
, long long Den
, int N
= 0,
1470 bool Enabled
= (N
< 19) && (Num
<= max_value
<long long>() / 10)>
1471 struct count_fractional_digits
{
1472 static constexpr int value
=
1473 Num
% Den
== 0 ? N
: count_fractional_digits
<Num
* 10, Den
, N
+ 1>::value
;
1476 // Base case that doesn't instantiate any more templates
1477 // in order to avoid overflow.
1478 template <long long Num
, long long Den
, int N
>
1479 struct count_fractional_digits
<Num
, Den
, N
, false> {
1480 static constexpr int value
= (Num
% Den
== 0) ? N
: 6;
1483 constexpr long long pow10(std::uint32_t n
) {
1484 return n
== 0 ? 1 : 10 * pow10(n
- 1);
1487 template <class Rep
, class Period
,
1488 FMT_ENABLE_IF(std::numeric_limits
<Rep
>::is_signed
)>
1489 constexpr std::chrono::duration
<Rep
, Period
> abs(
1490 std::chrono::duration
<Rep
, Period
> d
) {
1491 // We need to compare the duration using the count() method directly
1492 // due to a compiler bug in clang-11 regarding the spaceship operator,
1493 // when -Wzero-as-null-pointer-constant is enabled.
1494 // In clang-12 the bug has been fixed. See
1495 // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
1496 // https://www.godbolt.org/z/Knbb5joYx.
1497 return d
.count() >= d
.zero().count() ? d
: -d
;
1500 template <class Rep
, class Period
,
1501 FMT_ENABLE_IF(!std::numeric_limits
<Rep
>::is_signed
)>
1502 constexpr std::chrono::duration
<Rep
, Period
> abs(
1503 std::chrono::duration
<Rep
, Period
> d
) {
1507 template <typename Char
, typename Rep
, typename OutputIt
,
1508 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1509 OutputIt
format_duration_value(OutputIt out
, Rep val
, int) {
1510 return write
<Char
>(out
, val
);
1513 template <typename Char
, typename Rep
, typename OutputIt
,
1514 FMT_ENABLE_IF(std::is_floating_point
<Rep
>::value
)>
1515 OutputIt
format_duration_value(OutputIt out
, Rep val
, int precision
) {
1516 auto specs
= basic_format_specs
<Char
>();
1517 specs
.precision
= precision
;
1518 specs
.type
= precision
>= 0 ? presentation_type::fixed_lower
1519 : presentation_type::general_lower
;
1520 return write
<Char
>(out
, val
, specs
);
1523 template <typename Char
, typename OutputIt
>
1524 OutputIt
copy_unit(string_view unit
, OutputIt out
, Char
) {
1525 return std::copy(unit
.begin(), unit
.end(), out
);
1528 template <typename OutputIt
>
1529 OutputIt
copy_unit(string_view unit
, OutputIt out
, wchar_t) {
1530 // This works when wchar_t is UTF-32 because units only contain characters
1531 // that have the same representation in UTF-16 and UTF-32.
1532 utf8_to_utf16
u(unit
);
1533 return std::copy(u
.c_str(), u
.c_str() + u
.size(), out
);
1536 template <typename Char
, typename Period
, typename OutputIt
>
1537 OutputIt
format_duration_unit(OutputIt out
) {
1538 if (const char* unit
= get_units
<Period
>())
1539 return copy_unit(string_view(unit
), out
, Char());
1541 out
= write
<Char
>(out
, Period::num
);
1542 if (const_check(Period::den
!= 1)) {
1544 out
= write
<Char
>(out
, Period::den
);
1554 std::locale locale_
;
1556 bool has_locale_
= false;
1559 get_locale(bool localized
, locale_ref loc
) : has_locale_(localized
) {
1561 ::new (&locale_
) std::locale(loc
.template get
<std::locale
>());
1564 if (has_locale_
) locale_
.~locale();
1566 operator const std::locale
&() const {
1567 return has_locale_
? locale_
: get_classic_locale();
1571 template <typename FormatContext
, typename OutputIt
, typename Rep
,
1573 struct chrono_formatter
{
1574 FormatContext
& context
;
1577 bool localized
= false;
1578 // rep is unsigned to avoid overflow.
1580 conditional_t
<std::is_integral
<Rep
>::value
&& sizeof(Rep
) < sizeof(int),
1581 unsigned, typename make_unsigned_or_unchanged
<Rep
>::type
>;
1583 using seconds
= std::chrono::duration
<rep
>;
1585 using milliseconds
= std::chrono::duration
<rep
, std::milli
>;
1588 using char_type
= typename
FormatContext::char_type
;
1589 using tm_writer_type
= tm_writer
<OutputIt
, char_type
>;
1591 chrono_formatter(FormatContext
& ctx
, OutputIt o
,
1592 std::chrono::duration
<Rep
, Period
> d
)
1595 val(static_cast<rep
>(d
.count())),
1597 if (d
.count() < 0) {
1602 // this may overflow and/or the result may not fit in the
1604 #if FMT_SAFE_DURATION_CAST
1605 // might need checked conversion (rep!=Rep)
1606 auto tmpval
= std::chrono::duration
<rep
, Period
>(val
);
1607 s
= fmt_safe_duration_cast
<seconds
>(tmpval
);
1609 s
= std::chrono::duration_cast
<seconds
>(
1610 std::chrono::duration
<rep
, Period
>(val
));
1614 // returns true if nan or inf, writes to out.
1615 bool handle_nan_inf() {
1616 if (isfinite(val
)) {
1632 Rep
hour() const { return static_cast<Rep
>(mod((s
.count() / 3600), 24)); }
1634 Rep
hour12() const {
1635 Rep hour
= static_cast<Rep
>(mod((s
.count() / 3600), 12));
1636 return hour
<= 0 ? 12 : hour
;
1639 Rep
minute() const { return static_cast<Rep
>(mod((s
.count() / 60), 60)); }
1640 Rep
second() const { return static_cast<Rep
>(mod(s
.count(), 60)); }
1642 std::tm
time() const {
1643 auto time
= std::tm();
1644 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1645 time
.tm_min
= to_nonnegative_int(minute(), 60);
1646 time
.tm_sec
= to_nonnegative_int(second(), 60);
1657 void write(Rep value
, int width
) {
1659 if (isnan(value
)) return write_nan();
1660 uint32_or_64_or_128_t
<int> n
=
1661 to_unsigned(to_nonnegative_int(value
, max_value
<int>()));
1662 int num_digits
= detail::count_digits(n
);
1663 if (width
> num_digits
) out
= std::fill_n(out
, width
- num_digits
, '0');
1664 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1667 template <typename Duration
> void write_fractional_seconds(Duration d
) {
1668 FMT_ASSERT(!std::is_floating_point
<typename
Duration::rep
>::value
, "");
1669 constexpr auto num_fractional_digits
=
1670 count_fractional_digits
<Duration::period::num
,
1671 Duration::period::den
>::value
;
1673 using subsecond_precision
= std::chrono::duration
<
1674 typename
std::common_type
<typename
Duration::rep
,
1675 std::chrono::seconds::rep
>::type
,
1676 std::ratio
<1, detail::pow10(num_fractional_digits
)>>;
1677 if (std::ratio_less
<typename
subsecond_precision::period
,
1678 std::chrono::seconds::period
>::value
) {
1681 detail::abs(d
) - std::chrono::duration_cast
<std::chrono::seconds
>(d
);
1683 std::chrono::treat_as_floating_point
<
1684 typename
subsecond_precision::rep
>::value
1685 ? fractional
.count()
1686 : std::chrono::duration_cast
<subsecond_precision
>(fractional
)
1688 uint32_or_64_or_128_t
<long long> n
=
1689 to_unsigned(to_nonnegative_int(subseconds
, max_value
<long long>()));
1690 int num_digits
= detail::count_digits(n
);
1691 if (num_fractional_digits
> num_digits
)
1692 out
= std::fill_n(out
, num_fractional_digits
- num_digits
, '0');
1693 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1697 void write_nan() { std::copy_n("nan", 3, out
); }
1698 void write_pinf() { std::copy_n("inf", 3, out
); }
1699 void write_ninf() { std::copy_n("-inf", 4, out
); }
1701 template <typename Callback
, typename
... Args
>
1702 void format_tm(const tm
& time
, Callback cb
, Args
... args
) {
1703 if (isnan(val
)) return write_nan();
1704 get_locale
loc(localized
, context
.locale());
1705 auto w
= tm_writer_type(loc
, out
, time
);
1710 void on_text(const char_type
* begin
, const char_type
* end
) {
1711 std::copy(begin
, end
, out
);
1714 // These are not implemented because durations don't have date information.
1715 void on_abbr_weekday() {}
1716 void on_full_weekday() {}
1717 void on_dec0_weekday(numeric_system
) {}
1718 void on_dec1_weekday(numeric_system
) {}
1719 void on_abbr_month() {}
1720 void on_full_month() {}
1721 void on_datetime(numeric_system
) {}
1722 void on_loc_date(numeric_system
) {}
1723 void on_loc_time(numeric_system
) {}
1724 void on_us_date() {}
1725 void on_iso_date() {}
1726 void on_utc_offset() {}
1727 void on_tz_name() {}
1728 void on_year(numeric_system
) {}
1729 void on_short_year(numeric_system
) {}
1730 void on_offset_year() {}
1731 void on_century(numeric_system
) {}
1732 void on_iso_week_based_year() {}
1733 void on_iso_week_based_short_year() {}
1734 void on_dec_month(numeric_system
) {}
1735 void on_dec0_week_of_year(numeric_system
) {}
1736 void on_dec1_week_of_year(numeric_system
) {}
1737 void on_iso_week_of_year(numeric_system
) {}
1738 void on_day_of_year() {}
1739 void on_day_of_month(numeric_system
) {}
1740 void on_day_of_month_space(numeric_system
) {}
1742 void on_24_hour(numeric_system ns
) {
1743 if (handle_nan_inf()) return;
1745 if (ns
== numeric_system::standard
) return write(hour(), 2);
1747 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1748 format_tm(time
, &tm_writer_type::on_24_hour
, ns
);
1751 void on_12_hour(numeric_system ns
) {
1752 if (handle_nan_inf()) return;
1754 if (ns
== numeric_system::standard
) return write(hour12(), 2);
1756 time
.tm_hour
= to_nonnegative_int(hour12(), 12);
1757 format_tm(time
, &tm_writer_type::on_12_hour
, ns
);
1760 void on_minute(numeric_system ns
) {
1761 if (handle_nan_inf()) return;
1763 if (ns
== numeric_system::standard
) return write(minute(), 2);
1765 time
.tm_min
= to_nonnegative_int(minute(), 60);
1766 format_tm(time
, &tm_writer_type::on_minute
, ns
);
1769 void on_second(numeric_system ns
) {
1770 if (handle_nan_inf()) return;
1772 if (ns
== numeric_system::standard
) {
1773 if (std::is_floating_point
<rep
>::value
) {
1774 constexpr auto num_fractional_digits
=
1775 count_fractional_digits
<Period::num
, Period::den
>::value
;
1776 auto buf
= memory_buffer();
1777 format_to(std::back_inserter(buf
), runtime("{:.{}f}"),
1778 std::fmod(val
* static_cast<rep
>(Period::num
) /
1779 static_cast<rep
>(Period::den
),
1780 static_cast<rep
>(60)),
1781 num_fractional_digits
);
1782 if (negative
) *out
++ = '-';
1783 if (buf
.size() < 2 || buf
[1] == '.') *out
++ = '0';
1784 out
= std::copy(buf
.begin(), buf
.end(), out
);
1787 write_fractional_seconds(std::chrono::duration
<rep
, Period
>(val
));
1792 time
.tm_sec
= to_nonnegative_int(second(), 60);
1793 format_tm(time
, &tm_writer_type::on_second
, ns
);
1796 void on_12_hour_time() {
1797 if (handle_nan_inf()) return;
1798 format_tm(time(), &tm_writer_type::on_12_hour_time
);
1801 void on_24_hour_time() {
1802 if (handle_nan_inf()) {
1813 void on_iso_time() {
1816 if (handle_nan_inf()) return;
1817 on_second(numeric_system::standard
);
1821 if (handle_nan_inf()) return;
1822 format_tm(time(), &tm_writer_type::on_am_pm
);
1825 void on_duration_value() {
1826 if (handle_nan_inf()) return;
1828 out
= format_duration_value
<char_type
>(out
, val
, precision
);
1831 void on_duration_unit() {
1832 out
= format_duration_unit
<char_type
, Period
>(out
);
1836 FMT_END_DETAIL_NAMESPACE
1838 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1839 using weekday
= std::chrono::weekday
;
1841 // A fallback version of weekday.
1844 unsigned char value
;
1847 weekday() = default;
1848 explicit constexpr weekday(unsigned wd
) noexcept
1849 : value(static_cast<unsigned char>(wd
!= 7 ? wd
: 0)) {}
1850 constexpr unsigned c_encoding() const noexcept
{ return value
; }
1853 class year_month_day
{};
1856 // A rudimentary weekday formatter.
1857 template <typename Char
> struct formatter
<weekday
, Char
> {
1859 bool localized
= false;
1862 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
1863 -> decltype(ctx
.begin()) {
1864 auto begin
= ctx
.begin(), end
= ctx
.end();
1865 if (begin
!= end
&& *begin
== 'L') {
1872 template <typename FormatContext
>
1873 auto format(weekday wd
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
1874 auto time
= std::tm();
1875 time
.tm_wday
= static_cast<int>(wd
.c_encoding());
1876 detail::get_locale
loc(localized
, ctx
.locale());
1877 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
1878 w
.on_abbr_weekday();
1883 template <typename Rep
, typename Period
, typename Char
>
1884 struct formatter
<std::chrono::duration
<Rep
, Period
>, Char
> {
1886 basic_format_specs
<Char
> specs
;
1888 using arg_ref_type
= detail::arg_ref
<Char
>;
1889 arg_ref_type width_ref
;
1890 arg_ref_type precision_ref
;
1891 bool localized
= false;
1892 basic_string_view
<Char
> format_str
;
1893 using duration
= std::chrono::duration
<Rep
, Period
>;
1895 struct spec_handler
{
1897 basic_format_parse_context
<Char
>& context
;
1898 basic_string_view
<Char
> format_str
;
1900 template <typename Id
> FMT_CONSTEXPR arg_ref_type
make_arg_ref(Id arg_id
) {
1901 context
.check_arg_id(arg_id
);
1902 return arg_ref_type(arg_id
);
1905 FMT_CONSTEXPR arg_ref_type
make_arg_ref(basic_string_view
<Char
> arg_id
) {
1906 context
.check_arg_id(arg_id
);
1907 return arg_ref_type(arg_id
);
1910 FMT_CONSTEXPR arg_ref_type
make_arg_ref(detail::auto_id
) {
1911 return arg_ref_type(context
.next_arg_id());
1914 void on_error(const char* msg
) { FMT_THROW(format_error(msg
)); }
1915 FMT_CONSTEXPR
void on_fill(basic_string_view
<Char
> fill
) {
1916 f
.specs
.fill
= fill
;
1918 FMT_CONSTEXPR
void on_align(align_t align
) { f
.specs
.align
= align
; }
1919 FMT_CONSTEXPR
void on_width(int width
) { f
.specs
.width
= width
; }
1920 FMT_CONSTEXPR
void on_precision(int _precision
) {
1921 f
.precision
= _precision
;
1923 FMT_CONSTEXPR
void end_precision() {}
1925 template <typename Id
> FMT_CONSTEXPR
void on_dynamic_width(Id arg_id
) {
1926 f
.width_ref
= make_arg_ref(arg_id
);
1929 template <typename Id
> FMT_CONSTEXPR
void on_dynamic_precision(Id arg_id
) {
1930 f
.precision_ref
= make_arg_ref(arg_id
);
1934 using iterator
= typename basic_format_parse_context
<Char
>::iterator
;
1935 struct parse_range
{
1940 FMT_CONSTEXPR parse_range
do_parse(basic_format_parse_context
<Char
>& ctx
) {
1941 auto begin
= ctx
.begin(), end
= ctx
.end();
1942 if (begin
== end
|| *begin
== '}') return {begin
, begin
};
1943 spec_handler handler
{*this, ctx
, format_str
};
1944 begin
= detail::parse_align(begin
, end
, handler
);
1945 if (begin
== end
) return {begin
, begin
};
1946 begin
= detail::parse_width(begin
, end
, handler
);
1947 if (begin
== end
) return {begin
, begin
};
1948 if (*begin
== '.') {
1949 if (std::is_floating_point
<Rep
>::value
)
1950 begin
= detail::parse_precision(begin
, end
, handler
);
1952 handler
.on_error("precision not allowed for this argument type");
1954 if (begin
!= end
&& *begin
== 'L') {
1958 end
= detail::parse_chrono_format(begin
, end
,
1959 detail::chrono_format_checker());
1960 return {begin
, end
};
1964 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
1965 -> decltype(ctx
.begin()) {
1966 auto range
= do_parse(ctx
);
1967 format_str
= basic_string_view
<Char
>(
1968 &*range
.begin
, detail::to_unsigned(range
.end
- range
.begin
));
1972 template <typename FormatContext
>
1973 auto format(const duration
& d
, FormatContext
& ctx
) const
1974 -> decltype(ctx
.out()) {
1975 auto specs_copy
= specs
;
1976 auto precision_copy
= precision
;
1977 auto begin
= format_str
.begin(), end
= format_str
.end();
1978 // As a possible future optimization, we could avoid extra copying if width
1979 // is not specified.
1980 basic_memory_buffer
<Char
> buf
;
1981 auto out
= std::back_inserter(buf
);
1982 detail::handle_dynamic_spec
<detail::width_checker
>(specs_copy
.width
,
1984 detail::handle_dynamic_spec
<detail::precision_checker
>(precision_copy
,
1985 precision_ref
, ctx
);
1986 if (begin
== end
|| *begin
== '}') {
1987 out
= detail::format_duration_value
<Char
>(out
, d
.count(), precision_copy
);
1988 detail::format_duration_unit
<Char
, Period
>(out
);
1990 detail::chrono_formatter
<FormatContext
, decltype(out
), Rep
, Period
> f(
1992 f
.precision
= precision_copy
;
1993 f
.localized
= localized
;
1994 detail::parse_chrono_format(begin
, end
, f
);
1996 return detail::write(
1997 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs_copy
);
2001 template <typename Char
, typename Duration
>
2002 struct formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2003 Char
> : formatter
<std::tm
, Char
> {
2004 FMT_CONSTEXPR
formatter() {
2005 basic_string_view
<Char
> default_specs
=
2006 detail::string_literal
<Char
, '%', 'F', ' ', '%', 'T'>{};
2007 this->do_parse(default_specs
.begin(), default_specs
.end());
2010 template <typename FormatContext
>
2011 auto format(std::chrono::time_point
<std::chrono::system_clock
> val
,
2012 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2013 return formatter
<std::tm
, Char
>::format(localtime(val
), ctx
);
2017 template <typename Char
> struct formatter
<std::tm
, Char
> {
2024 spec spec_
= spec::unknown
;
2025 basic_string_view
<Char
> specs
;
2028 template <typename It
> FMT_CONSTEXPR
auto do_parse(It begin
, It end
) -> It
{
2029 if (begin
!= end
&& *begin
== ':') ++begin
;
2030 end
= detail::parse_chrono_format(begin
, end
, detail::tm_format_checker());
2031 // Replace default spec only if the new spec is not empty.
2032 if (end
!= begin
) specs
= {begin
, detail::to_unsigned(end
- begin
)};
2037 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2038 -> decltype(ctx
.begin()) {
2039 auto end
= this->do_parse(ctx
.begin(), ctx
.end());
2040 // basic_string_view<>::compare isn't constexpr before C++17.
2041 if (specs
.size() == 2 && specs
[0] == Char('%')) {
2042 if (specs
[1] == Char('F'))
2043 spec_
= spec::year_month_day
;
2044 else if (specs
[1] == Char('T'))
2045 spec_
= spec::hh_mm_ss
;
2050 template <typename FormatContext
>
2051 auto format(const std::tm
& tm
, FormatContext
& ctx
) const
2052 -> decltype(ctx
.out()) {
2053 const auto loc_ref
= ctx
.locale();
2054 detail::get_locale
loc(static_cast<bool>(loc_ref
), loc_ref
);
2055 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), tm
);
2056 if (spec_
== spec::year_month_day
)
2058 else if (spec_
== spec::hh_mm_ss
)
2061 detail::parse_chrono_format(specs
.begin(), specs
.end(), w
);
2066 FMT_MODULE_EXPORT_END
2069 #endif // FMT_CHRONO_H_