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.
14 # include <cmath> // std::isfinite
15 # include <cstring> // std::memcpy
20 # include <type_traits>
27 // Check if std::chrono::local_t is available.
28 #ifndef FMT_USE_LOCAL_TIME
29 # ifdef __cpp_lib_chrono
30 # define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
32 # define FMT_USE_LOCAL_TIME 0
36 // Check if std::chrono::utc_timestamp is available.
37 #ifndef FMT_USE_UTC_TIME
38 # ifdef __cpp_lib_chrono
39 # define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
41 # define FMT_USE_UTC_TIME 0
47 // UWP doesn't provide _tzset.
48 # if FMT_HAS_INCLUDE("winapifamily.h")
49 # include <winapifamily.h>
51 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
52 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
53 # define FMT_USE_TZSET 1
55 # define FMT_USE_TZSET 0
59 // Enable safe chrono durations, unless explicitly disabled.
60 #ifndef FMT_SAFE_DURATION_CAST
61 # define FMT_SAFE_DURATION_CAST 1
63 #if FMT_SAFE_DURATION_CAST
65 // For conversion between std::chrono::durations without undefined
66 // behaviour or erroneous results.
67 // This is a stripped down version of duration_cast, for inclusion in fmt.
68 // See https://github.com/pauldreik/safe_duration_cast
70 // Copyright Paul Dreik 2019
71 namespace safe_duration_cast
{
73 template <typename To
, typename From
,
74 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
75 std::numeric_limits
<From
>::is_signed
==
76 std::numeric_limits
<To
>::is_signed
)>
77 FMT_CONSTEXPR
auto lossless_integral_conversion(const From from
, int& ec
)
80 using F
= std::numeric_limits
<From
>;
81 using T
= std::numeric_limits
<To
>;
82 static_assert(F::is_integer
, "From must be integral");
83 static_assert(T::is_integer
, "To must be integral");
85 // A and B are both signed, or both unsigned.
86 if (detail::const_check(F::digits
<= T::digits
)) {
87 // From fits in To without any problem.
89 // From does not always fit in To, resort to a dynamic check.
90 if (from
< (T::min
)() || from
> (T::max
)()) {
96 return static_cast<To
>(from
);
99 /// Converts From to To, without loss. If the dynamic value of from
100 /// can't be converted to To without loss, ec is set.
101 template <typename To
, typename From
,
102 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
103 std::numeric_limits
<From
>::is_signed
!=
104 std::numeric_limits
<To
>::is_signed
)>
105 FMT_CONSTEXPR
auto lossless_integral_conversion(const From from
, int& ec
)
108 using F
= std::numeric_limits
<From
>;
109 using T
= std::numeric_limits
<To
>;
110 static_assert(F::is_integer
, "From must be integral");
111 static_assert(T::is_integer
, "To must be integral");
113 if (detail::const_check(F::is_signed
&& !T::is_signed
)) {
114 // From may be negative, not allowed!
115 if (fmt::detail::is_negative(from
)) {
119 // From is positive. Can it always fit in To?
120 if (detail::const_check(F::digits
> T::digits
) &&
121 from
> static_cast<From
>(detail::max_value
<To
>())) {
127 if (detail::const_check(!F::is_signed
&& T::is_signed
&&
128 F::digits
>= T::digits
) &&
129 from
> static_cast<From
>(detail::max_value
<To
>())) {
133 return static_cast<To
>(from
); // Lossless conversion.
136 template <typename To
, typename From
,
137 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
138 FMT_CONSTEXPR
auto lossless_integral_conversion(const From from
, int& ec
)
146 * converts From to To if possible, otherwise ec is set.
149 * ---------------------------------|---------------
152 * normal, fits in output | converted (possibly lossy)
153 * normal, does not fit in output | ec is set
154 * subnormal | best effort
158 template <typename To
, typename From
,
159 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
)>
160 FMT_CONSTEXPR
auto safe_float_conversion(const From from
, int& ec
) -> To
{
162 using T
= std::numeric_limits
<To
>;
163 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
164 static_assert(std::is_floating_point
<To
>::value
, "To must be floating");
166 // catch the only happy case
167 if (std::isfinite(from
)) {
168 if (from
>= T::lowest() && from
<= (T::max
)()) {
169 return static_cast<To
>(from
);
176 // nan and inf will be preserved
177 return static_cast<To
>(from
);
180 template <typename To
, typename From
,
181 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
182 FMT_CONSTEXPR
auto safe_float_conversion(const From from
, int& ec
) -> To
{
184 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
188 /// Safe duration cast between integral durations
189 template <typename To
, typename FromRep
, typename FromPeriod
,
190 FMT_ENABLE_IF(std::is_integral
<FromRep
>::value
),
191 FMT_ENABLE_IF(std::is_integral
<typename
To::rep
>::value
)>
192 auto safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
194 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
196 // the basic idea is that we need to convert from count() in the from type
197 // to count() in the To type, by multiplying it with this:
199 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
201 static_assert(Factor::num
> 0, "num must be positive");
202 static_assert(Factor::den
> 0, "den must be positive");
204 // the conversion is like this: multiply from.count() with Factor::num
205 // /Factor::den and convert it to To::rep, all this without
206 // overflow/underflow. let's start by finding a suitable type that can hold
207 // both To, From and Factor::num
208 using IntermediateRep
=
209 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
210 decltype(Factor::num
)>::type
;
212 // safe conversion to IntermediateRep
213 IntermediateRep count
=
214 lossless_integral_conversion
<IntermediateRep
>(from
.count(), ec
);
216 // multiply with Factor::num without overflow or underflow
217 if (detail::const_check(Factor::num
!= 1)) {
218 const auto max1
= detail::max_value
<IntermediateRep
>() / Factor::num
;
224 (std::numeric_limits
<IntermediateRep
>::min
)() / Factor::num
;
225 if (detail::const_check(!std::is_unsigned
<IntermediateRep
>::value
) &&
230 count
*= Factor::num
;
233 if (detail::const_check(Factor::den
!= 1)) count
/= Factor::den
;
234 auto tocount
= lossless_integral_conversion
<typename
To::rep
>(count
, ec
);
235 return ec
? To() : To(tocount
);
238 /// Safe duration_cast between floating point durations
239 template <typename To
, typename FromRep
, typename FromPeriod
,
240 FMT_ENABLE_IF(std::is_floating_point
<FromRep
>::value
),
241 FMT_ENABLE_IF(std::is_floating_point
<typename
To::rep
>::value
)>
242 auto safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
244 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
246 if (std::isnan(from
.count())) {
247 // nan in, gives nan out. easy.
248 return To
{std::numeric_limits
<typename
To::rep
>::quiet_NaN()};
250 // maybe we should also check if from is denormal, and decide what to do about
253 // +-inf should be preserved.
254 if (std::isinf(from
.count())) {
255 return To
{from
.count()};
258 // the basic idea is that we need to convert from count() in the from type
259 // to count() in the To type, by multiplying it with this:
261 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
263 static_assert(Factor::num
> 0, "num must be positive");
264 static_assert(Factor::den
> 0, "den must be positive");
266 // the conversion is like this: multiply from.count() with Factor::num
267 // /Factor::den and convert it to To::rep, all this without
268 // overflow/underflow. let's start by finding a suitable type that can hold
269 // both To, From and Factor::num
270 using IntermediateRep
=
271 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
272 decltype(Factor::num
)>::type
;
274 // force conversion of From::rep -> IntermediateRep to be safe,
275 // even if it will never happen be narrowing in this context.
276 IntermediateRep count
=
277 safe_float_conversion
<IntermediateRep
>(from
.count(), ec
);
282 // multiply with Factor::num without overflow or underflow
283 if (detail::const_check(Factor::num
!= 1)) {
284 constexpr auto max1
= detail::max_value
<IntermediateRep
>() /
285 static_cast<IntermediateRep
>(Factor::num
);
290 constexpr auto min1
= std::numeric_limits
<IntermediateRep
>::lowest() /
291 static_cast<IntermediateRep
>(Factor::num
);
296 count
*= static_cast<IntermediateRep
>(Factor::num
);
299 // this can't go wrong, right? den>0 is checked earlier.
300 if (detail::const_check(Factor::den
!= 1)) {
301 using common_t
= typename
std::common_type
<IntermediateRep
, intmax_t>::type
;
302 count
/= static_cast<common_t
>(Factor::den
);
305 // convert to the to type, safely
306 using ToRep
= typename
To::rep
;
308 const ToRep tocount
= safe_float_conversion
<ToRep
>(count
, ec
);
314 } // namespace safe_duration_cast
317 // Prevents expansion of a preceding token as a function-style macro.
318 // Usage: f FMT_NOMACRO()
322 template <typename T
= void> struct null
{};
323 inline auto localtime_r
FMT_NOMACRO(...) -> null
<> { return null
<>(); }
324 inline auto localtime_s(...) -> null
<> { return null
<>(); }
325 inline auto gmtime_r(...) -> null
<> { return null
<>(); }
326 inline auto gmtime_s(...) -> null
<> { return null
<>(); }
328 // It is defined here and not in ostream.h because the latter has expensive
330 template <typename Streambuf
> class formatbuf
: public Streambuf
{
332 using char_type
= typename
Streambuf::char_type
;
333 using streamsize
= decltype(std::declval
<Streambuf
>().sputn(nullptr, 0));
334 using int_type
= typename
Streambuf::int_type
;
335 using traits_type
= typename
Streambuf::traits_type
;
337 buffer
<char_type
>& buffer_
;
340 explicit formatbuf(buffer
<char_type
>& buf
) : buffer_(buf
) {}
343 // The put area is always empty. This makes the implementation simpler and has
344 // the advantage that the streambuf and the buffer are always in sync and
345 // sputc never writes into uninitialized memory. A disadvantage is that each
346 // call to sputc always results in a (virtual) call to overflow. There is no
347 // disadvantage here for sputn since this always results in a call to xsputn.
349 auto overflow(int_type ch
) -> int_type override
{
350 if (!traits_type::eq_int_type(ch
, traits_type::eof()))
351 buffer_
.push_back(static_cast<char_type
>(ch
));
355 auto xsputn(const char_type
* s
, streamsize count
) -> streamsize override
{
356 buffer_
.append(s
, s
+ count
);
361 inline auto get_classic_locale() -> const std::locale
& {
362 static const auto& locale
= std::locale::classic();
366 template <typename CodeUnit
> struct codecvt_result
{
367 static constexpr const size_t max_size
= 32;
368 CodeUnit buf
[max_size
];
372 template <typename CodeUnit
>
373 void write_codecvt(codecvt_result
<CodeUnit
>& out
, string_view in_buf
,
374 const std::locale
& loc
) {
375 #if FMT_CLANG_VERSION
376 # pragma clang diagnostic push
377 # pragma clang diagnostic ignored "-Wdeprecated"
378 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
379 # pragma clang diagnostic pop
381 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
383 auto mb
= std::mbstate_t();
384 const char* from_next
= nullptr;
385 auto result
= f
.in(mb
, in_buf
.begin(), in_buf
.end(), from_next
,
386 std::begin(out
.buf
), std::end(out
.buf
), out
.end
);
387 if (result
!= std::codecvt_base::ok
)
388 FMT_THROW(format_error("failed to format time"));
391 template <typename OutputIt
>
392 auto write_encoded_tm_str(OutputIt out
, string_view in
, const std::locale
& loc
)
394 if (detail::use_utf8() && loc
!= get_classic_locale()) {
395 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
397 #if FMT_MSC_VERSION != 0 || \
398 (defined(__GLIBCXX__) && \
399 (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0))
400 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
402 using code_unit
= wchar_t;
404 using code_unit
= char32_t
;
407 using unit_t
= codecvt_result
<code_unit
>;
409 write_codecvt(unit
, in
, loc
);
410 // In UTF-8 is used one to four one-byte code units.
412 to_utf8
<code_unit
, basic_memory_buffer
<char, unit_t::max_size
* 4>>();
413 if (!u
.convert({unit
.buf
, to_unsigned(unit
.end
- unit
.buf
)}))
414 FMT_THROW(format_error("failed to format time"));
415 return copy
<char>(u
.c_str(), u
.c_str() + u
.size(), out
);
417 return copy
<char>(in
.data(), in
.data() + in
.size(), out
);
420 template <typename Char
, typename OutputIt
,
421 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
422 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
424 codecvt_result
<Char
> unit
;
425 write_codecvt(unit
, sv
, loc
);
426 return copy
<Char
>(unit
.buf
, unit
.end
, out
);
429 template <typename Char
, typename OutputIt
,
430 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
431 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
433 return write_encoded_tm_str(out
, sv
, loc
);
436 template <typename Char
>
437 inline void do_write(buffer
<Char
>& buf
, const std::tm
& time
,
438 const std::locale
& loc
, char format
, char modifier
) {
439 auto&& format_buf
= formatbuf
<std::basic_streambuf
<Char
>>(buf
);
440 auto&& os
= std::basic_ostream
<Char
>(&format_buf
);
442 const auto& facet
= std::use_facet
<std::time_put
<Char
>>(loc
);
443 auto end
= facet
.put(os
, os
, Char(' '), &time
, format
, modifier
);
444 if (end
.failed()) FMT_THROW(format_error("failed to format time"));
447 template <typename Char
, typename OutputIt
,
448 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
449 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
450 char format
, char modifier
= 0) -> OutputIt
{
451 auto&& buf
= get_buffer
<Char
>(out
);
452 do_write
<Char
>(buf
, time
, loc
, format
, modifier
);
453 return get_iterator(buf
, out
);
456 template <typename Char
, typename OutputIt
,
457 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
458 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
459 char format
, char modifier
= 0) -> OutputIt
{
460 auto&& buf
= basic_memory_buffer
<Char
>();
461 do_write
<char>(buf
, time
, loc
, format
, modifier
);
462 return write_encoded_tm_str(out
, string_view(buf
.data(), buf
.size()), loc
);
465 template <typename Rep1
, typename Rep2
>
466 struct is_same_arithmetic_type
467 : public std::integral_constant
<bool,
468 (std::is_integral
<Rep1
>::value
&&
469 std::is_integral
<Rep2
>::value
) ||
470 (std::is_floating_point
<Rep1
>::value
&&
471 std::is_floating_point
<Rep2
>::value
)> {
475 typename To
, typename FromRep
, typename FromPeriod
,
476 FMT_ENABLE_IF(is_same_arithmetic_type
<FromRep
, typename
To::rep
>::value
)>
477 auto fmt_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
) -> To
{
478 #if FMT_SAFE_DURATION_CAST
479 // Throwing version of safe_duration_cast is only available for
480 // integer to integer or float to float casts.
482 To to
= safe_duration_cast::safe_duration_cast
<To
>(from
, ec
);
483 if (ec
) FMT_THROW(format_error("cannot format duration"));
486 // Standard duration cast, may overflow.
487 return std::chrono::duration_cast
<To
>(from
);
492 typename To
, typename FromRep
, typename FromPeriod
,
493 FMT_ENABLE_IF(!is_same_arithmetic_type
<FromRep
, typename
To::rep
>::value
)>
494 auto fmt_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
) -> To
{
495 // Mixed integer <-> float cast is not supported by safe_duration_cast.
496 return std::chrono::duration_cast
<To
>(from
);
499 template <typename Duration
>
501 std::chrono::time_point
<std::chrono::system_clock
, Duration
> time_point
)
503 // Cannot use std::chrono::system_clock::to_time_t since this would first
504 // require a cast to std::chrono::system_clock::time_point, which could
506 return fmt_duration_cast
<std::chrono::duration
<std::time_t>>(
507 time_point
.time_since_epoch())
510 } // namespace detail
515 * Converts given time since epoch as `std::time_t` value into calendar time,
516 * expressed in local time. Unlike `std::localtime`, this function is
517 * thread-safe on most platforms.
519 inline auto localtime(std::time_t time
) -> std::tm
{
524 dispatcher(std::time_t t
) : time_(t
) {}
527 using namespace fmt::detail
;
528 return handle(localtime_r(&time_
, &tm_
));
531 auto handle(std::tm
* tm
) -> bool { return tm
!= nullptr; }
533 auto handle(detail::null
<>) -> bool {
534 using namespace fmt::detail
;
535 return fallback(localtime_s(&tm_
, &time_
));
538 auto fallback(int res
) -> bool { return res
== 0; }
541 auto fallback(detail::null
<>) -> bool {
542 using namespace fmt::detail
;
543 std::tm
* tm
= std::localtime(&time_
);
545 return tm
!= nullptr;
550 // Too big time values may be unsupported.
551 if (!lt
.run()) FMT_THROW(format_error("time_t value out of range"));
555 #if FMT_USE_LOCAL_TIME
556 template <typename Duration
>
557 inline auto localtime(std::chrono::local_time
<Duration
> time
) -> std::tm
{
559 detail::to_time_t(std::chrono::current_zone()->to_sys(time
)));
564 * Converts given time since epoch as `std::time_t` value into calendar time,
565 * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
566 * function is thread-safe on most platforms.
568 inline auto gmtime(std::time_t time
) -> std::tm
{
573 dispatcher(std::time_t t
) : time_(t
) {}
576 using namespace fmt::detail
;
577 return handle(gmtime_r(&time_
, &tm_
));
580 auto handle(std::tm
* tm
) -> bool { return tm
!= nullptr; }
582 auto handle(detail::null
<>) -> bool {
583 using namespace fmt::detail
;
584 return fallback(gmtime_s(&tm_
, &time_
));
587 auto fallback(int res
) -> bool { return res
== 0; }
590 auto fallback(detail::null
<>) -> bool {
591 std::tm
* tm
= std::gmtime(&time_
);
593 return tm
!= nullptr;
597 auto gt
= dispatcher(time
);
598 // Too big time values may be unsupported.
599 if (!gt
.run()) FMT_THROW(format_error("time_t value out of range"));
603 template <typename Duration
>
605 std::chrono::time_point
<std::chrono::system_clock
, Duration
> time_point
)
607 return gmtime(detail::to_time_t(time_point
));
612 // Writes two-digit numbers a, b and c separated by sep to buf.
613 // The method by Pavel Novikov based on
614 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
615 inline void write_digit2_separated(char* buf
, unsigned a
, unsigned b
,
616 unsigned c
, char sep
) {
617 unsigned long long digits
=
618 a
| (b
<< 24) | (static_cast<unsigned long long>(c
) << 48);
619 // Convert each value to BCD.
620 // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
623 // a can be found from x:
626 // y = x + a * 6 = x + floor(x / 10) * 6
627 // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
628 digits
+= (((digits
* 205) >> 11) & 0x000f00000f00000f) * 6;
629 // Put low nibbles to high bytes and high nibbles to low bytes.
630 digits
= ((digits
& 0x00f00000f00000f0) >> 4) |
631 ((digits
& 0x000f00000f00000f) << 8);
632 auto usep
= static_cast<unsigned long long>(sep
);
633 // Add ASCII '0' to each digit byte and insert separators.
634 digits
|= 0x3030003030003030 | (usep
<< 16) | (usep
<< 40);
636 constexpr const size_t len
= 8;
637 if (const_check(is_big_endian())) {
639 std::memcpy(tmp
, &digits
, len
);
640 std::reverse_copy(tmp
, tmp
+ len
, buf
);
642 std::memcpy(buf
, &digits
, len
);
646 template <typename Period
>
647 FMT_CONSTEXPR
inline auto get_units() -> const char* {
648 if (std::is_same
<Period
, std::atto
>::value
) return "as";
649 if (std::is_same
<Period
, std::femto
>::value
) return "fs";
650 if (std::is_same
<Period
, std::pico
>::value
) return "ps";
651 if (std::is_same
<Period
, std::nano
>::value
) return "ns";
652 if (std::is_same
<Period
, std::micro
>::value
) return "µs";
653 if (std::is_same
<Period
, std::milli
>::value
) return "ms";
654 if (std::is_same
<Period
, std::centi
>::value
) return "cs";
655 if (std::is_same
<Period
, std::deci
>::value
) return "ds";
656 if (std::is_same
<Period
, std::ratio
<1>>::value
) return "s";
657 if (std::is_same
<Period
, std::deca
>::value
) return "das";
658 if (std::is_same
<Period
, std::hecto
>::value
) return "hs";
659 if (std::is_same
<Period
, std::kilo
>::value
) return "ks";
660 if (std::is_same
<Period
, std::mega
>::value
) return "Ms";
661 if (std::is_same
<Period
, std::giga
>::value
) return "Gs";
662 if (std::is_same
<Period
, std::tera
>::value
) return "Ts";
663 if (std::is_same
<Period
, std::peta
>::value
) return "Ps";
664 if (std::is_same
<Period
, std::exa
>::value
) return "Es";
665 if (std::is_same
<Period
, std::ratio
<60>>::value
) return "min";
666 if (std::is_same
<Period
, std::ratio
<3600>>::value
) return "h";
667 if (std::is_same
<Period
, std::ratio
<86400>>::value
) return "d";
671 enum class numeric_system
{
673 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
677 // Glibc extensions for formatting numeric values.
678 enum class pad_type
{
679 // Pad a numeric result string with zeros (the default).
681 // Do not pad a numeric result string.
683 // Pad a numeric result string with spaces.
687 template <typename OutputIt
>
688 auto write_padding(OutputIt out
, pad_type pad
, int width
) -> OutputIt
{
689 if (pad
== pad_type::none
) return out
;
690 return detail::fill_n(out
, width
, pad
== pad_type::space
? ' ' : '0');
693 template <typename OutputIt
>
694 auto write_padding(OutputIt out
, pad_type pad
) -> OutputIt
{
695 if (pad
!= pad_type::none
) *out
++ = pad
== pad_type::space
? ' ' : '0';
699 // Parses a put_time-like format string and invokes handler actions.
700 template <typename Char
, typename Handler
>
701 FMT_CONSTEXPR
auto parse_chrono_format(const Char
* begin
, const Char
* end
,
702 Handler
&& handler
) -> const Char
* {
703 if (begin
== end
|| *begin
== '}') return begin
;
704 if (*begin
!= '%') FMT_THROW(format_error("invalid format"));
707 pad_type pad
= pad_type::zero
;
714 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
715 ++ptr
; // consume '%'
716 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
720 pad
= pad_type::space
;
724 pad
= pad_type::none
;
728 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
732 handler
.on_text(ptr
- 1, ptr
);
735 const Char newline
[] = {'\n'};
736 handler
.on_text(newline
, newline
+ 1);
740 const Char tab
[] = {'\t'};
741 handler
.on_text(tab
, tab
+ 1);
746 handler
.on_year(numeric_system::standard
);
749 handler
.on_short_year(numeric_system::standard
);
752 handler
.on_century(numeric_system::standard
);
755 handler
.on_iso_week_based_year();
758 handler
.on_iso_week_based_short_year();
762 handler
.on_abbr_weekday();
765 handler
.on_full_weekday();
768 handler
.on_dec0_weekday(numeric_system::standard
);
771 handler
.on_dec1_weekday(numeric_system::standard
);
776 handler
.on_abbr_month();
779 handler
.on_full_month();
782 handler
.on_dec_month(numeric_system::standard
);
784 // Day of the year/month:
786 handler
.on_dec0_week_of_year(numeric_system::standard
, pad
);
789 handler
.on_dec1_week_of_year(numeric_system::standard
, pad
);
792 handler
.on_iso_week_of_year(numeric_system::standard
, pad
);
795 handler
.on_day_of_year();
798 handler
.on_day_of_month(numeric_system::standard
, pad
);
801 handler
.on_day_of_month(numeric_system::standard
, pad_type::space
);
803 // Hour, minute, second:
805 handler
.on_24_hour(numeric_system::standard
, pad
);
808 handler
.on_12_hour(numeric_system::standard
, pad
);
811 handler
.on_minute(numeric_system::standard
, pad
);
814 handler
.on_second(numeric_system::standard
, pad
);
818 handler
.on_datetime(numeric_system::standard
);
821 handler
.on_loc_date(numeric_system::standard
);
824 handler
.on_loc_time(numeric_system::standard
);
827 handler
.on_us_date();
830 handler
.on_iso_date();
833 handler
.on_12_hour_time();
836 handler
.on_24_hour_time();
839 handler
.on_iso_time();
845 handler
.on_duration_value();
848 handler
.on_duration_unit();
851 handler
.on_utc_offset(numeric_system::standard
);
854 handler
.on_tz_name();
856 // Alternative representation:
858 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
862 handler
.on_year(numeric_system::alternative
);
865 handler
.on_offset_year();
868 handler
.on_century(numeric_system::alternative
);
871 handler
.on_datetime(numeric_system::alternative
);
874 handler
.on_loc_date(numeric_system::alternative
);
877 handler
.on_loc_time(numeric_system::alternative
);
880 handler
.on_utc_offset(numeric_system::alternative
);
883 FMT_THROW(format_error("invalid format"));
888 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
892 handler
.on_short_year(numeric_system::alternative
);
895 handler
.on_dec_month(numeric_system::alternative
);
898 handler
.on_dec0_week_of_year(numeric_system::alternative
, pad
);
901 handler
.on_dec1_week_of_year(numeric_system::alternative
, pad
);
904 handler
.on_iso_week_of_year(numeric_system::alternative
, pad
);
907 handler
.on_day_of_month(numeric_system::alternative
, pad
);
910 handler
.on_day_of_month(numeric_system::alternative
, pad_type::space
);
913 handler
.on_dec0_weekday(numeric_system::alternative
);
916 handler
.on_dec1_weekday(numeric_system::alternative
);
919 handler
.on_24_hour(numeric_system::alternative
, pad
);
922 handler
.on_12_hour(numeric_system::alternative
, pad
);
925 handler
.on_minute(numeric_system::alternative
, pad
);
928 handler
.on_second(numeric_system::alternative
, pad
);
931 handler
.on_utc_offset(numeric_system::alternative
);
934 FMT_THROW(format_error("invalid format"));
938 FMT_THROW(format_error("invalid format"));
942 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
946 template <typename Derived
> struct null_chrono_spec_handler
{
947 FMT_CONSTEXPR
void unsupported() {
948 static_cast<Derived
*>(this)->unsupported();
950 FMT_CONSTEXPR
void on_year(numeric_system
) { unsupported(); }
951 FMT_CONSTEXPR
void on_short_year(numeric_system
) { unsupported(); }
952 FMT_CONSTEXPR
void on_offset_year() { unsupported(); }
953 FMT_CONSTEXPR
void on_century(numeric_system
) { unsupported(); }
954 FMT_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
955 FMT_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
956 FMT_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
957 FMT_CONSTEXPR
void on_full_weekday() { unsupported(); }
958 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) { unsupported(); }
959 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) { unsupported(); }
960 FMT_CONSTEXPR
void on_abbr_month() { unsupported(); }
961 FMT_CONSTEXPR
void on_full_month() { unsupported(); }
962 FMT_CONSTEXPR
void on_dec_month(numeric_system
) { unsupported(); }
963 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
, pad_type
) {
966 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
, pad_type
) {
969 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
, pad_type
) {
972 FMT_CONSTEXPR
void on_day_of_year() { unsupported(); }
973 FMT_CONSTEXPR
void on_day_of_month(numeric_system
, pad_type
) {
976 FMT_CONSTEXPR
void on_24_hour(numeric_system
) { unsupported(); }
977 FMT_CONSTEXPR
void on_12_hour(numeric_system
) { unsupported(); }
978 FMT_CONSTEXPR
void on_minute(numeric_system
) { unsupported(); }
979 FMT_CONSTEXPR
void on_second(numeric_system
) { unsupported(); }
980 FMT_CONSTEXPR
void on_datetime(numeric_system
) { unsupported(); }
981 FMT_CONSTEXPR
void on_loc_date(numeric_system
) { unsupported(); }
982 FMT_CONSTEXPR
void on_loc_time(numeric_system
) { unsupported(); }
983 FMT_CONSTEXPR
void on_us_date() { unsupported(); }
984 FMT_CONSTEXPR
void on_iso_date() { unsupported(); }
985 FMT_CONSTEXPR
void on_12_hour_time() { unsupported(); }
986 FMT_CONSTEXPR
void on_24_hour_time() { unsupported(); }
987 FMT_CONSTEXPR
void on_iso_time() { unsupported(); }
988 FMT_CONSTEXPR
void on_am_pm() { unsupported(); }
989 FMT_CONSTEXPR
void on_duration_value() { unsupported(); }
990 FMT_CONSTEXPR
void on_duration_unit() { unsupported(); }
991 FMT_CONSTEXPR
void on_utc_offset(numeric_system
) { unsupported(); }
992 FMT_CONSTEXPR
void on_tz_name() { unsupported(); }
995 struct tm_format_checker
: null_chrono_spec_handler
<tm_format_checker
> {
996 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no format")); }
998 template <typename Char
>
999 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
1000 FMT_CONSTEXPR
void on_year(numeric_system
) {}
1001 FMT_CONSTEXPR
void on_short_year(numeric_system
) {}
1002 FMT_CONSTEXPR
void on_offset_year() {}
1003 FMT_CONSTEXPR
void on_century(numeric_system
) {}
1004 FMT_CONSTEXPR
void on_iso_week_based_year() {}
1005 FMT_CONSTEXPR
void on_iso_week_based_short_year() {}
1006 FMT_CONSTEXPR
void on_abbr_weekday() {}
1007 FMT_CONSTEXPR
void on_full_weekday() {}
1008 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) {}
1009 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) {}
1010 FMT_CONSTEXPR
void on_abbr_month() {}
1011 FMT_CONSTEXPR
void on_full_month() {}
1012 FMT_CONSTEXPR
void on_dec_month(numeric_system
) {}
1013 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
, pad_type
) {}
1014 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
, pad_type
) {}
1015 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
, pad_type
) {}
1016 FMT_CONSTEXPR
void on_day_of_year() {}
1017 FMT_CONSTEXPR
void on_day_of_month(numeric_system
, pad_type
) {}
1018 FMT_CONSTEXPR
void on_24_hour(numeric_system
, pad_type
) {}
1019 FMT_CONSTEXPR
void on_12_hour(numeric_system
, pad_type
) {}
1020 FMT_CONSTEXPR
void on_minute(numeric_system
, pad_type
) {}
1021 FMT_CONSTEXPR
void on_second(numeric_system
, pad_type
) {}
1022 FMT_CONSTEXPR
void on_datetime(numeric_system
) {}
1023 FMT_CONSTEXPR
void on_loc_date(numeric_system
) {}
1024 FMT_CONSTEXPR
void on_loc_time(numeric_system
) {}
1025 FMT_CONSTEXPR
void on_us_date() {}
1026 FMT_CONSTEXPR
void on_iso_date() {}
1027 FMT_CONSTEXPR
void on_12_hour_time() {}
1028 FMT_CONSTEXPR
void on_24_hour_time() {}
1029 FMT_CONSTEXPR
void on_iso_time() {}
1030 FMT_CONSTEXPR
void on_am_pm() {}
1031 FMT_CONSTEXPR
void on_utc_offset(numeric_system
) {}
1032 FMT_CONSTEXPR
void on_tz_name() {}
1035 inline auto tm_wday_full_name(int wday
) -> const char* {
1036 static constexpr const char* full_name_list
[] = {
1037 "Sunday", "Monday", "Tuesday", "Wednesday",
1038 "Thursday", "Friday", "Saturday"};
1039 return wday
>= 0 && wday
<= 6 ? full_name_list
[wday
] : "?";
1041 inline auto tm_wday_short_name(int wday
) -> const char* {
1042 static constexpr const char* short_name_list
[] = {"Sun", "Mon", "Tue", "Wed",
1043 "Thu", "Fri", "Sat"};
1044 return wday
>= 0 && wday
<= 6 ? short_name_list
[wday
] : "???";
1047 inline auto tm_mon_full_name(int mon
) -> const char* {
1048 static constexpr const char* full_name_list
[] = {
1049 "January", "February", "March", "April", "May", "June",
1050 "July", "August", "September", "October", "November", "December"};
1051 return mon
>= 0 && mon
<= 11 ? full_name_list
[mon
] : "?";
1053 inline auto tm_mon_short_name(int mon
) -> const char* {
1054 static constexpr const char* short_name_list
[] = {
1055 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1056 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
1058 return mon
>= 0 && mon
<= 11 ? short_name_list
[mon
] : "???";
1061 template <typename T
, typename
= void>
1062 struct has_member_data_tm_gmtoff
: std::false_type
{};
1063 template <typename T
>
1064 struct has_member_data_tm_gmtoff
<T
, void_t
<decltype(T::tm_gmtoff
)>>
1065 : std::true_type
{};
1067 template <typename T
, typename
= void>
1068 struct has_member_data_tm_zone
: std::false_type
{};
1069 template <typename T
>
1070 struct has_member_data_tm_zone
<T
, void_t
<decltype(T::tm_zone
)>>
1071 : std::true_type
{};
1074 inline void tzset_once() {
1075 static bool init
= []() -> bool {
1079 ignore_unused(init
);
1083 // Converts value to Int and checks that it's in the range [0, upper).
1084 template <typename T
, typename Int
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1085 inline auto to_nonnegative_int(T value
, Int upper
) -> Int
{
1086 if (!std::is_unsigned
<Int
>::value
&&
1087 (value
< 0 || to_unsigned(value
) > to_unsigned(upper
))) {
1088 FMT_THROW(fmt::format_error("chrono value is out of range"));
1090 return static_cast<Int
>(value
);
1092 template <typename T
, typename Int
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
1093 inline auto to_nonnegative_int(T value
, Int upper
) -> Int
{
1094 auto int_value
= static_cast<Int
>(value
);
1095 if (int_value
< 0 || value
> static_cast<T
>(upper
))
1096 FMT_THROW(format_error("invalid value"));
1100 constexpr auto pow10(std::uint32_t n
) -> long long {
1101 return n
== 0 ? 1 : 10 * pow10(n
- 1);
1104 // Counts the number of fractional digits in the range [0, 18] according to the
1105 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
1106 // microseconds precision.
1107 template <long long Num
, long long Den
, int N
= 0,
1108 bool Enabled
= (N
< 19) && (Num
<= max_value
<long long>() / 10)>
1109 struct count_fractional_digits
{
1110 static constexpr int value
=
1111 Num
% Den
== 0 ? N
: count_fractional_digits
<Num
* 10, Den
, N
+ 1>::value
;
1114 // Base case that doesn't instantiate any more templates
1115 // in order to avoid overflow.
1116 template <long long Num
, long long Den
, int N
>
1117 struct count_fractional_digits
<Num
, Den
, N
, false> {
1118 static constexpr int value
= (Num
% Den
== 0) ? N
: 6;
1121 // Format subseconds which are given as an integer type with an appropriate
1122 // number of digits.
1123 template <typename Char
, typename OutputIt
, typename Duration
>
1124 void write_fractional_seconds(OutputIt
& out
, Duration d
, int precision
= -1) {
1125 constexpr auto num_fractional_digits
=
1126 count_fractional_digits
<Duration::period::num
,
1127 Duration::period::den
>::value
;
1129 using subsecond_precision
= std::chrono::duration
<
1130 typename
std::common_type
<typename
Duration::rep
,
1131 std::chrono::seconds::rep
>::type
,
1132 std::ratio
<1, detail::pow10(num_fractional_digits
)>>;
1134 const auto fractional
= d
- fmt_duration_cast
<std::chrono::seconds
>(d
);
1135 const auto subseconds
=
1136 std::chrono::treat_as_floating_point
<
1137 typename
subsecond_precision::rep
>::value
1138 ? fractional
.count()
1139 : fmt_duration_cast
<subsecond_precision
>(fractional
).count();
1140 auto n
= static_cast<uint32_or_64_or_128_t
<long long>>(subseconds
);
1141 const int num_digits
= detail::count_digits(n
);
1143 int leading_zeroes
= (std::max
)(0, num_fractional_digits
- num_digits
);
1144 if (precision
< 0) {
1145 FMT_ASSERT(!std::is_floating_point
<typename
Duration::rep
>::value
, "");
1146 if (std::ratio_less
<typename
subsecond_precision::period
,
1147 std::chrono::seconds::period
>::value
) {
1149 out
= detail::fill_n(out
, leading_zeroes
, '0');
1150 out
= format_decimal
<Char
>(out
, n
, num_digits
).end
;
1152 } else if (precision
> 0) {
1154 leading_zeroes
= (std::min
)(leading_zeroes
, precision
);
1155 int remaining
= precision
- leading_zeroes
;
1156 out
= detail::fill_n(out
, leading_zeroes
, '0');
1157 if (remaining
< num_digits
) {
1158 int num_truncated_digits
= num_digits
- remaining
;
1159 n
/= to_unsigned(detail::pow10(to_unsigned(num_truncated_digits
)));
1161 out
= format_decimal
<Char
>(out
, n
, remaining
).end
;
1166 out
= format_decimal
<Char
>(out
, n
, num_digits
).end
;
1167 remaining
-= num_digits
;
1169 out
= detail::fill_n(out
, remaining
, '0');
1173 // Format subseconds which are given as a floating point type with an
1174 // appropriate number of digits. We cannot pass the Duration here, as we
1175 // explicitly need to pass the Rep value in the chrono_formatter.
1176 template <typename Duration
>
1177 void write_floating_seconds(memory_buffer
& buf
, Duration duration
,
1178 int num_fractional_digits
= -1) {
1179 using rep
= typename
Duration::rep
;
1180 FMT_ASSERT(std::is_floating_point
<rep
>::value
, "");
1182 auto val
= duration
.count();
1184 if (num_fractional_digits
< 0) {
1185 // For `std::round` with fallback to `round`:
1186 // On some toolchains `std::round` is not available (e.g. GCC 6).
1187 using namespace std
;
1188 num_fractional_digits
=
1189 count_fractional_digits
<Duration::period::num
,
1190 Duration::period::den
>::value
;
1191 if (num_fractional_digits
< 6 && static_cast<rep
>(round(val
)) != val
)
1192 num_fractional_digits
= 6;
1195 fmt::format_to(std::back_inserter(buf
), FMT_STRING("{:.{}f}"),
1196 std::fmod(val
* static_cast<rep
>(Duration::period::num
) /
1197 static_cast<rep
>(Duration::period::den
),
1198 static_cast<rep
>(60)),
1199 num_fractional_digits
);
1202 template <typename OutputIt
, typename Char
,
1203 typename Duration
= std::chrono::seconds
>
1206 static constexpr int days_per_week
= 7;
1208 const std::locale
& loc_
;
1209 const bool is_classic_
;
1211 const Duration
* subsecs_
;
1214 auto tm_sec() const noexcept
-> int {
1215 FMT_ASSERT(tm_
.tm_sec
>= 0 && tm_
.tm_sec
<= 61, "");
1218 auto tm_min() const noexcept
-> int {
1219 FMT_ASSERT(tm_
.tm_min
>= 0 && tm_
.tm_min
<= 59, "");
1222 auto tm_hour() const noexcept
-> int {
1223 FMT_ASSERT(tm_
.tm_hour
>= 0 && tm_
.tm_hour
<= 23, "");
1226 auto tm_mday() const noexcept
-> int {
1227 FMT_ASSERT(tm_
.tm_mday
>= 1 && tm_
.tm_mday
<= 31, "");
1230 auto tm_mon() const noexcept
-> int {
1231 FMT_ASSERT(tm_
.tm_mon
>= 0 && tm_
.tm_mon
<= 11, "");
1234 auto tm_year() const noexcept
-> long long { return 1900ll + tm_
.tm_year
; }
1235 auto tm_wday() const noexcept
-> int {
1236 FMT_ASSERT(tm_
.tm_wday
>= 0 && tm_
.tm_wday
<= 6, "");
1239 auto tm_yday() const noexcept
-> int {
1240 FMT_ASSERT(tm_
.tm_yday
>= 0 && tm_
.tm_yday
<= 365, "");
1244 auto tm_hour12() const noexcept
-> int {
1245 const auto h
= tm_hour();
1246 const auto z
= h
< 12 ? h
: h
- 12;
1247 return z
== 0 ? 12 : z
;
1250 // POSIX and the C Standard are unclear or inconsistent about what %C and %y
1251 // do if the year is negative or exceeds 9999. Use the convention that %C
1252 // concatenated with %y yields the same output as %Y, and that %Y contains at
1253 // least 4 characters, with more only if necessary.
1254 auto split_year_lower(long long year
) const noexcept
-> int {
1255 auto l
= year
% 100;
1256 if (l
< 0) l
= -l
; // l in [0, 99]
1257 return static_cast<int>(l
);
1260 // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
1261 auto iso_year_weeks(long long curr_year
) const noexcept
-> int {
1262 const auto prev_year
= curr_year
- 1;
1264 (curr_year
+ curr_year
/ 4 - curr_year
/ 100 + curr_year
/ 400) %
1267 (prev_year
+ prev_year
/ 4 - prev_year
/ 100 + prev_year
/ 400) %
1269 return 52 + ((curr_p
== 4 || prev_p
== 3) ? 1 : 0);
1271 auto iso_week_num(int tm_yday
, int tm_wday
) const noexcept
-> int {
1272 return (tm_yday
+ 11 - (tm_wday
== 0 ? days_per_week
: tm_wday
)) /
1275 auto tm_iso_week_year() const noexcept
-> long long {
1276 const auto year
= tm_year();
1277 const auto w
= iso_week_num(tm_yday(), tm_wday());
1278 if (w
< 1) return year
- 1;
1279 if (w
> iso_year_weeks(year
)) return year
+ 1;
1282 auto tm_iso_week_of_year() const noexcept
-> int {
1283 const auto year
= tm_year();
1284 const auto w
= iso_week_num(tm_yday(), tm_wday());
1285 if (w
< 1) return iso_year_weeks(year
- 1);
1286 if (w
> iso_year_weeks(year
)) return 1;
1290 void write1(int value
) {
1291 *out_
++ = static_cast<char>('0' + to_unsigned(value
) % 10);
1293 void write2(int value
) {
1294 const char* d
= digits2(to_unsigned(value
) % 100);
1298 void write2(int value
, pad_type pad
) {
1299 unsigned int v
= to_unsigned(value
) % 100;
1301 const char* d
= digits2(v
);
1305 out_
= detail::write_padding(out_
, pad
);
1306 *out_
++ = static_cast<char>('0' + v
);
1310 void write_year_extended(long long year
) {
1311 // At least 4 characters.
1318 uint32_or_64_or_128_t
<long long> n
= to_unsigned(year
);
1319 const int num_digits
= count_digits(n
);
1320 if (width
> num_digits
)
1321 out_
= detail::fill_n(out_
, width
- num_digits
, '0');
1322 out_
= format_decimal
<Char
>(out_
, n
, num_digits
).end
;
1324 void write_year(long long year
) {
1325 if (year
>= 0 && year
< 10000) {
1326 write2(static_cast<int>(year
/ 100));
1327 write2(static_cast<int>(year
% 100));
1329 write_year_extended(year
);
1333 void write_utc_offset(long offset
, numeric_system ns
) {
1341 write2(static_cast<int>(offset
/ 60));
1342 if (ns
!= numeric_system::standard
) *out_
++ = ':';
1343 write2(static_cast<int>(offset
% 60));
1345 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_gmtoff
<T
>::value
)>
1346 void format_utc_offset_impl(const T
& tm
, numeric_system ns
) {
1347 write_utc_offset(tm
.tm_gmtoff
, ns
);
1349 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_gmtoff
<T
>::value
)>
1350 void format_utc_offset_impl(const T
& tm
, numeric_system ns
) {
1351 #if defined(_WIN32) && defined(_UCRT)
1356 _get_timezone(&offset
);
1359 _get_dstbias(&dstbias
);
1362 write_utc_offset(-offset
, ns
);
1364 if (ns
== numeric_system::standard
) return format_localized('z');
1366 // Extract timezone offset from timezone conversion functions.
1368 std::time_t gt
= std::mktime(>m
);
1369 std::tm ltm
= gmtime(gt
);
1370 std::time_t lt
= std::mktime(<m
);
1371 long offset
= gt
- lt
;
1372 write_utc_offset(offset
, ns
);
1376 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_zone
<T
>::value
)>
1377 void format_tz_name_impl(const T
& tm
) {
1379 out_
= write_tm_str
<Char
>(out_
, tm
.tm_zone
, loc_
);
1381 format_localized('Z');
1383 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_zone
<T
>::value
)>
1384 void format_tz_name_impl(const T
&) {
1385 format_localized('Z');
1388 void format_localized(char format
, char modifier
= 0) {
1389 out_
= write
<Char
>(out_
, tm_
, loc_
, format
, modifier
);
1393 tm_writer(const std::locale
& loc
, OutputIt out
, const std::tm
& tm
,
1394 const Duration
* subsecs
= nullptr)
1396 is_classic_(loc_
== get_classic_locale()),
1401 auto out() const -> OutputIt
{ return out_
; }
1403 FMT_CONSTEXPR
void on_text(const Char
* begin
, const Char
* end
) {
1404 out_
= copy
<Char
>(begin
, end
, out_
);
1407 void on_abbr_weekday() {
1409 out_
= write(out_
, tm_wday_short_name(tm_wday()));
1411 format_localized('a');
1413 void on_full_weekday() {
1415 out_
= write(out_
, tm_wday_full_name(tm_wday()));
1417 format_localized('A');
1419 void on_dec0_weekday(numeric_system ns
) {
1420 if (is_classic_
|| ns
== numeric_system::standard
) return write1(tm_wday());
1421 format_localized('w', 'O');
1423 void on_dec1_weekday(numeric_system ns
) {
1424 if (is_classic_
|| ns
== numeric_system::standard
) {
1425 auto wday
= tm_wday();
1426 write1(wday
== 0 ? days_per_week
: wday
);
1428 format_localized('u', 'O');
1432 void on_abbr_month() {
1434 out_
= write(out_
, tm_mon_short_name(tm_mon()));
1436 format_localized('b');
1438 void on_full_month() {
1440 out_
= write(out_
, tm_mon_full_name(tm_mon()));
1442 format_localized('B');
1445 void on_datetime(numeric_system ns
) {
1451 on_day_of_month(numeric_system::standard
, pad_type::space
);
1455 on_year(numeric_system::standard
);
1457 format_localized('c', ns
== numeric_system::standard
? '\0' : 'E');
1460 void on_loc_date(numeric_system ns
) {
1464 format_localized('x', ns
== numeric_system::standard
? '\0' : 'E');
1466 void on_loc_time(numeric_system ns
) {
1470 format_localized('X', ns
== numeric_system::standard
? '\0' : 'E');
1474 write_digit2_separated(buf
, to_unsigned(tm_mon() + 1),
1475 to_unsigned(tm_mday()),
1476 to_unsigned(split_year_lower(tm_year())), '/');
1477 out_
= copy
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1479 void on_iso_date() {
1480 auto year
= tm_year();
1483 if (year
>= 0 && year
< 10000) {
1484 copy2(buf
, digits2(static_cast<size_t>(year
/ 100)));
1487 write_year_extended(year
);
1490 write_digit2_separated(buf
+ 2, static_cast<unsigned>(year
% 100),
1491 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1493 out_
= copy
<Char
>(std::begin(buf
) + offset
, std::end(buf
), out_
);
1496 void on_utc_offset(numeric_system ns
) { format_utc_offset_impl(tm_
, ns
); }
1497 void on_tz_name() { format_tz_name_impl(tm_
); }
1499 void on_year(numeric_system ns
) {
1500 if (is_classic_
|| ns
== numeric_system::standard
)
1501 return write_year(tm_year());
1502 format_localized('Y', 'E');
1504 void on_short_year(numeric_system ns
) {
1505 if (is_classic_
|| ns
== numeric_system::standard
)
1506 return write2(split_year_lower(tm_year()));
1507 format_localized('y', 'O');
1509 void on_offset_year() {
1510 if (is_classic_
) return write2(split_year_lower(tm_year()));
1511 format_localized('y', 'E');
1514 void on_century(numeric_system ns
) {
1515 if (is_classic_
|| ns
== numeric_system::standard
) {
1516 auto year
= tm_year();
1517 auto upper
= year
/ 100;
1518 if (year
>= -99 && year
< 0) {
1519 // Zero upper on negative year.
1522 } else if (upper
>= 0 && upper
< 100) {
1523 write2(static_cast<int>(upper
));
1525 out_
= write
<Char
>(out_
, upper
);
1528 format_localized('C', 'E');
1532 void on_dec_month(numeric_system ns
) {
1533 if (is_classic_
|| ns
== numeric_system::standard
)
1534 return write2(tm_mon() + 1);
1535 format_localized('m', 'O');
1538 void on_dec0_week_of_year(numeric_system ns
, pad_type pad
) {
1539 if (is_classic_
|| ns
== numeric_system::standard
)
1540 return write2((tm_yday() + days_per_week
- tm_wday()) / days_per_week
,
1542 format_localized('U', 'O');
1544 void on_dec1_week_of_year(numeric_system ns
, pad_type pad
) {
1545 if (is_classic_
|| ns
== numeric_system::standard
) {
1546 auto wday
= tm_wday();
1547 write2((tm_yday() + days_per_week
-
1548 (wday
== 0 ? (days_per_week
- 1) : (wday
- 1))) /
1552 format_localized('W', 'O');
1555 void on_iso_week_of_year(numeric_system ns
, pad_type pad
) {
1556 if (is_classic_
|| ns
== numeric_system::standard
)
1557 return write2(tm_iso_week_of_year(), pad
);
1558 format_localized('V', 'O');
1561 void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
1562 void on_iso_week_based_short_year() {
1563 write2(split_year_lower(tm_iso_week_year()));
1566 void on_day_of_year() {
1567 auto yday
= tm_yday() + 1;
1571 void on_day_of_month(numeric_system ns
, pad_type pad
) {
1572 if (is_classic_
|| ns
== numeric_system::standard
)
1573 return write2(tm_mday(), pad
);
1574 format_localized('d', 'O');
1577 void on_24_hour(numeric_system ns
, pad_type pad
) {
1578 if (is_classic_
|| ns
== numeric_system::standard
)
1579 return write2(tm_hour(), pad
);
1580 format_localized('H', 'O');
1582 void on_12_hour(numeric_system ns
, pad_type pad
) {
1583 if (is_classic_
|| ns
== numeric_system::standard
)
1584 return write2(tm_hour12(), pad
);
1585 format_localized('I', 'O');
1587 void on_minute(numeric_system ns
, pad_type pad
) {
1588 if (is_classic_
|| ns
== numeric_system::standard
)
1589 return write2(tm_min(), pad
);
1590 format_localized('M', 'O');
1593 void on_second(numeric_system ns
, pad_type pad
) {
1594 if (is_classic_
|| ns
== numeric_system::standard
) {
1595 write2(tm_sec(), pad
);
1597 if (std::is_floating_point
<typename
Duration::rep
>::value
) {
1598 auto buf
= memory_buffer();
1599 write_floating_seconds(buf
, *subsecs_
);
1600 if (buf
.size() > 1) {
1601 // Remove the leading "0", write something like ".123".
1602 out_
= std::copy(buf
.begin() + 1, buf
.end(), out_
);
1605 write_fractional_seconds
<Char
>(out_
, *subsecs_
);
1609 // Currently no formatting of subseconds when a locale is set.
1610 format_localized('S', 'O');
1614 void on_12_hour_time() {
1617 write_digit2_separated(buf
, to_unsigned(tm_hour12()),
1618 to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
1619 out_
= copy
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1623 format_localized('r');
1626 void on_24_hour_time() {
1631 void on_iso_time() {
1634 on_second(numeric_system::standard
, pad_type::zero
);
1639 *out_
++ = tm_hour() < 12 ? 'A' : 'P';
1642 format_localized('p');
1646 // These apply to chrono durations but not tm.
1647 void on_duration_value() {}
1648 void on_duration_unit() {}
1651 struct chrono_format_checker
: null_chrono_spec_handler
<chrono_format_checker
> {
1652 bool has_precision_integral
= false;
1654 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no date")); }
1656 template <typename Char
>
1657 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
1658 FMT_CONSTEXPR
void on_day_of_year() {}
1659 FMT_CONSTEXPR
void on_24_hour(numeric_system
, pad_type
) {}
1660 FMT_CONSTEXPR
void on_12_hour(numeric_system
, pad_type
) {}
1661 FMT_CONSTEXPR
void on_minute(numeric_system
, pad_type
) {}
1662 FMT_CONSTEXPR
void on_second(numeric_system
, pad_type
) {}
1663 FMT_CONSTEXPR
void on_12_hour_time() {}
1664 FMT_CONSTEXPR
void on_24_hour_time() {}
1665 FMT_CONSTEXPR
void on_iso_time() {}
1666 FMT_CONSTEXPR
void on_am_pm() {}
1667 FMT_CONSTEXPR
void on_duration_value() const {
1668 if (has_precision_integral
) {
1669 FMT_THROW(format_error("precision not allowed for this argument type"));
1672 FMT_CONSTEXPR
void on_duration_unit() {}
1675 template <typename T
,
1676 FMT_ENABLE_IF(std::is_integral
<T
>::value
&& has_isfinite
<T
>::value
)>
1677 inline auto isfinite(T
) -> bool {
1681 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1682 inline auto mod(T x
, int y
) -> T
{
1683 return x
% static_cast<T
>(y
);
1685 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
1686 inline auto mod(T x
, int y
) -> T
{
1687 return std::fmod(x
, static_cast<T
>(y
));
1690 // If T is an integral type, maps T to its unsigned counterpart, otherwise
1691 // leaves it unchanged (unlike std::make_unsigned).
1692 template <typename T
, bool INTEGRAL
= std::is_integral
<T
>::value
>
1693 struct make_unsigned_or_unchanged
{
1697 template <typename T
> struct make_unsigned_or_unchanged
<T
, true> {
1698 using type
= typename
std::make_unsigned
<T
>::type
;
1701 template <typename Rep
, typename Period
,
1702 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1703 inline auto get_milliseconds(std::chrono::duration
<Rep
, Period
> d
)
1704 -> std::chrono::duration
<Rep
, std::milli
> {
1705 // this may overflow and/or the result may not fit in the
1707 #if FMT_SAFE_DURATION_CAST
1708 using CommonSecondsType
=
1709 typename
std::common_type
<decltype(d
), std::chrono::seconds
>::type
;
1710 const auto d_as_common
= fmt_duration_cast
<CommonSecondsType
>(d
);
1711 const auto d_as_whole_seconds
=
1712 fmt_duration_cast
<std::chrono::seconds
>(d_as_common
);
1713 // this conversion should be nonproblematic
1714 const auto diff
= d_as_common
- d_as_whole_seconds
;
1716 fmt_duration_cast
<std::chrono::duration
<Rep
, std::milli
>>(diff
);
1719 auto s
= fmt_duration_cast
<std::chrono::seconds
>(d
);
1720 return fmt_duration_cast
<std::chrono::milliseconds
>(d
- s
);
1724 template <typename Char
, typename Rep
, typename OutputIt
,
1725 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1726 auto format_duration_value(OutputIt out
, Rep val
, int) -> OutputIt
{
1727 return write
<Char
>(out
, val
);
1730 template <typename Char
, typename Rep
, typename OutputIt
,
1731 FMT_ENABLE_IF(std::is_floating_point
<Rep
>::value
)>
1732 auto format_duration_value(OutputIt out
, Rep val
, int precision
) -> OutputIt
{
1733 auto specs
= format_specs();
1734 specs
.precision
= precision
;
1736 precision
>= 0 ? presentation_type::fixed
: presentation_type::general
;
1737 return write
<Char
>(out
, val
, specs
);
1740 template <typename Char
, typename OutputIt
>
1741 auto copy_unit(string_view unit
, OutputIt out
, Char
) -> OutputIt
{
1742 return std::copy(unit
.begin(), unit
.end(), out
);
1745 template <typename OutputIt
>
1746 auto copy_unit(string_view unit
, OutputIt out
, wchar_t) -> OutputIt
{
1747 // This works when wchar_t is UTF-32 because units only contain characters
1748 // that have the same representation in UTF-16 and UTF-32.
1749 utf8_to_utf16
u(unit
);
1750 return std::copy(u
.c_str(), u
.c_str() + u
.size(), out
);
1753 template <typename Char
, typename Period
, typename OutputIt
>
1754 auto format_duration_unit(OutputIt out
) -> OutputIt
{
1755 if (const char* unit
= get_units
<Period
>())
1756 return copy_unit(string_view(unit
), out
, Char());
1758 out
= write
<Char
>(out
, Period::num
);
1759 if (const_check(Period::den
!= 1)) {
1761 out
= write
<Char
>(out
, Period::den
);
1771 std::locale locale_
;
1773 bool has_locale_
= false;
1776 get_locale(bool localized
, locale_ref loc
) : has_locale_(localized
) {
1777 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
1779 ::new (&locale_
) std::locale(loc
.template get
<std::locale
>());
1783 if (has_locale_
) locale_
.~locale();
1785 operator const std::locale
&() const {
1786 return has_locale_
? locale_
: get_classic_locale();
1790 template <typename FormatContext
, typename OutputIt
, typename Rep
,
1792 struct chrono_formatter
{
1793 FormatContext
& context
;
1796 bool localized
= false;
1797 // rep is unsigned to avoid overflow.
1799 conditional_t
<std::is_integral
<Rep
>::value
&& sizeof(Rep
) < sizeof(int),
1800 unsigned, typename make_unsigned_or_unchanged
<Rep
>::type
>;
1802 using seconds
= std::chrono::duration
<rep
>;
1804 using milliseconds
= std::chrono::duration
<rep
, std::milli
>;
1807 using char_type
= typename
FormatContext::char_type
;
1808 using tm_writer_type
= tm_writer
<OutputIt
, char_type
>;
1810 chrono_formatter(FormatContext
& ctx
, OutputIt o
,
1811 std::chrono::duration
<Rep
, Period
> d
)
1814 val(static_cast<rep
>(d
.count())),
1816 if (d
.count() < 0) {
1821 // this may overflow and/or the result may not fit in the
1823 // might need checked conversion (rep!=Rep)
1824 s
= fmt_duration_cast
<seconds
>(std::chrono::duration
<rep
, Period
>(val
));
1827 // returns true if nan or inf, writes to out.
1828 auto handle_nan_inf() -> bool {
1829 if (isfinite(val
)) {
1845 auto days() const -> Rep
{ return static_cast<Rep
>(s
.count() / 86400); }
1846 auto hour() const -> Rep
{
1847 return static_cast<Rep
>(mod((s
.count() / 3600), 24));
1850 auto hour12() const -> Rep
{
1851 Rep hour
= static_cast<Rep
>(mod((s
.count() / 3600), 12));
1852 return hour
<= 0 ? 12 : hour
;
1855 auto minute() const -> Rep
{
1856 return static_cast<Rep
>(mod((s
.count() / 60), 60));
1858 auto second() const -> Rep
{ return static_cast<Rep
>(mod(s
.count(), 60)); }
1860 auto time() const -> std::tm
{
1861 auto time
= std::tm();
1862 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1863 time
.tm_min
= to_nonnegative_int(minute(), 60);
1864 time
.tm_sec
= to_nonnegative_int(second(), 60);
1875 void write(Rep value
, int width
, pad_type pad
= pad_type::zero
) {
1877 if (isnan(value
)) return write_nan();
1878 uint32_or_64_or_128_t
<int> n
=
1879 to_unsigned(to_nonnegative_int(value
, max_value
<int>()));
1880 int num_digits
= detail::count_digits(n
);
1881 if (width
> num_digits
) {
1882 out
= detail::write_padding(out
, pad
, width
- num_digits
);
1884 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1887 void write_nan() { std::copy_n("nan", 3, out
); }
1888 void write_pinf() { std::copy_n("inf", 3, out
); }
1889 void write_ninf() { std::copy_n("-inf", 4, out
); }
1891 template <typename Callback
, typename
... Args
>
1892 void format_tm(const tm
& time
, Callback cb
, Args
... args
) {
1893 if (isnan(val
)) return write_nan();
1894 get_locale
loc(localized
, context
.locale());
1895 auto w
= tm_writer_type(loc
, out
, time
);
1900 void on_text(const char_type
* begin
, const char_type
* end
) {
1901 std::copy(begin
, end
, out
);
1904 // These are not implemented because durations don't have date information.
1905 void on_abbr_weekday() {}
1906 void on_full_weekday() {}
1907 void on_dec0_weekday(numeric_system
) {}
1908 void on_dec1_weekday(numeric_system
) {}
1909 void on_abbr_month() {}
1910 void on_full_month() {}
1911 void on_datetime(numeric_system
) {}
1912 void on_loc_date(numeric_system
) {}
1913 void on_loc_time(numeric_system
) {}
1914 void on_us_date() {}
1915 void on_iso_date() {}
1916 void on_utc_offset(numeric_system
) {}
1917 void on_tz_name() {}
1918 void on_year(numeric_system
) {}
1919 void on_short_year(numeric_system
) {}
1920 void on_offset_year() {}
1921 void on_century(numeric_system
) {}
1922 void on_iso_week_based_year() {}
1923 void on_iso_week_based_short_year() {}
1924 void on_dec_month(numeric_system
) {}
1925 void on_dec0_week_of_year(numeric_system
, pad_type
) {}
1926 void on_dec1_week_of_year(numeric_system
, pad_type
) {}
1927 void on_iso_week_of_year(numeric_system
, pad_type
) {}
1928 void on_day_of_month(numeric_system
, pad_type
) {}
1930 void on_day_of_year() {
1931 if (handle_nan_inf()) return;
1935 void on_24_hour(numeric_system ns
, pad_type pad
) {
1936 if (handle_nan_inf()) return;
1938 if (ns
== numeric_system::standard
) return write(hour(), 2, pad
);
1940 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1941 format_tm(time
, &tm_writer_type::on_24_hour
, ns
, pad
);
1944 void on_12_hour(numeric_system ns
, pad_type pad
) {
1945 if (handle_nan_inf()) return;
1947 if (ns
== numeric_system::standard
) return write(hour12(), 2, pad
);
1949 time
.tm_hour
= to_nonnegative_int(hour12(), 12);
1950 format_tm(time
, &tm_writer_type::on_12_hour
, ns
, pad
);
1953 void on_minute(numeric_system ns
, pad_type pad
) {
1954 if (handle_nan_inf()) return;
1956 if (ns
== numeric_system::standard
) return write(minute(), 2, pad
);
1958 time
.tm_min
= to_nonnegative_int(minute(), 60);
1959 format_tm(time
, &tm_writer_type::on_minute
, ns
, pad
);
1962 void on_second(numeric_system ns
, pad_type pad
) {
1963 if (handle_nan_inf()) return;
1965 if (ns
== numeric_system::standard
) {
1966 if (std::is_floating_point
<rep
>::value
) {
1967 auto buf
= memory_buffer();
1968 write_floating_seconds(buf
, std::chrono::duration
<rep
, Period
>(val
),
1970 if (negative
) *out
++ = '-';
1971 if (buf
.size() < 2 || buf
[1] == '.') {
1972 out
= detail::write_padding(out
, pad
);
1974 out
= std::copy(buf
.begin(), buf
.end(), out
);
1976 write(second(), 2, pad
);
1977 write_fractional_seconds
<char_type
>(
1978 out
, std::chrono::duration
<rep
, Period
>(val
), precision
);
1983 time
.tm_sec
= to_nonnegative_int(second(), 60);
1984 format_tm(time
, &tm_writer_type::on_second
, ns
, pad
);
1987 void on_12_hour_time() {
1988 if (handle_nan_inf()) return;
1989 format_tm(time(), &tm_writer_type::on_12_hour_time
);
1992 void on_24_hour_time() {
1993 if (handle_nan_inf()) {
2004 void on_iso_time() {
2007 if (handle_nan_inf()) return;
2008 on_second(numeric_system::standard
, pad_type::zero
);
2012 if (handle_nan_inf()) return;
2013 format_tm(time(), &tm_writer_type::on_am_pm
);
2016 void on_duration_value() {
2017 if (handle_nan_inf()) return;
2019 out
= format_duration_value
<char_type
>(out
, val
, precision
);
2022 void on_duration_unit() {
2023 out
= format_duration_unit
<char_type
, Period
>(out
);
2027 } // namespace detail
2029 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
2030 using weekday
= std::chrono::weekday
;
2031 using day
= std::chrono::day
;
2032 using month
= std::chrono::month
;
2033 using year
= std::chrono::year
;
2034 using year_month_day
= std::chrono::year_month_day
;
2036 // A fallback version of weekday.
2039 unsigned char value_
;
2042 weekday() = default;
2043 constexpr explicit weekday(unsigned wd
) noexcept
2044 : value_(static_cast<unsigned char>(wd
!= 7 ? wd
: 0)) {}
2045 constexpr auto c_encoding() const noexcept
-> unsigned { return value_
; }
2050 unsigned char value_
;
2054 constexpr explicit day(unsigned d
) noexcept
2055 : value_(static_cast<unsigned char>(d
)) {}
2056 constexpr explicit operator unsigned() const noexcept
{ return value_
; }
2061 unsigned char value_
;
2065 constexpr explicit month(unsigned m
) noexcept
2066 : value_(static_cast<unsigned char>(m
)) {}
2067 constexpr explicit operator unsigned() const noexcept
{ return value_
; }
2076 constexpr explicit year(int y
) noexcept
: value_(y
) {}
2077 constexpr explicit operator int() const noexcept
{ return value_
; }
2080 class year_month_day
{
2087 year_month_day() = default;
2088 constexpr year_month_day(const year
& y
, const month
& m
, const day
& d
) noexcept
2089 : year_(y
), month_(m
), day_(d
) {}
2090 constexpr auto year() const noexcept
-> fmt::year
{ return year_
; }
2091 constexpr auto month() const noexcept
-> fmt::month
{ return month_
; }
2092 constexpr auto day() const noexcept
-> fmt::day
{ return day_
; }
2096 template <typename Char
>
2097 struct formatter
<weekday
, Char
> : private formatter
<std::tm
, Char
> {
2099 bool localized_
= false;
2100 bool use_tm_formatter_
= false;
2103 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2104 -> decltype(ctx
.begin()) {
2105 auto it
= ctx
.begin(), end
= ctx
.end();
2106 if (it
!= end
&& *it
== 'L') {
2111 use_tm_formatter_
= it
!= end
&& *it
!= '}';
2112 return use_tm_formatter_
? formatter
<std::tm
, Char
>::parse(ctx
) : it
;
2115 template <typename FormatContext
>
2116 auto format(weekday wd
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2117 auto time
= std::tm();
2118 time
.tm_wday
= static_cast<int>(wd
.c_encoding());
2119 if (use_tm_formatter_
) return formatter
<std::tm
, Char
>::format(time
, ctx
);
2120 detail::get_locale
loc(localized_
, ctx
.locale());
2121 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
2122 w
.on_abbr_weekday();
2127 template <typename Char
>
2128 struct formatter
<day
, Char
> : private formatter
<std::tm
, Char
> {
2130 bool use_tm_formatter_
= false;
2133 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2134 -> decltype(ctx
.begin()) {
2135 auto it
= ctx
.begin(), end
= ctx
.end();
2136 use_tm_formatter_
= it
!= end
&& *it
!= '}';
2137 return use_tm_formatter_
? formatter
<std::tm
, Char
>::parse(ctx
) : it
;
2140 template <typename FormatContext
>
2141 auto format(day d
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2142 auto time
= std::tm();
2143 time
.tm_mday
= static_cast<int>(static_cast<unsigned>(d
));
2144 if (use_tm_formatter_
) return formatter
<std::tm
, Char
>::format(time
, ctx
);
2145 detail::get_locale
loc(false, ctx
.locale());
2146 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
2147 w
.on_day_of_month(detail::numeric_system::standard
, detail::pad_type::zero
);
2152 template <typename Char
>
2153 struct formatter
<month
, Char
> : private formatter
<std::tm
, Char
> {
2155 bool localized_
= false;
2156 bool use_tm_formatter_
= false;
2159 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2160 -> decltype(ctx
.begin()) {
2161 auto it
= ctx
.begin(), end
= ctx
.end();
2162 if (it
!= end
&& *it
== 'L') {
2167 use_tm_formatter_
= it
!= end
&& *it
!= '}';
2168 return use_tm_formatter_
? formatter
<std::tm
, Char
>::parse(ctx
) : it
;
2171 template <typename FormatContext
>
2172 auto format(month m
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2173 auto time
= std::tm();
2174 time
.tm_mon
= static_cast<int>(static_cast<unsigned>(m
)) - 1;
2175 if (use_tm_formatter_
) return formatter
<std::tm
, Char
>::format(time
, ctx
);
2176 detail::get_locale
loc(localized_
, ctx
.locale());
2177 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
2183 template <typename Char
>
2184 struct formatter
<year
, Char
> : private formatter
<std::tm
, Char
> {
2186 bool use_tm_formatter_
= false;
2189 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2190 -> decltype(ctx
.begin()) {
2191 auto it
= ctx
.begin(), end
= ctx
.end();
2192 use_tm_formatter_
= it
!= end
&& *it
!= '}';
2193 return use_tm_formatter_
? formatter
<std::tm
, Char
>::parse(ctx
) : it
;
2196 template <typename FormatContext
>
2197 auto format(year y
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2198 auto time
= std::tm();
2199 time
.tm_year
= static_cast<int>(y
) - 1900;
2200 if (use_tm_formatter_
) return formatter
<std::tm
, Char
>::format(time
, ctx
);
2201 detail::get_locale
loc(false, ctx
.locale());
2202 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
2203 w
.on_year(detail::numeric_system::standard
);
2208 template <typename Char
>
2209 struct formatter
<year_month_day
, Char
> : private formatter
<std::tm
, Char
> {
2211 bool use_tm_formatter_
= false;
2214 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2215 -> decltype(ctx
.begin()) {
2216 auto it
= ctx
.begin(), end
= ctx
.end();
2217 use_tm_formatter_
= it
!= end
&& *it
!= '}';
2218 return use_tm_formatter_
? formatter
<std::tm
, Char
>::parse(ctx
) : it
;
2221 template <typename FormatContext
>
2222 auto format(year_month_day val
, FormatContext
& ctx
) const
2223 -> decltype(ctx
.out()) {
2224 auto time
= std::tm();
2225 time
.tm_year
= static_cast<int>(val
.year()) - 1900;
2226 time
.tm_mon
= static_cast<int>(static_cast<unsigned>(val
.month())) - 1;
2227 time
.tm_mday
= static_cast<int>(static_cast<unsigned>(val
.day()));
2228 if (use_tm_formatter_
) return formatter
<std::tm
, Char
>::format(time
, ctx
);
2229 detail::get_locale
loc(true, ctx
.locale());
2230 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
2236 template <typename Rep
, typename Period
, typename Char
>
2237 struct formatter
<std::chrono::duration
<Rep
, Period
>, Char
> {
2239 format_specs specs_
;
2240 detail::arg_ref
<Char
> width_ref_
;
2241 detail::arg_ref
<Char
> precision_ref_
;
2242 bool localized_
= false;
2243 basic_string_view
<Char
> format_str_
;
2246 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2247 -> decltype(ctx
.begin()) {
2248 auto it
= ctx
.begin(), end
= ctx
.end();
2249 if (it
== end
|| *it
== '}') return it
;
2251 it
= detail::parse_align(it
, end
, specs_
);
2252 if (it
== end
) return it
;
2254 it
= detail::parse_dynamic_spec(it
, end
, specs_
.width
, width_ref_
, ctx
);
2255 if (it
== end
) return it
;
2257 auto checker
= detail::chrono_format_checker();
2259 checker
.has_precision_integral
= !std::is_floating_point
<Rep
>::value
;
2260 it
= detail::parse_precision(it
, end
, specs_
.precision
, precision_ref_
,
2263 if (it
!= end
&& *it
== 'L') {
2267 end
= detail::parse_chrono_format(it
, end
, checker
);
2268 format_str_
= {it
, detail::to_unsigned(end
- it
)};
2272 template <typename FormatContext
>
2273 auto format(std::chrono::duration
<Rep
, Period
> d
, FormatContext
& ctx
) const
2274 -> decltype(ctx
.out()) {
2275 auto specs
= specs_
;
2276 auto precision
= specs
.precision
;
2277 specs
.precision
= -1;
2278 auto begin
= format_str_
.begin(), end
= format_str_
.end();
2279 // As a possible future optimization, we could avoid extra copying if width
2280 // is not specified.
2281 auto buf
= basic_memory_buffer
<Char
>();
2282 auto out
= std::back_inserter(buf
);
2283 detail::handle_dynamic_spec
<detail::width_checker
>(specs
.width
, width_ref_
,
2285 detail::handle_dynamic_spec
<detail::precision_checker
>(precision
,
2286 precision_ref_
, ctx
);
2287 if (begin
== end
|| *begin
== '}') {
2288 out
= detail::format_duration_value
<Char
>(out
, d
.count(), precision
);
2289 detail::format_duration_unit
<Char
, Period
>(out
);
2291 using chrono_formatter
=
2292 detail::chrono_formatter
<FormatContext
, decltype(out
), Rep
, Period
>;
2293 auto f
= chrono_formatter(ctx
, out
, d
);
2294 f
.precision
= precision
;
2295 f
.localized
= localized_
;
2296 detail::parse_chrono_format(begin
, end
, f
);
2298 return detail::write(
2299 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs
);
2303 template <typename Char
, typename Duration
>
2304 struct formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2305 Char
> : formatter
<std::tm
, Char
> {
2306 FMT_CONSTEXPR
formatter() {
2307 this->format_str_
= detail::string_literal
<Char
, '%', 'F', ' ', '%', 'T'>{};
2310 template <typename FormatContext
>
2311 auto format(std::chrono::time_point
<std::chrono::system_clock
, Duration
> val
,
2312 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2313 std::tm tm
= gmtime(val
);
2314 using period
= typename
Duration::period
;
2315 if (detail::const_check(
2316 period::num
== 1 && period::den
== 1 &&
2317 !std::is_floating_point
<typename
Duration::rep
>::value
)) {
2318 return formatter
<std::tm
, Char
>::format(tm
, ctx
);
2320 Duration epoch
= val
.time_since_epoch();
2321 Duration subsecs
= detail::fmt_duration_cast
<Duration
>(
2322 epoch
- detail::fmt_duration_cast
<std::chrono::seconds
>(epoch
));
2323 if (subsecs
.count() < 0) {
2325 detail::fmt_duration_cast
<Duration
>(std::chrono::seconds(1));
2329 tm
= gmtime(val
- second
);
2330 subsecs
+= detail::fmt_duration_cast
<Duration
>(std::chrono::seconds(1));
2332 return formatter
<std::tm
, Char
>::do_format(tm
, ctx
, &subsecs
);
2336 #if FMT_USE_LOCAL_TIME
2337 template <typename Char
, typename Duration
>
2338 struct formatter
<std::chrono::local_time
<Duration
>, Char
>
2339 : formatter
<std::tm
, Char
> {
2340 FMT_CONSTEXPR
formatter() {
2341 this->format_str_
= detail::string_literal
<Char
, '%', 'F', ' ', '%', 'T'>{};
2344 template <typename FormatContext
>
2345 auto format(std::chrono::local_time
<Duration
> val
, FormatContext
& ctx
) const
2346 -> decltype(ctx
.out()) {
2347 using period
= typename
Duration::period
;
2348 if (period::num
!= 1 || period::den
!= 1 ||
2349 std::is_floating_point
<typename
Duration::rep
>::value
) {
2350 const auto epoch
= val
.time_since_epoch();
2351 const auto subsecs
= detail::fmt_duration_cast
<Duration
>(
2352 epoch
- detail::fmt_duration_cast
<std::chrono::seconds
>(epoch
));
2354 return formatter
<std::tm
, Char
>::do_format(localtime(val
), ctx
, &subsecs
);
2357 return formatter
<std::tm
, Char
>::format(localtime(val
), ctx
);
2362 #if FMT_USE_UTC_TIME
2363 template <typename Char
, typename Duration
>
2364 struct formatter
<std::chrono::time_point
<std::chrono::utc_clock
, Duration
>,
2366 : formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2368 template <typename FormatContext
>
2369 auto format(std::chrono::time_point
<std::chrono::utc_clock
, Duration
> val
,
2370 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2372 std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2373 Char
>::format(std::chrono::utc_clock::to_sys(val
), ctx
);
2378 template <typename Char
> struct formatter
<std::tm
, Char
> {
2380 format_specs specs_
;
2381 detail::arg_ref
<Char
> width_ref_
;
2384 basic_string_view
<Char
> format_str_
;
2386 template <typename FormatContext
, typename Duration
>
2387 auto do_format(const std::tm
& tm
, FormatContext
& ctx
,
2388 const Duration
* subsecs
) const -> decltype(ctx
.out()) {
2389 auto specs
= specs_
;
2390 auto buf
= basic_memory_buffer
<Char
>();
2391 auto out
= std::back_inserter(buf
);
2392 detail::handle_dynamic_spec
<detail::width_checker
>(specs
.width
, width_ref_
,
2395 auto loc_ref
= ctx
.locale();
2396 detail::get_locale
loc(static_cast<bool>(loc_ref
), loc_ref
);
2398 detail::tm_writer
<decltype(out
), Char
, Duration
>(loc
, out
, tm
, subsecs
);
2399 detail::parse_chrono_format(format_str_
.begin(), format_str_
.end(), w
);
2400 return detail::write(
2401 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs
);
2405 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2406 -> decltype(ctx
.begin()) {
2407 auto it
= ctx
.begin(), end
= ctx
.end();
2408 if (it
== end
|| *it
== '}') return it
;
2410 it
= detail::parse_align(it
, end
, specs_
);
2411 if (it
== end
) return it
;
2413 it
= detail::parse_dynamic_spec(it
, end
, specs_
.width
, width_ref_
, ctx
);
2414 if (it
== end
) return it
;
2416 end
= detail::parse_chrono_format(it
, end
, detail::tm_format_checker());
2417 // Replace the default format_str only if the new spec is not empty.
2418 if (end
!= it
) format_str_
= {it
, detail::to_unsigned(end
- it
)};
2422 template <typename FormatContext
>
2423 auto format(const std::tm
& tm
, FormatContext
& ctx
) const
2424 -> decltype(ctx
.out()) {
2425 return do_format
<FormatContext
, std::chrono::seconds
>(tm
, ctx
, nullptr);
2432 #endif // FMT_CHRONO_H_