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>
25 // Check if std::chrono::local_t is available.
26 #ifndef FMT_USE_LOCAL_TIME
27 # ifdef __cpp_lib_chrono
28 # define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
30 # define FMT_USE_LOCAL_TIME 0
34 // Check if std::chrono::utc_timestamp is available.
35 #ifndef FMT_USE_UTC_TIME
36 # ifdef __cpp_lib_chrono
37 # define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
39 # define FMT_USE_UTC_TIME 0
45 // UWP doesn't provide _tzset.
46 # if FMT_HAS_INCLUDE("winapifamily.h")
47 # include <winapifamily.h>
49 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
50 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
51 # define FMT_USE_TZSET 1
53 # define FMT_USE_TZSET 0
57 // Enable safe chrono durations, unless explicitly disabled.
58 #ifndef FMT_SAFE_DURATION_CAST
59 # define FMT_SAFE_DURATION_CAST 1
61 #if FMT_SAFE_DURATION_CAST
63 // For conversion between std::chrono::durations without undefined
64 // behaviour or erroneous results.
65 // This is a stripped down version of duration_cast, for inclusion in fmt.
66 // See https://github.com/pauldreik/safe_duration_cast
68 // Copyright Paul Dreik 2019
69 namespace safe_duration_cast
{
71 template <typename To
, typename From
,
72 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
73 std::numeric_limits
<From
>::is_signed
==
74 std::numeric_limits
<To
>::is_signed
)>
75 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
77 using F
= std::numeric_limits
<From
>;
78 using T
= std::numeric_limits
<To
>;
79 static_assert(F::is_integer
, "From must be integral");
80 static_assert(T::is_integer
, "To must be integral");
82 // A and B are both signed, or both unsigned.
83 if (detail::const_check(F::digits
<= T::digits
)) {
84 // From fits in To without any problem.
86 // From does not always fit in To, resort to a dynamic check.
87 if (from
< (T::min
)() || from
> (T::max
)()) {
93 return static_cast<To
>(from
);
97 * converts From to To, without loss. If the dynamic value of from
98 * can't be converted to To without loss, ec is set.
100 template <typename To
, typename From
,
101 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
102 std::numeric_limits
<From
>::is_signed
!=
103 std::numeric_limits
<To
>::is_signed
)>
104 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
106 using F
= std::numeric_limits
<From
>;
107 using T
= std::numeric_limits
<To
>;
108 static_assert(F::is_integer
, "From must be integral");
109 static_assert(T::is_integer
, "To must be integral");
111 if (detail::const_check(F::is_signed
&& !T::is_signed
)) {
112 // From may be negative, not allowed!
113 if (fmt::detail::is_negative(from
)) {
117 // From is positive. Can it always fit in To?
118 if (detail::const_check(F::digits
> T::digits
) &&
119 from
> static_cast<From
>(detail::max_value
<To
>())) {
125 if (detail::const_check(!F::is_signed
&& T::is_signed
&&
126 F::digits
>= T::digits
) &&
127 from
> static_cast<From
>(detail::max_value
<To
>())) {
131 return static_cast<To
>(from
); // Lossless conversion.
134 template <typename To
, typename From
,
135 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
136 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
143 * converts From to To if possible, otherwise ec is set.
146 * ---------------------------------|---------------
149 * normal, fits in output | converted (possibly lossy)
150 * normal, does not fit in output | ec is set
151 * subnormal | best effort
155 template <typename To
, typename From
,
156 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
)>
157 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
159 using T
= std::numeric_limits
<To
>;
160 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
161 static_assert(std::is_floating_point
<To
>::value
, "To must be floating");
163 // catch the only happy case
164 if (std::isfinite(from
)) {
165 if (from
>= T::lowest() && from
<= (T::max
)()) {
166 return static_cast<To
>(from
);
173 // nan and inf will be preserved
174 return static_cast<To
>(from
);
177 template <typename To
, typename From
,
178 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
179 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
181 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
186 * safe duration cast between integral durations
188 template <typename To
, typename FromRep
, typename FromPeriod
,
189 FMT_ENABLE_IF(std::is_integral
<FromRep
>::value
),
190 FMT_ENABLE_IF(std::is_integral
<typename
To::rep
>::value
)>
191 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
193 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
195 // the basic idea is that we need to convert from count() in the from type
196 // to count() in the To type, by multiplying it with this:
198 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
200 static_assert(Factor::num
> 0, "num must be positive");
201 static_assert(Factor::den
> 0, "den must be positive");
203 // the conversion is like this: multiply from.count() with Factor::num
204 // /Factor::den and convert it to To::rep, all this without
205 // overflow/underflow. let's start by finding a suitable type that can hold
206 // both To, From and Factor::num
207 using IntermediateRep
=
208 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
209 decltype(Factor::num
)>::type
;
211 // safe conversion to IntermediateRep
212 IntermediateRep count
=
213 lossless_integral_conversion
<IntermediateRep
>(from
.count(), ec
);
215 // multiply with Factor::num without overflow or underflow
216 if (detail::const_check(Factor::num
!= 1)) {
217 const auto max1
= detail::max_value
<IntermediateRep
>() / Factor::num
;
223 (std::numeric_limits
<IntermediateRep
>::min
)() / Factor::num
;
224 if (detail::const_check(!std::is_unsigned
<IntermediateRep
>::value
) &&
229 count
*= Factor::num
;
232 if (detail::const_check(Factor::den
!= 1)) count
/= Factor::den
;
233 auto tocount
= lossless_integral_conversion
<typename
To::rep
>(count
, ec
);
234 return ec
? To() : To(tocount
);
238 * safe duration_cast between floating point durations
240 template <typename To
, typename FromRep
, typename FromPeriod
,
241 FMT_ENABLE_IF(std::is_floating_point
<FromRep
>::value
),
242 FMT_ENABLE_IF(std::is_floating_point
<typename
To::rep
>::value
)>
243 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
245 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
247 if (std::isnan(from
.count())) {
248 // nan in, gives nan out. easy.
249 return To
{std::numeric_limits
<typename
To::rep
>::quiet_NaN()};
251 // maybe we should also check if from is denormal, and decide what to do about
254 // +-inf should be preserved.
255 if (std::isinf(from
.count())) {
256 return To
{from
.count()};
259 // the basic idea is that we need to convert from count() in the from type
260 // to count() in the To type, by multiplying it with this:
262 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
264 static_assert(Factor::num
> 0, "num must be positive");
265 static_assert(Factor::den
> 0, "den must be positive");
267 // the conversion is like this: multiply from.count() with Factor::num
268 // /Factor::den and convert it to To::rep, all this without
269 // overflow/underflow. let's start by finding a suitable type that can hold
270 // both To, From and Factor::num
271 using IntermediateRep
=
272 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
273 decltype(Factor::num
)>::type
;
275 // force conversion of From::rep -> IntermediateRep to be safe,
276 // even if it will never happen be narrowing in this context.
277 IntermediateRep count
=
278 safe_float_conversion
<IntermediateRep
>(from
.count(), ec
);
283 // multiply with Factor::num without overflow or underflow
284 if (detail::const_check(Factor::num
!= 1)) {
285 constexpr auto max1
= detail::max_value
<IntermediateRep
>() /
286 static_cast<IntermediateRep
>(Factor::num
);
291 constexpr auto min1
= std::numeric_limits
<IntermediateRep
>::lowest() /
292 static_cast<IntermediateRep
>(Factor::num
);
297 count
*= static_cast<IntermediateRep
>(Factor::num
);
300 // this can't go wrong, right? den>0 is checked earlier.
301 if (detail::const_check(Factor::den
!= 1)) {
302 using common_t
= typename
std::common_type
<IntermediateRep
, intmax_t>::type
;
303 count
/= static_cast<common_t
>(Factor::den
);
306 // convert to the to type, safely
307 using ToRep
= typename
To::rep
;
309 const ToRep tocount
= safe_float_conversion
<ToRep
>(count
, ec
);
315 } // namespace safe_duration_cast
318 // Prevents expansion of a preceding token as a function-style macro.
319 // Usage: f FMT_NOMACRO()
323 template <typename T
= void> struct null
{};
324 inline null
<> localtime_r
FMT_NOMACRO(...) { return null
<>(); }
325 inline null
<> localtime_s(...) { return null
<>(); }
326 inline null
<> gmtime_r(...) { return null
<>(); }
327 inline null
<> gmtime_s(...) { return null
<>(); }
329 inline const std::locale
& get_classic_locale() {
330 static const auto& locale
= std::locale::classic();
334 template <typename CodeUnit
> struct codecvt_result
{
335 static constexpr const size_t max_size
= 32;
336 CodeUnit buf
[max_size
];
339 template <typename CodeUnit
>
340 constexpr const size_t codecvt_result
<CodeUnit
>::max_size
;
342 template <typename CodeUnit
>
343 void write_codecvt(codecvt_result
<CodeUnit
>& out
, string_view in_buf
,
344 const std::locale
& loc
) {
345 #if FMT_CLANG_VERSION
346 # pragma clang diagnostic push
347 # pragma clang diagnostic ignored "-Wdeprecated"
348 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
349 # pragma clang diagnostic pop
351 auto& f
= std::use_facet
<std::codecvt
<CodeUnit
, char, std::mbstate_t>>(loc
);
353 auto mb
= std::mbstate_t();
354 const char* from_next
= nullptr;
355 auto result
= f
.in(mb
, in_buf
.begin(), in_buf
.end(), from_next
,
356 std::begin(out
.buf
), std::end(out
.buf
), out
.end
);
357 if (result
!= std::codecvt_base::ok
)
358 FMT_THROW(format_error("failed to format time"));
361 template <typename OutputIt
>
362 auto write_encoded_tm_str(OutputIt out
, string_view in
, const std::locale
& loc
)
364 if (detail::is_utf8() && loc
!= get_classic_locale()) {
365 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
367 #if FMT_MSC_VERSION != 0 || \
368 (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
369 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
371 using code_unit
= wchar_t;
373 using code_unit
= char32_t
;
376 using unit_t
= codecvt_result
<code_unit
>;
378 write_codecvt(unit
, in
, loc
);
379 // In UTF-8 is used one to four one-byte code units.
381 to_utf8
<code_unit
, basic_memory_buffer
<char, unit_t::max_size
* 4>>();
382 if (!u
.convert({unit
.buf
, to_unsigned(unit
.end
- unit
.buf
)}))
383 FMT_THROW(format_error("failed to format time"));
384 return copy_str
<char>(u
.c_str(), u
.c_str() + u
.size(), out
);
386 return copy_str
<char>(in
.data(), in
.data() + in
.size(), out
);
389 template <typename Char
, typename OutputIt
,
390 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
391 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
393 codecvt_result
<Char
> unit
;
394 write_codecvt(unit
, sv
, loc
);
395 return copy_str
<Char
>(unit
.buf
, unit
.end
, out
);
398 template <typename Char
, typename OutputIt
,
399 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
400 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
402 return write_encoded_tm_str(out
, sv
, loc
);
405 template <typename Char
>
406 inline void do_write(buffer
<Char
>& buf
, const std::tm
& time
,
407 const std::locale
& loc
, char format
, char modifier
) {
408 auto&& format_buf
= formatbuf
<std::basic_streambuf
<Char
>>(buf
);
409 auto&& os
= std::basic_ostream
<Char
>(&format_buf
);
411 using iterator
= std::ostreambuf_iterator
<Char
>;
412 const auto& facet
= std::use_facet
<std::time_put
<Char
, iterator
>>(loc
);
413 auto end
= facet
.put(os
, os
, Char(' '), &time
, format
, modifier
);
414 if (end
.failed()) FMT_THROW(format_error("failed to format time"));
417 template <typename Char
, typename OutputIt
,
418 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
419 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
420 char format
, char modifier
= 0) -> OutputIt
{
421 auto&& buf
= get_buffer
<Char
>(out
);
422 do_write
<Char
>(buf
, time
, loc
, format
, modifier
);
423 return get_iterator(buf
, out
);
426 template <typename Char
, typename OutputIt
,
427 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
428 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
429 char format
, char modifier
= 0) -> OutputIt
{
430 auto&& buf
= basic_memory_buffer
<Char
>();
431 do_write
<char>(buf
, time
, loc
, format
, modifier
);
432 return write_encoded_tm_str(out
, string_view(buf
.data(), buf
.size()), loc
);
435 } // namespace detail
440 Converts given time since epoch as ``std::time_t`` value into calendar time,
441 expressed in local time. Unlike ``std::localtime``, this function is
442 thread-safe on most platforms.
444 inline std::tm
localtime(std::time_t time
) {
449 dispatcher(std::time_t t
) : time_(t
) {}
452 using namespace fmt::detail
;
453 return handle(localtime_r(&time_
, &tm_
));
456 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
458 bool handle(detail::null
<>) {
459 using namespace fmt::detail
;
460 return fallback(localtime_s(&tm_
, &time_
));
463 bool fallback(int res
) { return res
== 0; }
466 bool fallback(detail::null
<>) {
467 using namespace fmt::detail
;
468 std::tm
* tm
= std::localtime(&time_
);
470 return tm
!= nullptr;
475 // Too big time values may be unsupported.
476 if (!lt
.run()) FMT_THROW(format_error("time_t value out of range"));
480 #if FMT_USE_LOCAL_TIME
481 template <typename Duration
>
482 inline auto localtime(std::chrono::local_time
<Duration
> time
) -> std::tm
{
483 return localtime(std::chrono::system_clock::to_time_t(
484 std::chrono::current_zone()->to_sys(time
)));
489 Converts given time since epoch as ``std::time_t`` value into calendar time,
490 expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
491 function is thread-safe on most platforms.
493 inline std::tm
gmtime(std::time_t time
) {
498 dispatcher(std::time_t t
) : time_(t
) {}
501 using namespace fmt::detail
;
502 return handle(gmtime_r(&time_
, &tm_
));
505 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
507 bool handle(detail::null
<>) {
508 using namespace fmt::detail
;
509 return fallback(gmtime_s(&tm_
, &time_
));
512 bool fallback(int res
) { return res
== 0; }
515 bool fallback(detail::null
<>) {
516 std::tm
* tm
= std::gmtime(&time_
);
518 return tm
!= nullptr;
522 auto gt
= dispatcher(time
);
523 // Too big time values may be unsupported.
524 if (!gt
.run()) FMT_THROW(format_error("time_t value out of range"));
528 inline std::tm
gmtime(
529 std::chrono::time_point
<std::chrono::system_clock
> time_point
) {
530 return gmtime(std::chrono::system_clock::to_time_t(time_point
));
535 // Writes two-digit numbers a, b and c separated by sep to buf.
536 // The method by Pavel Novikov based on
537 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
538 inline void write_digit2_separated(char* buf
, unsigned a
, unsigned b
,
539 unsigned c
, char sep
) {
540 unsigned long long digits
=
541 a
| (b
<< 24) | (static_cast<unsigned long long>(c
) << 48);
542 // Convert each value to BCD.
543 // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
546 // a can be found from x:
549 // y = x + a * 6 = x + floor(x / 10) * 6
550 // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
551 digits
+= (((digits
* 205) >> 11) & 0x000f00000f00000f) * 6;
552 // Put low nibbles to high bytes and high nibbles to low bytes.
553 digits
= ((digits
& 0x00f00000f00000f0) >> 4) |
554 ((digits
& 0x000f00000f00000f) << 8);
555 auto usep
= static_cast<unsigned long long>(sep
);
556 // Add ASCII '0' to each digit byte and insert separators.
557 digits
|= 0x3030003030003030 | (usep
<< 16) | (usep
<< 40);
559 constexpr const size_t len
= 8;
560 if (const_check(is_big_endian())) {
562 std::memcpy(tmp
, &digits
, len
);
563 std::reverse_copy(tmp
, tmp
+ len
, buf
);
565 std::memcpy(buf
, &digits
, len
);
569 template <typename Period
> FMT_CONSTEXPR
inline const char* get_units() {
570 if (std::is_same
<Period
, std::atto
>::value
) return "as";
571 if (std::is_same
<Period
, std::femto
>::value
) return "fs";
572 if (std::is_same
<Period
, std::pico
>::value
) return "ps";
573 if (std::is_same
<Period
, std::nano
>::value
) return "ns";
574 if (std::is_same
<Period
, std::micro
>::value
) return "µs";
575 if (std::is_same
<Period
, std::milli
>::value
) return "ms";
576 if (std::is_same
<Period
, std::centi
>::value
) return "cs";
577 if (std::is_same
<Period
, std::deci
>::value
) return "ds";
578 if (std::is_same
<Period
, std::ratio
<1>>::value
) return "s";
579 if (std::is_same
<Period
, std::deca
>::value
) return "das";
580 if (std::is_same
<Period
, std::hecto
>::value
) return "hs";
581 if (std::is_same
<Period
, std::kilo
>::value
) return "ks";
582 if (std::is_same
<Period
, std::mega
>::value
) return "Ms";
583 if (std::is_same
<Period
, std::giga
>::value
) return "Gs";
584 if (std::is_same
<Period
, std::tera
>::value
) return "Ts";
585 if (std::is_same
<Period
, std::peta
>::value
) return "Ps";
586 if (std::is_same
<Period
, std::exa
>::value
) return "Es";
587 if (std::is_same
<Period
, std::ratio
<60>>::value
) return "m";
588 if (std::is_same
<Period
, std::ratio
<3600>>::value
) return "h";
592 enum class numeric_system
{
594 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
598 // Glibc extensions for formatting numeric values.
599 enum class pad_type
{
601 // Do not pad a numeric result string.
603 // Pad a numeric result string with zeros even if the conversion specifier
604 // character uses space-padding by default.
606 // Pad a numeric result string with spaces.
610 template <typename OutputIt
>
611 auto write_padding(OutputIt out
, pad_type pad
, int width
) -> OutputIt
{
612 if (pad
== pad_type::none
) return out
;
613 return std::fill_n(out
, width
, pad
== pad_type::space
? ' ' : '0');
616 template <typename OutputIt
>
617 auto write_padding(OutputIt out
, pad_type pad
) -> OutputIt
{
618 if (pad
!= pad_type::none
) *out
++ = pad
== pad_type::space
? ' ' : '0';
622 // Parses a put_time-like format string and invokes handler actions.
623 template <typename Char
, typename Handler
>
624 FMT_CONSTEXPR
const Char
* parse_chrono_format(const Char
* begin
,
627 if (begin
== end
|| *begin
== '}') return begin
;
628 if (*begin
!= '%') FMT_THROW(format_error("invalid format"));
630 pad_type pad
= pad_type::unspecified
;
638 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
639 ++ptr
; // consume '%'
640 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
644 pad
= pad_type::space
;
648 pad
= pad_type::none
;
652 pad
= pad_type::zero
;
656 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
660 handler
.on_text(ptr
- 1, ptr
);
663 const Char newline
[] = {'\n'};
664 handler
.on_text(newline
, newline
+ 1);
668 const Char tab
[] = {'\t'};
669 handler
.on_text(tab
, tab
+ 1);
674 handler
.on_year(numeric_system::standard
);
677 handler
.on_short_year(numeric_system::standard
);
680 handler
.on_century(numeric_system::standard
);
683 handler
.on_iso_week_based_year();
686 handler
.on_iso_week_based_short_year();
690 handler
.on_abbr_weekday();
693 handler
.on_full_weekday();
696 handler
.on_dec0_weekday(numeric_system::standard
);
699 handler
.on_dec1_weekday(numeric_system::standard
);
704 handler
.on_abbr_month();
707 handler
.on_full_month();
710 handler
.on_dec_month(numeric_system::standard
);
712 // Day of the year/month:
714 handler
.on_dec0_week_of_year(numeric_system::standard
);
717 handler
.on_dec1_week_of_year(numeric_system::standard
);
720 handler
.on_iso_week_of_year(numeric_system::standard
);
723 handler
.on_day_of_year();
726 handler
.on_day_of_month(numeric_system::standard
);
729 handler
.on_day_of_month_space(numeric_system::standard
);
731 // Hour, minute, second:
733 handler
.on_24_hour(numeric_system::standard
, pad
);
736 handler
.on_12_hour(numeric_system::standard
, pad
);
739 handler
.on_minute(numeric_system::standard
, pad
);
742 handler
.on_second(numeric_system::standard
, pad
);
746 handler
.on_datetime(numeric_system::standard
);
749 handler
.on_loc_date(numeric_system::standard
);
752 handler
.on_loc_time(numeric_system::standard
);
755 handler
.on_us_date();
758 handler
.on_iso_date();
761 handler
.on_12_hour_time();
764 handler
.on_24_hour_time();
767 handler
.on_iso_time();
773 handler
.on_duration_value();
776 handler
.on_duration_unit();
779 handler
.on_utc_offset(numeric_system::standard
);
782 handler
.on_tz_name();
784 // Alternative representation:
786 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
790 handler
.on_year(numeric_system::alternative
);
793 handler
.on_offset_year();
796 handler
.on_century(numeric_system::alternative
);
799 handler
.on_datetime(numeric_system::alternative
);
802 handler
.on_loc_date(numeric_system::alternative
);
805 handler
.on_loc_time(numeric_system::alternative
);
808 handler
.on_utc_offset(numeric_system::alternative
);
811 FMT_THROW(format_error("invalid format"));
816 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
820 handler
.on_short_year(numeric_system::alternative
);
823 handler
.on_dec_month(numeric_system::alternative
);
826 handler
.on_dec0_week_of_year(numeric_system::alternative
);
829 handler
.on_dec1_week_of_year(numeric_system::alternative
);
832 handler
.on_iso_week_of_year(numeric_system::alternative
);
835 handler
.on_day_of_month(numeric_system::alternative
);
838 handler
.on_day_of_month_space(numeric_system::alternative
);
841 handler
.on_dec0_weekday(numeric_system::alternative
);
844 handler
.on_dec1_weekday(numeric_system::alternative
);
847 handler
.on_24_hour(numeric_system::alternative
, pad
);
850 handler
.on_12_hour(numeric_system::alternative
, pad
);
853 handler
.on_minute(numeric_system::alternative
, pad
);
856 handler
.on_second(numeric_system::alternative
, pad
);
859 handler
.on_utc_offset(numeric_system::alternative
);
862 FMT_THROW(format_error("invalid format"));
866 FMT_THROW(format_error("invalid format"));
870 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
874 template <typename Derived
> struct null_chrono_spec_handler
{
875 FMT_CONSTEXPR
void unsupported() {
876 static_cast<Derived
*>(this)->unsupported();
878 FMT_CONSTEXPR
void on_year(numeric_system
) { unsupported(); }
879 FMT_CONSTEXPR
void on_short_year(numeric_system
) { unsupported(); }
880 FMT_CONSTEXPR
void on_offset_year() { unsupported(); }
881 FMT_CONSTEXPR
void on_century(numeric_system
) { unsupported(); }
882 FMT_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
883 FMT_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
884 FMT_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
885 FMT_CONSTEXPR
void on_full_weekday() { unsupported(); }
886 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) { unsupported(); }
887 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) { unsupported(); }
888 FMT_CONSTEXPR
void on_abbr_month() { unsupported(); }
889 FMT_CONSTEXPR
void on_full_month() { unsupported(); }
890 FMT_CONSTEXPR
void on_dec_month(numeric_system
) { unsupported(); }
891 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) { unsupported(); }
892 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) { unsupported(); }
893 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) { unsupported(); }
894 FMT_CONSTEXPR
void on_day_of_year() { unsupported(); }
895 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) { unsupported(); }
896 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) { unsupported(); }
897 FMT_CONSTEXPR
void on_24_hour(numeric_system
) { unsupported(); }
898 FMT_CONSTEXPR
void on_12_hour(numeric_system
) { unsupported(); }
899 FMT_CONSTEXPR
void on_minute(numeric_system
) { unsupported(); }
900 FMT_CONSTEXPR
void on_second(numeric_system
) { unsupported(); }
901 FMT_CONSTEXPR
void on_datetime(numeric_system
) { unsupported(); }
902 FMT_CONSTEXPR
void on_loc_date(numeric_system
) { unsupported(); }
903 FMT_CONSTEXPR
void on_loc_time(numeric_system
) { unsupported(); }
904 FMT_CONSTEXPR
void on_us_date() { unsupported(); }
905 FMT_CONSTEXPR
void on_iso_date() { unsupported(); }
906 FMT_CONSTEXPR
void on_12_hour_time() { unsupported(); }
907 FMT_CONSTEXPR
void on_24_hour_time() { unsupported(); }
908 FMT_CONSTEXPR
void on_iso_time() { unsupported(); }
909 FMT_CONSTEXPR
void on_am_pm() { unsupported(); }
910 FMT_CONSTEXPR
void on_duration_value() { unsupported(); }
911 FMT_CONSTEXPR
void on_duration_unit() { unsupported(); }
912 FMT_CONSTEXPR
void on_utc_offset(numeric_system
) { unsupported(); }
913 FMT_CONSTEXPR
void on_tz_name() { unsupported(); }
916 struct tm_format_checker
: null_chrono_spec_handler
<tm_format_checker
> {
917 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no format")); }
919 template <typename Char
>
920 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
921 FMT_CONSTEXPR
void on_year(numeric_system
) {}
922 FMT_CONSTEXPR
void on_short_year(numeric_system
) {}
923 FMT_CONSTEXPR
void on_offset_year() {}
924 FMT_CONSTEXPR
void on_century(numeric_system
) {}
925 FMT_CONSTEXPR
void on_iso_week_based_year() {}
926 FMT_CONSTEXPR
void on_iso_week_based_short_year() {}
927 FMT_CONSTEXPR
void on_abbr_weekday() {}
928 FMT_CONSTEXPR
void on_full_weekday() {}
929 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) {}
930 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) {}
931 FMT_CONSTEXPR
void on_abbr_month() {}
932 FMT_CONSTEXPR
void on_full_month() {}
933 FMT_CONSTEXPR
void on_dec_month(numeric_system
) {}
934 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) {}
935 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) {}
936 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) {}
937 FMT_CONSTEXPR
void on_day_of_year() {}
938 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) {}
939 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) {}
940 FMT_CONSTEXPR
void on_24_hour(numeric_system
, pad_type
) {}
941 FMT_CONSTEXPR
void on_12_hour(numeric_system
, pad_type
) {}
942 FMT_CONSTEXPR
void on_minute(numeric_system
, pad_type
) {}
943 FMT_CONSTEXPR
void on_second(numeric_system
, pad_type
) {}
944 FMT_CONSTEXPR
void on_datetime(numeric_system
) {}
945 FMT_CONSTEXPR
void on_loc_date(numeric_system
) {}
946 FMT_CONSTEXPR
void on_loc_time(numeric_system
) {}
947 FMT_CONSTEXPR
void on_us_date() {}
948 FMT_CONSTEXPR
void on_iso_date() {}
949 FMT_CONSTEXPR
void on_12_hour_time() {}
950 FMT_CONSTEXPR
void on_24_hour_time() {}
951 FMT_CONSTEXPR
void on_iso_time() {}
952 FMT_CONSTEXPR
void on_am_pm() {}
953 FMT_CONSTEXPR
void on_utc_offset(numeric_system
) {}
954 FMT_CONSTEXPR
void on_tz_name() {}
957 inline const char* tm_wday_full_name(int wday
) {
958 static constexpr const char* full_name_list
[] = {
959 "Sunday", "Monday", "Tuesday", "Wednesday",
960 "Thursday", "Friday", "Saturday"};
961 return wday
>= 0 && wday
<= 6 ? full_name_list
[wday
] : "?";
963 inline const char* tm_wday_short_name(int wday
) {
964 static constexpr const char* short_name_list
[] = {"Sun", "Mon", "Tue", "Wed",
965 "Thu", "Fri", "Sat"};
966 return wday
>= 0 && wday
<= 6 ? short_name_list
[wday
] : "???";
969 inline const char* tm_mon_full_name(int mon
) {
970 static constexpr const char* full_name_list
[] = {
971 "January", "February", "March", "April", "May", "June",
972 "July", "August", "September", "October", "November", "December"};
973 return mon
>= 0 && mon
<= 11 ? full_name_list
[mon
] : "?";
975 inline const char* tm_mon_short_name(int mon
) {
976 static constexpr const char* short_name_list
[] = {
977 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
978 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
980 return mon
>= 0 && mon
<= 11 ? short_name_list
[mon
] : "???";
983 template <typename T
, typename
= void>
984 struct has_member_data_tm_gmtoff
: std::false_type
{};
985 template <typename T
>
986 struct has_member_data_tm_gmtoff
<T
, void_t
<decltype(T::tm_gmtoff
)>>
989 template <typename T
, typename
= void>
990 struct has_member_data_tm_zone
: std::false_type
{};
991 template <typename T
>
992 struct has_member_data_tm_zone
<T
, void_t
<decltype(T::tm_zone
)>>
996 inline void tzset_once() {
997 static bool init
= []() -> bool {
1001 ignore_unused(init
);
1005 // Converts value to Int and checks that it's in the range [0, upper).
1006 template <typename T
, typename Int
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1007 inline Int
to_nonnegative_int(T value
, Int upper
) {
1008 FMT_ASSERT(std::is_unsigned
<Int
>::value
||
1009 (value
>= 0 && to_unsigned(value
) <= to_unsigned(upper
)),
1012 return static_cast<Int
>(value
);
1014 template <typename T
, typename Int
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
1015 inline Int
to_nonnegative_int(T value
, Int upper
) {
1016 if (value
< 0 || value
> static_cast<T
>(upper
))
1017 FMT_THROW(format_error("invalid value"));
1018 return static_cast<Int
>(value
);
1021 constexpr long long pow10(std::uint32_t n
) {
1022 return n
== 0 ? 1 : 10 * pow10(n
- 1);
1025 // Counts the number of fractional digits in the range [0, 18] according to the
1026 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
1027 // microseconds precision.
1028 template <long long Num
, long long Den
, int N
= 0,
1029 bool Enabled
= (N
< 19) && (Num
<= max_value
<long long>() / 10)>
1030 struct count_fractional_digits
{
1031 static constexpr int value
=
1032 Num
% Den
== 0 ? N
: count_fractional_digits
<Num
* 10, Den
, N
+ 1>::value
;
1035 // Base case that doesn't instantiate any more templates
1036 // in order to avoid overflow.
1037 template <long long Num
, long long Den
, int N
>
1038 struct count_fractional_digits
<Num
, Den
, N
, false> {
1039 static constexpr int value
= (Num
% Den
== 0) ? N
: 6;
1042 // Format subseconds which are given as an integer type with an appropriate
1043 // number of digits.
1044 template <typename Char
, typename OutputIt
, typename Duration
>
1045 void write_fractional_seconds(OutputIt
& out
, Duration d
, int precision
= -1) {
1046 constexpr auto num_fractional_digits
=
1047 count_fractional_digits
<Duration::period::num
,
1048 Duration::period::den
>::value
;
1050 using subsecond_precision
= std::chrono::duration
<
1051 typename
std::common_type
<typename
Duration::rep
,
1052 std::chrono::seconds::rep
>::type
,
1053 std::ratio
<1, detail::pow10(num_fractional_digits
)>>;
1055 const auto fractional
=
1056 d
- std::chrono::duration_cast
<std::chrono::seconds
>(d
);
1057 const auto subseconds
=
1058 std::chrono::treat_as_floating_point
<
1059 typename
subsecond_precision::rep
>::value
1060 ? fractional
.count()
1061 : std::chrono::duration_cast
<subsecond_precision
>(fractional
).count();
1062 auto n
= static_cast<uint32_or_64_or_128_t
<long long>>(subseconds
);
1063 const int num_digits
= detail::count_digits(n
);
1065 int leading_zeroes
= (std::max
)(0, num_fractional_digits
- num_digits
);
1066 if (precision
< 0) {
1067 FMT_ASSERT(!std::is_floating_point
<typename
Duration::rep
>::value
, "");
1068 if (std::ratio_less
<typename
subsecond_precision::period
,
1069 std::chrono::seconds::period
>::value
) {
1071 out
= std::fill_n(out
, leading_zeroes
, '0');
1072 out
= format_decimal
<Char
>(out
, n
, num_digits
).end
;
1076 leading_zeroes
= (std::min
)(leading_zeroes
, precision
);
1077 out
= std::fill_n(out
, leading_zeroes
, '0');
1078 int remaining
= precision
- leading_zeroes
;
1079 if (remaining
!= 0 && remaining
< num_digits
) {
1080 n
/= to_unsigned(detail::pow10(to_unsigned(num_digits
- remaining
)));
1081 out
= format_decimal
<Char
>(out
, n
, remaining
).end
;
1084 out
= format_decimal
<Char
>(out
, n
, num_digits
).end
;
1085 remaining
-= num_digits
;
1086 out
= std::fill_n(out
, remaining
, '0');
1090 // Format subseconds which are given as a floating point type with an
1091 // appropriate number of digits. We cannot pass the Duration here, as we
1092 // explicitly need to pass the Rep value in the chrono_formatter.
1093 template <typename Duration
>
1094 void write_floating_seconds(memory_buffer
& buf
, Duration duration
,
1095 int num_fractional_digits
= -1) {
1096 using rep
= typename
Duration::rep
;
1097 FMT_ASSERT(std::is_floating_point
<rep
>::value
, "");
1099 auto val
= duration
.count();
1101 if (num_fractional_digits
< 0) {
1102 // For `std::round` with fallback to `round`:
1103 // On some toolchains `std::round` is not available (e.g. GCC 6).
1104 using namespace std
;
1105 num_fractional_digits
=
1106 count_fractional_digits
<Duration::period::num
,
1107 Duration::period::den
>::value
;
1108 if (num_fractional_digits
< 6 && static_cast<rep
>(round(val
)) != val
)
1109 num_fractional_digits
= 6;
1112 format_to(std::back_inserter(buf
), FMT_STRING("{:.{}f}"),
1113 std::fmod(val
* static_cast<rep
>(Duration::period::num
) /
1114 static_cast<rep
>(Duration::period::den
),
1115 static_cast<rep
>(60)),
1116 num_fractional_digits
);
1119 template <typename OutputIt
, typename Char
,
1120 typename Duration
= std::chrono::seconds
>
1123 static constexpr int days_per_week
= 7;
1125 const std::locale
& loc_
;
1126 const bool is_classic_
;
1128 const Duration
* subsecs_
;
1131 auto tm_sec() const noexcept
-> int {
1132 FMT_ASSERT(tm_
.tm_sec
>= 0 && tm_
.tm_sec
<= 61, "");
1135 auto tm_min() const noexcept
-> int {
1136 FMT_ASSERT(tm_
.tm_min
>= 0 && tm_
.tm_min
<= 59, "");
1139 auto tm_hour() const noexcept
-> int {
1140 FMT_ASSERT(tm_
.tm_hour
>= 0 && tm_
.tm_hour
<= 23, "");
1143 auto tm_mday() const noexcept
-> int {
1144 FMT_ASSERT(tm_
.tm_mday
>= 1 && tm_
.tm_mday
<= 31, "");
1147 auto tm_mon() const noexcept
-> int {
1148 FMT_ASSERT(tm_
.tm_mon
>= 0 && tm_
.tm_mon
<= 11, "");
1151 auto tm_year() const noexcept
-> long long { return 1900ll + tm_
.tm_year
; }
1152 auto tm_wday() const noexcept
-> int {
1153 FMT_ASSERT(tm_
.tm_wday
>= 0 && tm_
.tm_wday
<= 6, "");
1156 auto tm_yday() const noexcept
-> int {
1157 FMT_ASSERT(tm_
.tm_yday
>= 0 && tm_
.tm_yday
<= 365, "");
1161 auto tm_hour12() const noexcept
-> int {
1162 const auto h
= tm_hour();
1163 const auto z
= h
< 12 ? h
: h
- 12;
1164 return z
== 0 ? 12 : z
;
1167 // POSIX and the C Standard are unclear or inconsistent about what %C and %y
1168 // do if the year is negative or exceeds 9999. Use the convention that %C
1169 // concatenated with %y yields the same output as %Y, and that %Y contains at
1170 // least 4 characters, with more only if necessary.
1171 auto split_year_lower(long long year
) const noexcept
-> int {
1172 auto l
= year
% 100;
1173 if (l
< 0) l
= -l
; // l in [0, 99]
1174 return static_cast<int>(l
);
1178 // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
1179 auto iso_year_weeks(long long curr_year
) const noexcept
-> int {
1180 const auto prev_year
= curr_year
- 1;
1182 (curr_year
+ curr_year
/ 4 - curr_year
/ 100 + curr_year
/ 400) %
1185 (prev_year
+ prev_year
/ 4 - prev_year
/ 100 + prev_year
/ 400) %
1187 return 52 + ((curr_p
== 4 || prev_p
== 3) ? 1 : 0);
1189 auto iso_week_num(int tm_yday
, int tm_wday
) const noexcept
-> int {
1190 return (tm_yday
+ 11 - (tm_wday
== 0 ? days_per_week
: tm_wday
)) /
1193 auto tm_iso_week_year() const noexcept
-> long long {
1194 const auto year
= tm_year();
1195 const auto w
= iso_week_num(tm_yday(), tm_wday());
1196 if (w
< 1) return year
- 1;
1197 if (w
> iso_year_weeks(year
)) return year
+ 1;
1200 auto tm_iso_week_of_year() const noexcept
-> int {
1201 const auto year
= tm_year();
1202 const auto w
= iso_week_num(tm_yday(), tm_wday());
1203 if (w
< 1) return iso_year_weeks(year
- 1);
1204 if (w
> iso_year_weeks(year
)) return 1;
1208 void write1(int value
) {
1209 *out_
++ = static_cast<char>('0' + to_unsigned(value
) % 10);
1211 void write2(int value
) {
1212 const char* d
= digits2(to_unsigned(value
) % 100);
1216 void write2(int value
, pad_type pad
) {
1217 unsigned int v
= to_unsigned(value
) % 100;
1219 const char* d
= digits2(v
);
1223 out_
= detail::write_padding(out_
, pad
);
1224 *out_
++ = static_cast<char>('0' + v
);
1228 void write_year_extended(long long year
) {
1229 // At least 4 characters.
1236 uint32_or_64_or_128_t
<long long> n
= to_unsigned(year
);
1237 const int num_digits
= count_digits(n
);
1238 if (width
> num_digits
) out_
= std::fill_n(out_
, width
- num_digits
, '0');
1239 out_
= format_decimal
<Char
>(out_
, n
, num_digits
).end
;
1241 void write_year(long long year
) {
1242 if (year
>= 0 && year
< 10000) {
1243 write2(static_cast<int>(year
/ 100));
1244 write2(static_cast<int>(year
% 100));
1246 write_year_extended(year
);
1250 void write_utc_offset(long offset
, numeric_system ns
) {
1258 write2(static_cast<int>(offset
/ 60));
1259 if (ns
!= numeric_system::standard
) *out_
++ = ':';
1260 write2(static_cast<int>(offset
% 60));
1262 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_gmtoff
<T
>::value
)>
1263 void format_utc_offset_impl(const T
& tm
, numeric_system ns
) {
1264 write_utc_offset(tm
.tm_gmtoff
, ns
);
1266 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_gmtoff
<T
>::value
)>
1267 void format_utc_offset_impl(const T
& tm
, numeric_system ns
) {
1268 #if defined(_WIN32) && defined(_UCRT)
1273 _get_timezone(&offset
);
1276 _get_dstbias(&dstbias
);
1279 write_utc_offset(-offset
, ns
);
1281 if (ns
== numeric_system::standard
) return format_localized('z');
1283 // Extract timezone offset from timezone conversion functions.
1285 std::time_t gt
= std::mktime(>m
);
1286 std::tm ltm
= gmtime(gt
);
1287 std::time_t lt
= std::mktime(<m
);
1288 long offset
= gt
- lt
;
1289 write_utc_offset(offset
, ns
);
1293 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_zone
<T
>::value
)>
1294 void format_tz_name_impl(const T
& tm
) {
1296 out_
= write_tm_str
<Char
>(out_
, tm
.tm_zone
, loc_
);
1298 format_localized('Z');
1300 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_zone
<T
>::value
)>
1301 void format_tz_name_impl(const T
&) {
1302 format_localized('Z');
1305 void format_localized(char format
, char modifier
= 0) {
1306 out_
= write
<Char
>(out_
, tm_
, loc_
, format
, modifier
);
1310 tm_writer(const std::locale
& loc
, OutputIt out
, const std::tm
& tm
,
1311 const Duration
* subsecs
= nullptr)
1313 is_classic_(loc_
== get_classic_locale()),
1318 OutputIt
out() const { return out_
; }
1320 FMT_CONSTEXPR
void on_text(const Char
* begin
, const Char
* end
) {
1321 out_
= copy_str
<Char
>(begin
, end
, out_
);
1324 void on_abbr_weekday() {
1326 out_
= write(out_
, tm_wday_short_name(tm_wday()));
1328 format_localized('a');
1330 void on_full_weekday() {
1332 out_
= write(out_
, tm_wday_full_name(tm_wday()));
1334 format_localized('A');
1336 void on_dec0_weekday(numeric_system ns
) {
1337 if (is_classic_
|| ns
== numeric_system::standard
) return write1(tm_wday());
1338 format_localized('w', 'O');
1340 void on_dec1_weekday(numeric_system ns
) {
1341 if (is_classic_
|| ns
== numeric_system::standard
) {
1342 auto wday
= tm_wday();
1343 write1(wday
== 0 ? days_per_week
: wday
);
1345 format_localized('u', 'O');
1349 void on_abbr_month() {
1351 out_
= write(out_
, tm_mon_short_name(tm_mon()));
1353 format_localized('b');
1355 void on_full_month() {
1357 out_
= write(out_
, tm_mon_full_name(tm_mon()));
1359 format_localized('B');
1362 void on_datetime(numeric_system ns
) {
1368 on_day_of_month_space(numeric_system::standard
);
1372 on_year(numeric_system::standard
);
1374 format_localized('c', ns
== numeric_system::standard
? '\0' : 'E');
1377 void on_loc_date(numeric_system ns
) {
1381 format_localized('x', ns
== numeric_system::standard
? '\0' : 'E');
1383 void on_loc_time(numeric_system ns
) {
1387 format_localized('X', ns
== numeric_system::standard
? '\0' : 'E');
1391 write_digit2_separated(buf
, to_unsigned(tm_mon() + 1),
1392 to_unsigned(tm_mday()),
1393 to_unsigned(split_year_lower(tm_year())), '/');
1394 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1396 void on_iso_date() {
1397 auto year
= tm_year();
1400 if (year
>= 0 && year
< 10000) {
1401 copy2(buf
, digits2(static_cast<size_t>(year
/ 100)));
1404 write_year_extended(year
);
1407 write_digit2_separated(buf
+ 2, static_cast<unsigned>(year
% 100),
1408 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1410 out_
= copy_str
<Char
>(std::begin(buf
) + offset
, std::end(buf
), out_
);
1413 void on_utc_offset(numeric_system ns
) { format_utc_offset_impl(tm_
, ns
); }
1414 void on_tz_name() { format_tz_name_impl(tm_
); }
1416 void on_year(numeric_system ns
) {
1417 if (is_classic_
|| ns
== numeric_system::standard
)
1418 return write_year(tm_year());
1419 format_localized('Y', 'E');
1421 void on_short_year(numeric_system ns
) {
1422 if (is_classic_
|| ns
== numeric_system::standard
)
1423 return write2(split_year_lower(tm_year()));
1424 format_localized('y', 'O');
1426 void on_offset_year() {
1427 if (is_classic_
) return write2(split_year_lower(tm_year()));
1428 format_localized('y', 'E');
1431 void on_century(numeric_system ns
) {
1432 if (is_classic_
|| ns
== numeric_system::standard
) {
1433 auto year
= tm_year();
1434 auto upper
= year
/ 100;
1435 if (year
>= -99 && year
< 0) {
1436 // Zero upper on negative year.
1439 } else if (upper
>= 0 && upper
< 100) {
1440 write2(static_cast<int>(upper
));
1442 out_
= write
<Char
>(out_
, upper
);
1445 format_localized('C', 'E');
1449 void on_dec_month(numeric_system ns
) {
1450 if (is_classic_
|| ns
== numeric_system::standard
)
1451 return write2(tm_mon() + 1);
1452 format_localized('m', 'O');
1455 void on_dec0_week_of_year(numeric_system ns
) {
1456 if (is_classic_
|| ns
== numeric_system::standard
)
1457 return write2((tm_yday() + days_per_week
- tm_wday()) / days_per_week
);
1458 format_localized('U', 'O');
1460 void on_dec1_week_of_year(numeric_system ns
) {
1461 if (is_classic_
|| ns
== numeric_system::standard
) {
1462 auto wday
= tm_wday();
1463 write2((tm_yday() + days_per_week
-
1464 (wday
== 0 ? (days_per_week
- 1) : (wday
- 1))) /
1467 format_localized('W', 'O');
1470 void on_iso_week_of_year(numeric_system ns
) {
1471 if (is_classic_
|| ns
== numeric_system::standard
)
1472 return write2(tm_iso_week_of_year());
1473 format_localized('V', 'O');
1476 void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
1477 void on_iso_week_based_short_year() {
1478 write2(split_year_lower(tm_iso_week_year()));
1481 void on_day_of_year() {
1482 auto yday
= tm_yday() + 1;
1486 void on_day_of_month(numeric_system ns
) {
1487 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_mday());
1488 format_localized('d', 'O');
1490 void on_day_of_month_space(numeric_system ns
) {
1491 if (is_classic_
|| ns
== numeric_system::standard
) {
1492 auto mday
= to_unsigned(tm_mday()) % 100;
1493 const char* d2
= digits2(mday
);
1494 *out_
++ = mday
< 10 ? ' ' : d2
[0];
1497 format_localized('e', 'O');
1501 void on_24_hour(numeric_system ns
, pad_type pad
) {
1502 if (is_classic_
|| ns
== numeric_system::standard
)
1503 return write2(tm_hour(), pad
);
1504 format_localized('H', 'O');
1506 void on_12_hour(numeric_system ns
, pad_type pad
) {
1507 if (is_classic_
|| ns
== numeric_system::standard
)
1508 return write2(tm_hour12(), pad
);
1509 format_localized('I', 'O');
1511 void on_minute(numeric_system ns
, pad_type pad
) {
1512 if (is_classic_
|| ns
== numeric_system::standard
)
1513 return write2(tm_min(), pad
);
1514 format_localized('M', 'O');
1517 void on_second(numeric_system ns
, pad_type pad
) {
1518 if (is_classic_
|| ns
== numeric_system::standard
) {
1519 write2(tm_sec(), pad
);
1521 if (std::is_floating_point
<typename
Duration::rep
>::value
) {
1522 auto buf
= memory_buffer();
1523 write_floating_seconds(buf
, *subsecs_
);
1524 if (buf
.size() > 1) {
1525 // Remove the leading "0", write something like ".123".
1526 out_
= std::copy(buf
.begin() + 1, buf
.end(), out_
);
1529 write_fractional_seconds
<Char
>(out_
, *subsecs_
);
1533 // Currently no formatting of subseconds when a locale is set.
1534 format_localized('S', 'O');
1538 void on_12_hour_time() {
1541 write_digit2_separated(buf
, to_unsigned(tm_hour12()),
1542 to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
1543 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1547 format_localized('r');
1550 void on_24_hour_time() {
1555 void on_iso_time() {
1558 on_second(numeric_system::standard
, pad_type::unspecified
);
1563 *out_
++ = tm_hour() < 12 ? 'A' : 'P';
1566 format_localized('p');
1570 // These apply to chrono durations but not tm.
1571 void on_duration_value() {}
1572 void on_duration_unit() {}
1575 struct chrono_format_checker
: null_chrono_spec_handler
<chrono_format_checker
> {
1576 bool has_precision_integral
= false;
1578 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no date")); }
1580 template <typename Char
>
1581 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
1582 FMT_CONSTEXPR
void on_24_hour(numeric_system
, pad_type
) {}
1583 FMT_CONSTEXPR
void on_12_hour(numeric_system
, pad_type
) {}
1584 FMT_CONSTEXPR
void on_minute(numeric_system
, pad_type
) {}
1585 FMT_CONSTEXPR
void on_second(numeric_system
, pad_type
) {}
1586 FMT_CONSTEXPR
void on_12_hour_time() {}
1587 FMT_CONSTEXPR
void on_24_hour_time() {}
1588 FMT_CONSTEXPR
void on_iso_time() {}
1589 FMT_CONSTEXPR
void on_am_pm() {}
1590 FMT_CONSTEXPR
void on_duration_value() const {
1591 if (has_precision_integral
) {
1592 FMT_THROW(format_error("precision not allowed for this argument type"));
1595 FMT_CONSTEXPR
void on_duration_unit() {}
1598 template <typename T
,
1599 FMT_ENABLE_IF(std::is_integral
<T
>::value
&& has_isfinite
<T
>::value
)>
1600 inline bool isfinite(T
) {
1604 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1605 inline T
mod(T x
, int y
) {
1606 return x
% static_cast<T
>(y
);
1608 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
1609 inline T
mod(T x
, int y
) {
1610 return std::fmod(x
, static_cast<T
>(y
));
1613 // If T is an integral type, maps T to its unsigned counterpart, otherwise
1614 // leaves it unchanged (unlike std::make_unsigned).
1615 template <typename T
, bool INTEGRAL
= std::is_integral
<T
>::value
>
1616 struct make_unsigned_or_unchanged
{
1620 template <typename T
> struct make_unsigned_or_unchanged
<T
, true> {
1621 using type
= typename
std::make_unsigned
<T
>::type
;
1624 #if FMT_SAFE_DURATION_CAST
1625 // throwing version of safe_duration_cast
1626 template <typename To
, typename FromRep
, typename FromPeriod
>
1627 To
fmt_safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
) {
1629 To to
= safe_duration_cast::safe_duration_cast
<To
>(from
, ec
);
1630 if (ec
) FMT_THROW(format_error("cannot format duration"));
1635 template <typename Rep
, typename Period
,
1636 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1637 inline std::chrono::duration
<Rep
, std::milli
> get_milliseconds(
1638 std::chrono::duration
<Rep
, Period
> d
) {
1639 // this may overflow and/or the result may not fit in the
1641 #if FMT_SAFE_DURATION_CAST
1642 using CommonSecondsType
=
1643 typename
std::common_type
<decltype(d
), std::chrono::seconds
>::type
;
1644 const auto d_as_common
= fmt_safe_duration_cast
<CommonSecondsType
>(d
);
1645 const auto d_as_whole_seconds
=
1646 fmt_safe_duration_cast
<std::chrono::seconds
>(d_as_common
);
1647 // this conversion should be nonproblematic
1648 const auto diff
= d_as_common
- d_as_whole_seconds
;
1650 fmt_safe_duration_cast
<std::chrono::duration
<Rep
, std::milli
>>(diff
);
1653 auto s
= std::chrono::duration_cast
<std::chrono::seconds
>(d
);
1654 return std::chrono::duration_cast
<std::chrono::milliseconds
>(d
- s
);
1658 template <typename Char
, typename Rep
, typename OutputIt
,
1659 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1660 OutputIt
format_duration_value(OutputIt out
, Rep val
, int) {
1661 return write
<Char
>(out
, val
);
1664 template <typename Char
, typename Rep
, typename OutputIt
,
1665 FMT_ENABLE_IF(std::is_floating_point
<Rep
>::value
)>
1666 OutputIt
format_duration_value(OutputIt out
, Rep val
, int precision
) {
1667 auto specs
= format_specs
<Char
>();
1668 specs
.precision
= precision
;
1669 specs
.type
= precision
>= 0 ? presentation_type::fixed_lower
1670 : presentation_type::general_lower
;
1671 return write
<Char
>(out
, val
, specs
);
1674 template <typename Char
, typename OutputIt
>
1675 OutputIt
copy_unit(string_view unit
, OutputIt out
, Char
) {
1676 return std::copy(unit
.begin(), unit
.end(), out
);
1679 template <typename OutputIt
>
1680 OutputIt
copy_unit(string_view unit
, OutputIt out
, wchar_t) {
1681 // This works when wchar_t is UTF-32 because units only contain characters
1682 // that have the same representation in UTF-16 and UTF-32.
1683 utf8_to_utf16
u(unit
);
1684 return std::copy(u
.c_str(), u
.c_str() + u
.size(), out
);
1687 template <typename Char
, typename Period
, typename OutputIt
>
1688 OutputIt
format_duration_unit(OutputIt out
) {
1689 if (const char* unit
= get_units
<Period
>())
1690 return copy_unit(string_view(unit
), out
, Char());
1692 out
= write
<Char
>(out
, Period::num
);
1693 if (const_check(Period::den
!= 1)) {
1695 out
= write
<Char
>(out
, Period::den
);
1705 std::locale locale_
;
1707 bool has_locale_
= false;
1710 get_locale(bool localized
, locale_ref loc
) : has_locale_(localized
) {
1712 ::new (&locale_
) std::locale(loc
.template get
<std::locale
>());
1715 if (has_locale_
) locale_
.~locale();
1717 operator const std::locale
&() const {
1718 return has_locale_
? locale_
: get_classic_locale();
1722 template <typename FormatContext
, typename OutputIt
, typename Rep
,
1724 struct chrono_formatter
{
1725 FormatContext
& context
;
1728 bool localized
= false;
1729 // rep is unsigned to avoid overflow.
1731 conditional_t
<std::is_integral
<Rep
>::value
&& sizeof(Rep
) < sizeof(int),
1732 unsigned, typename make_unsigned_or_unchanged
<Rep
>::type
>;
1734 using seconds
= std::chrono::duration
<rep
>;
1736 using milliseconds
= std::chrono::duration
<rep
, std::milli
>;
1739 using char_type
= typename
FormatContext::char_type
;
1740 using tm_writer_type
= tm_writer
<OutputIt
, char_type
>;
1742 chrono_formatter(FormatContext
& ctx
, OutputIt o
,
1743 std::chrono::duration
<Rep
, Period
> d
)
1746 val(static_cast<rep
>(d
.count())),
1748 if (d
.count() < 0) {
1753 // this may overflow and/or the result may not fit in the
1755 #if FMT_SAFE_DURATION_CAST
1756 // might need checked conversion (rep!=Rep)
1757 auto tmpval
= std::chrono::duration
<rep
, Period
>(val
);
1758 s
= fmt_safe_duration_cast
<seconds
>(tmpval
);
1760 s
= std::chrono::duration_cast
<seconds
>(
1761 std::chrono::duration
<rep
, Period
>(val
));
1765 // returns true if nan or inf, writes to out.
1766 bool handle_nan_inf() {
1767 if (isfinite(val
)) {
1783 Rep
hour() const { return static_cast<Rep
>(mod((s
.count() / 3600), 24)); }
1785 Rep
hour12() const {
1786 Rep hour
= static_cast<Rep
>(mod((s
.count() / 3600), 12));
1787 return hour
<= 0 ? 12 : hour
;
1790 Rep
minute() const { return static_cast<Rep
>(mod((s
.count() / 60), 60)); }
1791 Rep
second() const { return static_cast<Rep
>(mod(s
.count(), 60)); }
1793 std::tm
time() const {
1794 auto time
= std::tm();
1795 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1796 time
.tm_min
= to_nonnegative_int(minute(), 60);
1797 time
.tm_sec
= to_nonnegative_int(second(), 60);
1808 void write(Rep value
, int width
, pad_type pad
= pad_type::unspecified
) {
1810 if (isnan(value
)) return write_nan();
1811 uint32_or_64_or_128_t
<int> n
=
1812 to_unsigned(to_nonnegative_int(value
, max_value
<int>()));
1813 int num_digits
= detail::count_digits(n
);
1814 if (width
> num_digits
) {
1815 out
= detail::write_padding(out
, pad
, width
- num_digits
);
1817 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1820 void write_nan() { std::copy_n("nan", 3, out
); }
1821 void write_pinf() { std::copy_n("inf", 3, out
); }
1822 void write_ninf() { std::copy_n("-inf", 4, out
); }
1824 template <typename Callback
, typename
... Args
>
1825 void format_tm(const tm
& time
, Callback cb
, Args
... args
) {
1826 if (isnan(val
)) return write_nan();
1827 get_locale
loc(localized
, context
.locale());
1828 auto w
= tm_writer_type(loc
, out
, time
);
1833 void on_text(const char_type
* begin
, const char_type
* end
) {
1834 std::copy(begin
, end
, out
);
1837 // These are not implemented because durations don't have date information.
1838 void on_abbr_weekday() {}
1839 void on_full_weekday() {}
1840 void on_dec0_weekday(numeric_system
) {}
1841 void on_dec1_weekday(numeric_system
) {}
1842 void on_abbr_month() {}
1843 void on_full_month() {}
1844 void on_datetime(numeric_system
) {}
1845 void on_loc_date(numeric_system
) {}
1846 void on_loc_time(numeric_system
) {}
1847 void on_us_date() {}
1848 void on_iso_date() {}
1849 void on_utc_offset(numeric_system
) {}
1850 void on_tz_name() {}
1851 void on_year(numeric_system
) {}
1852 void on_short_year(numeric_system
) {}
1853 void on_offset_year() {}
1854 void on_century(numeric_system
) {}
1855 void on_iso_week_based_year() {}
1856 void on_iso_week_based_short_year() {}
1857 void on_dec_month(numeric_system
) {}
1858 void on_dec0_week_of_year(numeric_system
) {}
1859 void on_dec1_week_of_year(numeric_system
) {}
1860 void on_iso_week_of_year(numeric_system
) {}
1861 void on_day_of_year() {}
1862 void on_day_of_month(numeric_system
) {}
1863 void on_day_of_month_space(numeric_system
) {}
1865 void on_24_hour(numeric_system ns
, pad_type pad
) {
1866 if (handle_nan_inf()) return;
1868 if (ns
== numeric_system::standard
) return write(hour(), 2, pad
);
1870 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1871 format_tm(time
, &tm_writer_type::on_24_hour
, ns
, pad
);
1874 void on_12_hour(numeric_system ns
, pad_type pad
) {
1875 if (handle_nan_inf()) return;
1877 if (ns
== numeric_system::standard
) return write(hour12(), 2, pad
);
1879 time
.tm_hour
= to_nonnegative_int(hour12(), 12);
1880 format_tm(time
, &tm_writer_type::on_12_hour
, ns
, pad
);
1883 void on_minute(numeric_system ns
, pad_type pad
) {
1884 if (handle_nan_inf()) return;
1886 if (ns
== numeric_system::standard
) return write(minute(), 2, pad
);
1888 time
.tm_min
= to_nonnegative_int(minute(), 60);
1889 format_tm(time
, &tm_writer_type::on_minute
, ns
, pad
);
1892 void on_second(numeric_system ns
, pad_type pad
) {
1893 if (handle_nan_inf()) return;
1895 if (ns
== numeric_system::standard
) {
1896 if (std::is_floating_point
<rep
>::value
) {
1897 auto buf
= memory_buffer();
1898 write_floating_seconds(buf
, std::chrono::duration
<rep
, Period
>(val
),
1900 if (negative
) *out
++ = '-';
1901 if (buf
.size() < 2 || buf
[1] == '.') {
1902 out
= detail::write_padding(out
, pad
);
1904 out
= std::copy(buf
.begin(), buf
.end(), out
);
1906 write(second(), 2, pad
);
1907 write_fractional_seconds
<char_type
>(
1908 out
, std::chrono::duration
<rep
, Period
>(val
), precision
);
1913 time
.tm_sec
= to_nonnegative_int(second(), 60);
1914 format_tm(time
, &tm_writer_type::on_second
, ns
, pad
);
1917 void on_12_hour_time() {
1918 if (handle_nan_inf()) return;
1919 format_tm(time(), &tm_writer_type::on_12_hour_time
);
1922 void on_24_hour_time() {
1923 if (handle_nan_inf()) {
1934 void on_iso_time() {
1937 if (handle_nan_inf()) return;
1938 on_second(numeric_system::standard
, pad_type::unspecified
);
1942 if (handle_nan_inf()) return;
1943 format_tm(time(), &tm_writer_type::on_am_pm
);
1946 void on_duration_value() {
1947 if (handle_nan_inf()) return;
1949 out
= format_duration_value
<char_type
>(out
, val
, precision
);
1952 void on_duration_unit() {
1953 out
= format_duration_unit
<char_type
, Period
>(out
);
1957 } // namespace detail
1959 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1960 using weekday
= std::chrono::weekday
;
1962 // A fallback version of weekday.
1965 unsigned char value
;
1968 weekday() = default;
1969 explicit constexpr weekday(unsigned wd
) noexcept
1970 : value(static_cast<unsigned char>(wd
!= 7 ? wd
: 0)) {}
1971 constexpr unsigned c_encoding() const noexcept
{ return value
; }
1974 class year_month_day
{};
1977 // A rudimentary weekday formatter.
1978 template <typename Char
> struct formatter
<weekday
, Char
> {
1980 bool localized
= false;
1983 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
1984 -> decltype(ctx
.begin()) {
1985 auto begin
= ctx
.begin(), end
= ctx
.end();
1986 if (begin
!= end
&& *begin
== 'L') {
1993 template <typename FormatContext
>
1994 auto format(weekday wd
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
1995 auto time
= std::tm();
1996 time
.tm_wday
= static_cast<int>(wd
.c_encoding());
1997 detail::get_locale
loc(localized
, ctx
.locale());
1998 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
1999 w
.on_abbr_weekday();
2004 template <typename Rep
, typename Period
, typename Char
>
2005 struct formatter
<std::chrono::duration
<Rep
, Period
>, Char
> {
2007 format_specs
<Char
> specs_
;
2008 detail::arg_ref
<Char
> width_ref_
;
2009 detail::arg_ref
<Char
> precision_ref_
;
2010 bool localized_
= false;
2011 basic_string_view
<Char
> format_str_
;
2014 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2015 -> decltype(ctx
.begin()) {
2016 auto it
= ctx
.begin(), end
= ctx
.end();
2017 if (it
== end
|| *it
== '}') return it
;
2019 it
= detail::parse_align(it
, end
, specs_
);
2020 if (it
== end
) return it
;
2022 it
= detail::parse_dynamic_spec(it
, end
, specs_
.width
, width_ref_
, ctx
);
2023 if (it
== end
) return it
;
2025 auto checker
= detail::chrono_format_checker();
2027 checker
.has_precision_integral
= !std::is_floating_point
<Rep
>::value
;
2028 it
= detail::parse_precision(it
, end
, specs_
.precision
, precision_ref_
,
2031 if (it
!= end
&& *it
== 'L') {
2035 end
= detail::parse_chrono_format(it
, end
, checker
);
2036 format_str_
= {it
, detail::to_unsigned(end
- it
)};
2040 template <typename FormatContext
>
2041 auto format(std::chrono::duration
<Rep
, Period
> d
, FormatContext
& ctx
) const
2042 -> decltype(ctx
.out()) {
2043 auto specs
= specs_
;
2044 auto precision
= specs
.precision
;
2045 specs
.precision
= -1;
2046 auto begin
= format_str_
.begin(), end
= format_str_
.end();
2047 // As a possible future optimization, we could avoid extra copying if width
2048 // is not specified.
2049 auto buf
= basic_memory_buffer
<Char
>();
2050 auto out
= std::back_inserter(buf
);
2051 detail::handle_dynamic_spec
<detail::width_checker
>(specs
.width
, width_ref_
,
2053 detail::handle_dynamic_spec
<detail::precision_checker
>(precision
,
2054 precision_ref_
, ctx
);
2055 if (begin
== end
|| *begin
== '}') {
2056 out
= detail::format_duration_value
<Char
>(out
, d
.count(), precision
);
2057 detail::format_duration_unit
<Char
, Period
>(out
);
2059 using chrono_formatter
=
2060 detail::chrono_formatter
<FormatContext
, decltype(out
), Rep
, Period
>;
2061 auto f
= chrono_formatter(ctx
, out
, d
);
2062 f
.precision
= precision
;
2063 f
.localized
= localized_
;
2064 detail::parse_chrono_format(begin
, end
, f
);
2066 return detail::write(
2067 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs
);
2071 template <typename Char
, typename Duration
>
2072 struct formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2073 Char
> : formatter
<std::tm
, Char
> {
2074 FMT_CONSTEXPR
formatter() {
2075 this->format_str_
= detail::string_literal
<Char
, '%', 'F', ' ', '%', 'T'>{};
2078 template <typename FormatContext
>
2079 auto format(std::chrono::time_point
<std::chrono::system_clock
, Duration
> val
,
2080 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2081 using period
= typename
Duration::period
;
2082 if (detail::const_check(
2083 period::num
!= 1 || period::den
!= 1 ||
2084 std::is_floating_point
<typename
Duration::rep
>::value
)) {
2085 const auto epoch
= val
.time_since_epoch();
2086 auto subsecs
= std::chrono::duration_cast
<Duration
>(
2087 epoch
- std::chrono::duration_cast
<std::chrono::seconds
>(epoch
));
2089 if (subsecs
.count() < 0) {
2091 std::chrono::duration_cast
<Duration
>(std::chrono::seconds(1));
2092 if (epoch
.count() < ((Duration::min
)() + second
).count())
2093 FMT_THROW(format_error("duration is too small"));
2098 return formatter
<std::tm
, Char
>::do_format(
2099 gmtime(std::chrono::time_point_cast
<std::chrono::seconds
>(val
)), ctx
,
2103 return formatter
<std::tm
, Char
>::format(
2104 gmtime(std::chrono::time_point_cast
<std::chrono::seconds
>(val
)), ctx
);
2108 #if FMT_USE_LOCAL_TIME
2109 template <typename Char
, typename Duration
>
2110 struct formatter
<std::chrono::local_time
<Duration
>, Char
>
2111 : formatter
<std::tm
, Char
> {
2112 FMT_CONSTEXPR
formatter() {
2113 this->format_str_
= detail::string_literal
<Char
, '%', 'F', ' ', '%', 'T'>{};
2116 template <typename FormatContext
>
2117 auto format(std::chrono::local_time
<Duration
> val
, FormatContext
& ctx
) const
2118 -> decltype(ctx
.out()) {
2119 using period
= typename
Duration::period
;
2120 if (period::num
!= 1 || period::den
!= 1 ||
2121 std::is_floating_point
<typename
Duration::rep
>::value
) {
2122 const auto epoch
= val
.time_since_epoch();
2123 const auto subsecs
= std::chrono::duration_cast
<Duration
>(
2124 epoch
- std::chrono::duration_cast
<std::chrono::seconds
>(epoch
));
2126 return formatter
<std::tm
, Char
>::do_format(
2127 localtime(std::chrono::time_point_cast
<std::chrono::seconds
>(val
)),
2131 return formatter
<std::tm
, Char
>::format(
2132 localtime(std::chrono::time_point_cast
<std::chrono::seconds
>(val
)),
2138 #if FMT_USE_UTC_TIME
2139 template <typename Char
, typename Duration
>
2140 struct formatter
<std::chrono::time_point
<std::chrono::utc_clock
, Duration
>,
2142 : formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2144 template <typename FormatContext
>
2145 auto format(std::chrono::time_point
<std::chrono::utc_clock
, Duration
> val
,
2146 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2148 std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2149 Char
>::format(std::chrono::utc_clock::to_sys(val
), ctx
);
2154 template <typename Char
> struct formatter
<std::tm
, Char
> {
2156 format_specs
<Char
> specs_
;
2157 detail::arg_ref
<Char
> width_ref_
;
2160 basic_string_view
<Char
> format_str_
;
2162 template <typename FormatContext
, typename Duration
>
2163 auto do_format(const std::tm
& tm
, FormatContext
& ctx
,
2164 const Duration
* subsecs
) const -> decltype(ctx
.out()) {
2165 auto specs
= specs_
;
2166 auto buf
= basic_memory_buffer
<Char
>();
2167 auto out
= std::back_inserter(buf
);
2168 detail::handle_dynamic_spec
<detail::width_checker
>(specs
.width
, width_ref_
,
2171 auto loc_ref
= ctx
.locale();
2172 detail::get_locale
loc(static_cast<bool>(loc_ref
), loc_ref
);
2174 detail::tm_writer
<decltype(out
), Char
, Duration
>(loc
, out
, tm
, subsecs
);
2175 detail::parse_chrono_format(format_str_
.begin(), format_str_
.end(), w
);
2176 return detail::write(
2177 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs
);
2181 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
2182 -> decltype(ctx
.begin()) {
2183 auto it
= ctx
.begin(), end
= ctx
.end();
2184 if (it
== end
|| *it
== '}') return it
;
2186 it
= detail::parse_align(it
, end
, specs_
);
2187 if (it
== end
) return it
;
2189 it
= detail::parse_dynamic_spec(it
, end
, specs_
.width
, width_ref_
, ctx
);
2190 if (it
== end
) return it
;
2192 end
= detail::parse_chrono_format(it
, end
, detail::tm_format_checker());
2193 // Replace the default format_str only if the new spec is not empty.
2194 if (end
!= it
) format_str_
= {it
, detail::to_unsigned(end
- it
)};
2198 template <typename FormatContext
>
2199 auto format(const std::tm
& tm
, FormatContext
& ctx
) const
2200 -> decltype(ctx
.out()) {
2201 return do_format
<FormatContext
, std::chrono::seconds
>(tm
, ctx
, nullptr);
2208 #endif // FMT_CHRONO_H_