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.
17 #include <type_traits>
25 // UWP doesn't provide _tzset.
26 # if FMT_HAS_INCLUDE("winapifamily.h")
27 # include <winapifamily.h>
29 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
30 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
31 # define FMT_USE_TZSET 1
33 # define FMT_USE_TZSET 0
37 // Enable safe chrono durations, unless explicitly disabled.
38 #ifndef FMT_SAFE_DURATION_CAST
39 # define FMT_SAFE_DURATION_CAST 1
41 #if FMT_SAFE_DURATION_CAST
43 // For conversion between std::chrono::durations without undefined
44 // behaviour or erroneous results.
45 // This is a stripped down version of duration_cast, for inclusion in fmt.
46 // See https://github.com/pauldreik/safe_duration_cast
48 // Copyright Paul Dreik 2019
49 namespace safe_duration_cast
{
51 template <typename To
, typename From
,
52 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
53 std::numeric_limits
<From
>::is_signed
==
54 std::numeric_limits
<To
>::is_signed
)>
55 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
57 using F
= std::numeric_limits
<From
>;
58 using T
= std::numeric_limits
<To
>;
59 static_assert(F::is_integer
, "From must be integral");
60 static_assert(T::is_integer
, "To must be integral");
62 // A and B are both signed, or both unsigned.
63 if (detail::const_check(F::digits
<= T::digits
)) {
64 // From fits in To without any problem.
66 // From does not always fit in To, resort to a dynamic check.
67 if (from
< (T::min
)() || from
> (T::max
)()) {
73 return static_cast<To
>(from
);
77 * converts From to To, without loss. If the dynamic value of from
78 * can't be converted to To without loss, ec is set.
80 template <typename To
, typename From
,
81 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
&&
82 std::numeric_limits
<From
>::is_signed
!=
83 std::numeric_limits
<To
>::is_signed
)>
84 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
86 using F
= std::numeric_limits
<From
>;
87 using T
= std::numeric_limits
<To
>;
88 static_assert(F::is_integer
, "From must be integral");
89 static_assert(T::is_integer
, "To must be integral");
91 if (detail::const_check(F::is_signed
&& !T::is_signed
)) {
92 // From may be negative, not allowed!
93 if (fmt::detail::is_negative(from
)) {
97 // From is positive. Can it always fit in To?
98 if (detail::const_check(F::digits
> T::digits
) &&
99 from
> static_cast<From
>(detail::max_value
<To
>())) {
105 if (detail::const_check(!F::is_signed
&& T::is_signed
&&
106 F::digits
>= T::digits
) &&
107 from
> static_cast<From
>(detail::max_value
<To
>())) {
111 return static_cast<To
>(from
); // Lossless conversion.
114 template <typename To
, typename From
,
115 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
116 FMT_CONSTEXPR To
lossless_integral_conversion(const From from
, int& ec
) {
123 * converts From to To if possible, otherwise ec is set.
126 * ---------------------------------|---------------
129 * normal, fits in output | converted (possibly lossy)
130 * normal, does not fit in output | ec is set
131 * subnormal | best effort
135 template <typename To
, typename From
,
136 FMT_ENABLE_IF(!std::is_same
<From
, To
>::value
)>
137 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
139 using T
= std::numeric_limits
<To
>;
140 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
141 static_assert(std::is_floating_point
<To
>::value
, "To must be floating");
143 // catch the only happy case
144 if (std::isfinite(from
)) {
145 if (from
>= T::lowest() && from
<= (T::max
)()) {
146 return static_cast<To
>(from
);
153 // nan and inf will be preserved
154 return static_cast<To
>(from
);
157 template <typename To
, typename From
,
158 FMT_ENABLE_IF(std::is_same
<From
, To
>::value
)>
159 FMT_CONSTEXPR To
safe_float_conversion(const From from
, int& ec
) {
161 static_assert(std::is_floating_point
<From
>::value
, "From must be floating");
166 * safe duration cast between integral durations
168 template <typename To
, typename FromRep
, typename FromPeriod
,
169 FMT_ENABLE_IF(std::is_integral
<FromRep
>::value
),
170 FMT_ENABLE_IF(std::is_integral
<typename
To::rep
>::value
)>
171 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
173 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
175 // the basic idea is that we need to convert from count() in the from type
176 // to count() in the To type, by multiplying it with this:
178 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
180 static_assert(Factor::num
> 0, "num must be positive");
181 static_assert(Factor::den
> 0, "den must be positive");
183 // the conversion is like this: multiply from.count() with Factor::num
184 // /Factor::den and convert it to To::rep, all this without
185 // overflow/underflow. let's start by finding a suitable type that can hold
186 // both To, From and Factor::num
187 using IntermediateRep
=
188 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
189 decltype(Factor::num
)>::type
;
191 // safe conversion to IntermediateRep
192 IntermediateRep count
=
193 lossless_integral_conversion
<IntermediateRep
>(from
.count(), ec
);
195 // multiply with Factor::num without overflow or underflow
196 if (detail::const_check(Factor::num
!= 1)) {
197 const auto max1
= detail::max_value
<IntermediateRep
>() / Factor::num
;
203 (std::numeric_limits
<IntermediateRep
>::min
)() / Factor::num
;
208 count
*= Factor::num
;
211 if (detail::const_check(Factor::den
!= 1)) count
/= Factor::den
;
212 auto tocount
= lossless_integral_conversion
<typename
To::rep
>(count
, ec
);
213 return ec
? To() : To(tocount
);
217 * safe duration_cast between floating point durations
219 template <typename To
, typename FromRep
, typename FromPeriod
,
220 FMT_ENABLE_IF(std::is_floating_point
<FromRep
>::value
),
221 FMT_ENABLE_IF(std::is_floating_point
<typename
To::rep
>::value
)>
222 To
safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
,
224 using From
= std::chrono::duration
<FromRep
, FromPeriod
>;
226 if (std::isnan(from
.count())) {
227 // nan in, gives nan out. easy.
228 return To
{std::numeric_limits
<typename
To::rep
>::quiet_NaN()};
230 // maybe we should also check if from is denormal, and decide what to do about
233 // +-inf should be preserved.
234 if (std::isinf(from
.count())) {
235 return To
{from
.count()};
238 // the basic idea is that we need to convert from count() in the from type
239 // to count() in the To type, by multiplying it with this:
241 : std::ratio_divide
<typename
From::period
, typename
To::period
> {};
243 static_assert(Factor::num
> 0, "num must be positive");
244 static_assert(Factor::den
> 0, "den must be positive");
246 // the conversion is like this: multiply from.count() with Factor::num
247 // /Factor::den and convert it to To::rep, all this without
248 // overflow/underflow. let's start by finding a suitable type that can hold
249 // both To, From and Factor::num
250 using IntermediateRep
=
251 typename
std::common_type
<typename
From::rep
, typename
To::rep
,
252 decltype(Factor::num
)>::type
;
254 // force conversion of From::rep -> IntermediateRep to be safe,
255 // even if it will never happen be narrowing in this context.
256 IntermediateRep count
=
257 safe_float_conversion
<IntermediateRep
>(from
.count(), ec
);
262 // multiply with Factor::num without overflow or underflow
263 if (detail::const_check(Factor::num
!= 1)) {
264 constexpr auto max1
= detail::max_value
<IntermediateRep
>() /
265 static_cast<IntermediateRep
>(Factor::num
);
270 constexpr auto min1
= std::numeric_limits
<IntermediateRep
>::lowest() /
271 static_cast<IntermediateRep
>(Factor::num
);
276 count
*= static_cast<IntermediateRep
>(Factor::num
);
279 // this can't go wrong, right? den>0 is checked earlier.
280 if (detail::const_check(Factor::den
!= 1)) {
281 using common_t
= typename
std::common_type
<IntermediateRep
, intmax_t>::type
;
282 count
/= static_cast<common_t
>(Factor::den
);
285 // convert to the to type, safely
286 using ToRep
= typename
To::rep
;
288 const ToRep tocount
= safe_float_conversion
<ToRep
>(count
, ec
);
294 } // namespace safe_duration_cast
297 // Prevents expansion of a preceding token as a function-style macro.
298 // Usage: f FMT_NOMACRO()
302 template <typename T
= void> struct null
{};
303 inline null
<> localtime_r
FMT_NOMACRO(...) { return null
<>(); }
304 inline null
<> localtime_s(...) { return null
<>(); }
305 inline null
<> gmtime_r(...) { return null
<>(); }
306 inline null
<> gmtime_s(...) { return null
<>(); }
308 inline const std::locale
& get_classic_locale() {
309 static const auto& locale
= std::locale::classic();
313 template <typename CodeUnit
> struct codecvt_result
{
314 static constexpr const size_t max_size
= 32;
315 CodeUnit buf
[max_size
];
318 template <typename CodeUnit
>
319 constexpr const size_t codecvt_result
<CodeUnit
>::max_size
;
321 template <typename CodeUnit
>
322 void write_codecvt(codecvt_result
<CodeUnit
>& out
, string_view in_buf
,
323 const std::locale
& loc
) {
324 using codecvt
= std::codecvt
<CodeUnit
, char, std::mbstate_t>;
325 #if FMT_CLANG_VERSION
326 # pragma clang diagnostic push
327 # pragma clang diagnostic ignored "-Wdeprecated"
328 auto& f
= std::use_facet
<codecvt
>(loc
);
329 # pragma clang diagnostic pop
331 auto& f
= std::use_facet
<codecvt
>(loc
);
333 auto mb
= std::mbstate_t();
334 const char* from_next
= nullptr;
335 auto result
= f
.in(mb
, in_buf
.begin(), in_buf
.end(), from_next
,
336 std::begin(out
.buf
), std::end(out
.buf
), out
.end
);
337 if (result
!= std::codecvt_base::ok
)
338 FMT_THROW(format_error("failed to format time"));
341 template <typename OutputIt
>
342 auto write_encoded_tm_str(OutputIt out
, string_view in
, const std::locale
& loc
)
344 if (detail::is_utf8() && loc
!= get_classic_locale()) {
345 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
347 #if FMT_MSC_VER != 0 || \
348 (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
349 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
351 using code_unit
= wchar_t;
353 using code_unit
= char32_t
;
356 using unit_t
= codecvt_result
<code_unit
>;
358 write_codecvt(unit
, in
, loc
);
359 // In UTF-8 is used one to four one-byte code units.
360 auto&& buf
= basic_memory_buffer
<char, unit_t::max_size
* 4>();
361 for (code_unit
* p
= unit
.buf
; p
!= unit
.end
; ++p
) {
362 uint32_t c
= static_cast<uint32_t>(*p
);
363 if (sizeof(code_unit
) == 2 && c
>= 0xd800 && c
<= 0xdfff) {
366 if (p
== unit
.end
|| (c
& 0xfc00) != 0xd800 ||
367 (*p
& 0xfc00) != 0xdc00) {
368 FMT_THROW(format_error("failed to format time"));
370 c
= (c
<< 10) + static_cast<uint32_t>(*p
) - 0x35fdc00;
373 buf
.push_back(static_cast<char>(c
));
374 } else if (c
< 0x800) {
375 buf
.push_back(static_cast<char>(0xc0 | (c
>> 6)));
376 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
377 } else if ((c
>= 0x800 && c
<= 0xd7ff) || (c
>= 0xe000 && c
<= 0xffff)) {
378 buf
.push_back(static_cast<char>(0xe0 | (c
>> 12)));
379 buf
.push_back(static_cast<char>(0x80 | ((c
& 0xfff) >> 6)));
380 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
381 } else if (c
>= 0x10000 && c
<= 0x10ffff) {
382 buf
.push_back(static_cast<char>(0xf0 | (c
>> 18)));
383 buf
.push_back(static_cast<char>(0x80 | ((c
& 0x3ffff) >> 12)));
384 buf
.push_back(static_cast<char>(0x80 | ((c
& 0xfff) >> 6)));
385 buf
.push_back(static_cast<char>(0x80 | (c
& 0x3f)));
387 FMT_THROW(format_error("failed to format time"));
390 return copy_str
<char>(buf
.data(), buf
.data() + buf
.size(), out
);
392 return copy_str
<char>(in
.data(), in
.data() + in
.size(), out
);
395 template <typename Char
, typename OutputIt
,
396 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
397 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
399 codecvt_result
<Char
> unit
;
400 write_codecvt(unit
, sv
, loc
);
401 return copy_str
<Char
>(unit
.buf
, unit
.end
, out
);
404 template <typename Char
, typename OutputIt
,
405 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
406 auto write_tm_str(OutputIt out
, string_view sv
, const std::locale
& loc
)
408 return write_encoded_tm_str(out
, sv
, loc
);
411 template <typename Char
>
412 inline void do_write(buffer
<Char
>& buf
, const std::tm
& time
,
413 const std::locale
& loc
, char format
, char modifier
) {
414 auto&& format_buf
= formatbuf
<std::basic_streambuf
<Char
>>(buf
);
415 auto&& os
= std::basic_ostream
<Char
>(&format_buf
);
417 using iterator
= std::ostreambuf_iterator
<Char
>;
418 const auto& facet
= std::use_facet
<std::time_put
<Char
, iterator
>>(loc
);
419 auto end
= facet
.put(os
, os
, Char(' '), &time
, format
, modifier
);
420 if (end
.failed()) FMT_THROW(format_error("failed to format time"));
423 template <typename Char
, typename OutputIt
,
424 FMT_ENABLE_IF(!std::is_same
<Char
, char>::value
)>
425 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
426 char format
, char modifier
= 0) -> OutputIt
{
427 auto&& buf
= get_buffer
<Char
>(out
);
428 do_write
<Char
>(buf
, time
, loc
, format
, modifier
);
432 template <typename Char
, typename OutputIt
,
433 FMT_ENABLE_IF(std::is_same
<Char
, char>::value
)>
434 auto write(OutputIt out
, const std::tm
& time
, const std::locale
& loc
,
435 char format
, char modifier
= 0) -> OutputIt
{
436 auto&& buf
= basic_memory_buffer
<Char
>();
437 do_write
<char>(buf
, time
, loc
, format
, modifier
);
438 return write_encoded_tm_str(out
, string_view(buf
.data(), buf
.size()), loc
);
441 } // namespace detail
443 FMT_MODULE_EXPORT_BEGIN
446 Converts given time since epoch as ``std::time_t`` value into calendar time,
447 expressed in local time. Unlike ``std::localtime``, this function is
448 thread-safe on most platforms.
450 inline std::tm
localtime(std::time_t time
) {
455 dispatcher(std::time_t t
) : time_(t
) {}
458 using namespace fmt::detail
;
459 return handle(localtime_r(&time_
, &tm_
));
462 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
464 bool handle(detail::null
<>) {
465 using namespace fmt::detail
;
466 return fallback(localtime_s(&tm_
, &time_
));
469 bool fallback(int res
) { return res
== 0; }
472 bool fallback(detail::null
<>) {
473 using namespace fmt::detail
;
474 std::tm
* tm
= std::localtime(&time_
);
476 return tm
!= nullptr;
481 // Too big time values may be unsupported.
482 if (!lt
.run()) FMT_THROW(format_error("time_t value out of range"));
486 inline std::tm
localtime(
487 std::chrono::time_point
<std::chrono::system_clock
> time_point
) {
488 return localtime(std::chrono::system_clock::to_time_t(time_point
));
492 Converts given time since epoch as ``std::time_t`` value into calendar time,
493 expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
494 function is thread-safe on most platforms.
496 inline std::tm
gmtime(std::time_t time
) {
501 dispatcher(std::time_t t
) : time_(t
) {}
504 using namespace fmt::detail
;
505 return handle(gmtime_r(&time_
, &tm_
));
508 bool handle(std::tm
* tm
) { return tm
!= nullptr; }
510 bool handle(detail::null
<>) {
511 using namespace fmt::detail
;
512 return fallback(gmtime_s(&tm_
, &time_
));
515 bool fallback(int res
) { return res
== 0; }
518 bool fallback(detail::null
<>) {
519 std::tm
* tm
= std::gmtime(&time_
);
521 return tm
!= nullptr;
526 // Too big time values may be unsupported.
527 if (!gt
.run()) FMT_THROW(format_error("time_t value out of range"));
531 inline std::tm
gmtime(
532 std::chrono::time_point
<std::chrono::system_clock
> time_point
) {
533 return gmtime(std::chrono::system_clock::to_time_t(time_point
));
536 FMT_BEGIN_DETAIL_NAMESPACE
538 // Writes two-digit numbers a, b and c separated by sep to buf.
539 // The method by Pavel Novikov based on
540 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
541 inline void write_digit2_separated(char* buf
, unsigned a
, unsigned b
,
542 unsigned c
, char sep
) {
543 unsigned long long digits
=
544 a
| (b
<< 24) | (static_cast<unsigned long long>(c
) << 48);
545 // Convert each value to BCD.
546 // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
549 // a can be found from x:
552 // y = x + a * 6 = x + floor(x / 10) * 6
553 // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
554 digits
+= (((digits
* 205) >> 11) & 0x000f00000f00000f) * 6;
555 // Put low nibbles to high bytes and high nibbles to low bytes.
556 digits
= ((digits
& 0x00f00000f00000f0) >> 4) |
557 ((digits
& 0x000f00000f00000f) << 8);
558 auto usep
= static_cast<unsigned long long>(sep
);
559 // Add ASCII '0' to each digit byte and insert separators.
560 digits
|= 0x3030003030003030 | (usep
<< 16) | (usep
<< 40);
562 constexpr const size_t len
= 8;
563 if (const_check(is_big_endian())) {
565 memcpy(tmp
, &digits
, len
);
566 std::reverse_copy(tmp
, tmp
+ len
, buf
);
568 memcpy(buf
, &digits
, len
);
572 template <typename Period
> FMT_CONSTEXPR
inline const char* get_units() {
573 if (std::is_same
<Period
, std::atto
>::value
) return "as";
574 if (std::is_same
<Period
, std::femto
>::value
) return "fs";
575 if (std::is_same
<Period
, std::pico
>::value
) return "ps";
576 if (std::is_same
<Period
, std::nano
>::value
) return "ns";
577 if (std::is_same
<Period
, std::micro
>::value
) return "µs";
578 if (std::is_same
<Period
, std::milli
>::value
) return "ms";
579 if (std::is_same
<Period
, std::centi
>::value
) return "cs";
580 if (std::is_same
<Period
, std::deci
>::value
) return "ds";
581 if (std::is_same
<Period
, std::ratio
<1>>::value
) return "s";
582 if (std::is_same
<Period
, std::deca
>::value
) return "das";
583 if (std::is_same
<Period
, std::hecto
>::value
) return "hs";
584 if (std::is_same
<Period
, std::kilo
>::value
) return "ks";
585 if (std::is_same
<Period
, std::mega
>::value
) return "Ms";
586 if (std::is_same
<Period
, std::giga
>::value
) return "Gs";
587 if (std::is_same
<Period
, std::tera
>::value
) return "Ts";
588 if (std::is_same
<Period
, std::peta
>::value
) return "Ps";
589 if (std::is_same
<Period
, std::exa
>::value
) return "Es";
590 if (std::is_same
<Period
, std::ratio
<60>>::value
) return "m";
591 if (std::is_same
<Period
, std::ratio
<3600>>::value
) return "h";
595 enum class numeric_system
{
597 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
601 // Parses a put_time-like format string and invokes handler actions.
602 template <typename Char
, typename Handler
>
603 FMT_CONSTEXPR
const Char
* parse_chrono_format(const Char
* begin
,
614 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
615 ++ptr
; // consume '%'
616 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
620 handler
.on_text(ptr
- 1, ptr
);
623 const Char newline
[] = {'\n'};
624 handler
.on_text(newline
, newline
+ 1);
628 const Char tab
[] = {'\t'};
629 handler
.on_text(tab
, tab
+ 1);
634 handler
.on_year(numeric_system::standard
);
637 handler
.on_short_year(numeric_system::standard
);
640 handler
.on_century(numeric_system::standard
);
643 handler
.on_iso_week_based_year();
646 handler
.on_iso_week_based_short_year();
650 handler
.on_abbr_weekday();
653 handler
.on_full_weekday();
656 handler
.on_dec0_weekday(numeric_system::standard
);
659 handler
.on_dec1_weekday(numeric_system::standard
);
664 handler
.on_abbr_month();
667 handler
.on_full_month();
670 handler
.on_dec_month(numeric_system::standard
);
672 // Day of the year/month:
674 handler
.on_dec0_week_of_year(numeric_system::standard
);
677 handler
.on_dec1_week_of_year(numeric_system::standard
);
680 handler
.on_iso_week_of_year(numeric_system::standard
);
683 handler
.on_day_of_year();
686 handler
.on_day_of_month(numeric_system::standard
);
689 handler
.on_day_of_month_space(numeric_system::standard
);
691 // Hour, minute, second:
693 handler
.on_24_hour(numeric_system::standard
);
696 handler
.on_12_hour(numeric_system::standard
);
699 handler
.on_minute(numeric_system::standard
);
702 handler
.on_second(numeric_system::standard
);
706 handler
.on_datetime(numeric_system::standard
);
709 handler
.on_loc_date(numeric_system::standard
);
712 handler
.on_loc_time(numeric_system::standard
);
715 handler
.on_us_date();
718 handler
.on_iso_date();
721 handler
.on_12_hour_time();
724 handler
.on_24_hour_time();
727 handler
.on_iso_time();
733 handler
.on_duration_value();
736 handler
.on_duration_unit();
739 handler
.on_utc_offset();
742 handler
.on_tz_name();
744 // Alternative representation:
746 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
750 handler
.on_year(numeric_system::alternative
);
753 handler
.on_offset_year();
756 handler
.on_century(numeric_system::alternative
);
759 handler
.on_datetime(numeric_system::alternative
);
762 handler
.on_loc_date(numeric_system::alternative
);
765 handler
.on_loc_time(numeric_system::alternative
);
768 FMT_THROW(format_error("invalid format"));
773 if (ptr
== end
) FMT_THROW(format_error("invalid format"));
777 handler
.on_short_year(numeric_system::alternative
);
780 handler
.on_dec_month(numeric_system::alternative
);
783 handler
.on_dec0_week_of_year(numeric_system::alternative
);
786 handler
.on_dec1_week_of_year(numeric_system::alternative
);
789 handler
.on_iso_week_of_year(numeric_system::alternative
);
792 handler
.on_day_of_month(numeric_system::alternative
);
795 handler
.on_day_of_month_space(numeric_system::alternative
);
798 handler
.on_dec0_weekday(numeric_system::alternative
);
801 handler
.on_dec1_weekday(numeric_system::alternative
);
804 handler
.on_24_hour(numeric_system::alternative
);
807 handler
.on_12_hour(numeric_system::alternative
);
810 handler
.on_minute(numeric_system::alternative
);
813 handler
.on_second(numeric_system::alternative
);
816 FMT_THROW(format_error("invalid format"));
820 FMT_THROW(format_error("invalid format"));
824 if (begin
!= ptr
) handler
.on_text(begin
, ptr
);
828 template <typename Derived
> struct null_chrono_spec_handler
{
829 FMT_CONSTEXPR
void unsupported() {
830 static_cast<Derived
*>(this)->unsupported();
832 FMT_CONSTEXPR
void on_year(numeric_system
) { unsupported(); }
833 FMT_CONSTEXPR
void on_short_year(numeric_system
) { unsupported(); }
834 FMT_CONSTEXPR
void on_offset_year() { unsupported(); }
835 FMT_CONSTEXPR
void on_century(numeric_system
) { unsupported(); }
836 FMT_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
837 FMT_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
838 FMT_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
839 FMT_CONSTEXPR
void on_full_weekday() { unsupported(); }
840 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) { unsupported(); }
841 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) { unsupported(); }
842 FMT_CONSTEXPR
void on_abbr_month() { unsupported(); }
843 FMT_CONSTEXPR
void on_full_month() { unsupported(); }
844 FMT_CONSTEXPR
void on_dec_month(numeric_system
) { unsupported(); }
845 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) { unsupported(); }
846 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) { unsupported(); }
847 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) { unsupported(); }
848 FMT_CONSTEXPR
void on_day_of_year() { unsupported(); }
849 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) { unsupported(); }
850 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) { unsupported(); }
851 FMT_CONSTEXPR
void on_24_hour(numeric_system
) { unsupported(); }
852 FMT_CONSTEXPR
void on_12_hour(numeric_system
) { unsupported(); }
853 FMT_CONSTEXPR
void on_minute(numeric_system
) { unsupported(); }
854 FMT_CONSTEXPR
void on_second(numeric_system
) { unsupported(); }
855 FMT_CONSTEXPR
void on_datetime(numeric_system
) { unsupported(); }
856 FMT_CONSTEXPR
void on_loc_date(numeric_system
) { unsupported(); }
857 FMT_CONSTEXPR
void on_loc_time(numeric_system
) { unsupported(); }
858 FMT_CONSTEXPR
void on_us_date() { unsupported(); }
859 FMT_CONSTEXPR
void on_iso_date() { unsupported(); }
860 FMT_CONSTEXPR
void on_12_hour_time() { unsupported(); }
861 FMT_CONSTEXPR
void on_24_hour_time() { unsupported(); }
862 FMT_CONSTEXPR
void on_iso_time() { unsupported(); }
863 FMT_CONSTEXPR
void on_am_pm() { unsupported(); }
864 FMT_CONSTEXPR
void on_duration_value() { unsupported(); }
865 FMT_CONSTEXPR
void on_duration_unit() { unsupported(); }
866 FMT_CONSTEXPR
void on_utc_offset() { unsupported(); }
867 FMT_CONSTEXPR
void on_tz_name() { unsupported(); }
870 struct tm_format_checker
: null_chrono_spec_handler
<tm_format_checker
> {
871 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no format")); }
873 template <typename Char
>
874 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
875 FMT_CONSTEXPR
void on_year(numeric_system
) {}
876 FMT_CONSTEXPR
void on_short_year(numeric_system
) {}
877 FMT_CONSTEXPR
void on_offset_year() {}
878 FMT_CONSTEXPR
void on_century(numeric_system
) {}
879 FMT_CONSTEXPR
void on_iso_week_based_year() {}
880 FMT_CONSTEXPR
void on_iso_week_based_short_year() {}
881 FMT_CONSTEXPR
void on_abbr_weekday() {}
882 FMT_CONSTEXPR
void on_full_weekday() {}
883 FMT_CONSTEXPR
void on_dec0_weekday(numeric_system
) {}
884 FMT_CONSTEXPR
void on_dec1_weekday(numeric_system
) {}
885 FMT_CONSTEXPR
void on_abbr_month() {}
886 FMT_CONSTEXPR
void on_full_month() {}
887 FMT_CONSTEXPR
void on_dec_month(numeric_system
) {}
888 FMT_CONSTEXPR
void on_dec0_week_of_year(numeric_system
) {}
889 FMT_CONSTEXPR
void on_dec1_week_of_year(numeric_system
) {}
890 FMT_CONSTEXPR
void on_iso_week_of_year(numeric_system
) {}
891 FMT_CONSTEXPR
void on_day_of_year() {}
892 FMT_CONSTEXPR
void on_day_of_month(numeric_system
) {}
893 FMT_CONSTEXPR
void on_day_of_month_space(numeric_system
) {}
894 FMT_CONSTEXPR
void on_24_hour(numeric_system
) {}
895 FMT_CONSTEXPR
void on_12_hour(numeric_system
) {}
896 FMT_CONSTEXPR
void on_minute(numeric_system
) {}
897 FMT_CONSTEXPR
void on_second(numeric_system
) {}
898 FMT_CONSTEXPR
void on_datetime(numeric_system
) {}
899 FMT_CONSTEXPR
void on_loc_date(numeric_system
) {}
900 FMT_CONSTEXPR
void on_loc_time(numeric_system
) {}
901 FMT_CONSTEXPR
void on_us_date() {}
902 FMT_CONSTEXPR
void on_iso_date() {}
903 FMT_CONSTEXPR
void on_12_hour_time() {}
904 FMT_CONSTEXPR
void on_24_hour_time() {}
905 FMT_CONSTEXPR
void on_iso_time() {}
906 FMT_CONSTEXPR
void on_am_pm() {}
907 FMT_CONSTEXPR
void on_utc_offset() {}
908 FMT_CONSTEXPR
void on_tz_name() {}
911 inline const char* tm_wday_full_name(int wday
) {
912 static constexpr const char* full_name_list
[] = {
913 "Sunday", "Monday", "Tuesday", "Wednesday",
914 "Thursday", "Friday", "Saturday"};
915 return wday
>= 0 && wday
<= 6 ? full_name_list
[wday
] : "?";
917 inline const char* tm_wday_short_name(int wday
) {
918 static constexpr const char* short_name_list
[] = {"Sun", "Mon", "Tue", "Wed",
919 "Thu", "Fri", "Sat"};
920 return wday
>= 0 && wday
<= 6 ? short_name_list
[wday
] : "???";
923 inline const char* tm_mon_full_name(int mon
) {
924 static constexpr const char* full_name_list
[] = {
925 "January", "February", "March", "April", "May", "June",
926 "July", "August", "September", "October", "November", "December"};
927 return mon
>= 0 && mon
<= 11 ? full_name_list
[mon
] : "?";
929 inline const char* tm_mon_short_name(int mon
) {
930 static constexpr const char* short_name_list
[] = {
931 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
932 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
934 return mon
>= 0 && mon
<= 11 ? short_name_list
[mon
] : "???";
937 template <typename T
, typename
= void>
938 struct has_member_data_tm_gmtoff
: std::false_type
{};
939 template <typename T
>
940 struct has_member_data_tm_gmtoff
<T
, void_t
<decltype(T::tm_gmtoff
)>>
943 template <typename T
, typename
= void>
944 struct has_member_data_tm_zone
: std::false_type
{};
945 template <typename T
>
946 struct has_member_data_tm_zone
<T
, void_t
<decltype(T::tm_zone
)>>
950 inline void tzset_once() {
951 static bool init
= []() -> bool {
959 template <typename OutputIt
, typename Char
> class tm_writer
{
961 static constexpr int days_per_week
= 7;
963 const std::locale
& loc_
;
964 const bool is_classic_
;
968 auto tm_sec() const noexcept
-> int {
969 FMT_ASSERT(tm_
.tm_sec
>= 0 && tm_
.tm_sec
<= 61, "");
972 auto tm_min() const noexcept
-> int {
973 FMT_ASSERT(tm_
.tm_min
>= 0 && tm_
.tm_min
<= 59, "");
976 auto tm_hour() const noexcept
-> int {
977 FMT_ASSERT(tm_
.tm_hour
>= 0 && tm_
.tm_hour
<= 23, "");
980 auto tm_mday() const noexcept
-> int {
981 FMT_ASSERT(tm_
.tm_mday
>= 1 && tm_
.tm_mday
<= 31, "");
984 auto tm_mon() const noexcept
-> int {
985 FMT_ASSERT(tm_
.tm_mon
>= 0 && tm_
.tm_mon
<= 11, "");
988 auto tm_year() const noexcept
-> long long { return 1900ll + tm_
.tm_year
; }
989 auto tm_wday() const noexcept
-> int {
990 FMT_ASSERT(tm_
.tm_wday
>= 0 && tm_
.tm_wday
<= 6, "");
993 auto tm_yday() const noexcept
-> int {
994 FMT_ASSERT(tm_
.tm_yday
>= 0 && tm_
.tm_yday
<= 365, "");
998 auto tm_hour12() const noexcept
-> int {
999 const auto h
= tm_hour();
1000 const auto z
= h
< 12 ? h
: h
- 12;
1001 return z
== 0 ? 12 : z
;
1004 // POSIX and the C Standard are unclear or inconsistent about what %C and %y
1005 // do if the year is negative or exceeds 9999. Use the convention that %C
1006 // concatenated with %y yields the same output as %Y, and that %Y contains at
1007 // least 4 characters, with more only if necessary.
1008 auto split_year_lower(long long year
) const noexcept
-> int {
1009 auto l
= year
% 100;
1010 if (l
< 0) l
= -l
; // l in [0, 99]
1011 return static_cast<int>(l
);
1015 // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
1016 auto iso_year_weeks(long long curr_year
) const noexcept
-> int {
1017 const auto prev_year
= curr_year
- 1;
1019 (curr_year
+ curr_year
/ 4 - curr_year
/ 100 + curr_year
/ 400) %
1022 (prev_year
+ prev_year
/ 4 - prev_year
/ 100 + prev_year
/ 400) %
1024 return 52 + ((curr_p
== 4 || prev_p
== 3) ? 1 : 0);
1026 auto iso_week_num(int tm_yday
, int tm_wday
) const noexcept
-> int {
1027 return (tm_yday
+ 11 - (tm_wday
== 0 ? days_per_week
: tm_wday
)) /
1030 auto tm_iso_week_year() const noexcept
-> long long {
1031 const auto year
= tm_year();
1032 const auto w
= iso_week_num(tm_yday(), tm_wday());
1033 if (w
< 1) return year
- 1;
1034 if (w
> iso_year_weeks(year
)) return year
+ 1;
1037 auto tm_iso_week_of_year() const noexcept
-> int {
1038 const auto year
= tm_year();
1039 const auto w
= iso_week_num(tm_yday(), tm_wday());
1040 if (w
< 1) return iso_year_weeks(year
- 1);
1041 if (w
> iso_year_weeks(year
)) return 1;
1045 void write1(int value
) {
1046 *out_
++ = static_cast<char>('0' + to_unsigned(value
) % 10);
1048 void write2(int value
) {
1049 const char* d
= digits2(to_unsigned(value
) % 100);
1054 void write_year_extended(long long year
) {
1055 // At least 4 characters.
1062 uint32_or_64_or_128_t
<long long> n
= to_unsigned(year
);
1063 const int num_digits
= count_digits(n
);
1064 if (width
> num_digits
) out_
= std::fill_n(out_
, width
- num_digits
, '0');
1065 out_
= format_decimal
<Char
>(out_
, n
, num_digits
).end
;
1067 void write_year(long long year
) {
1068 if (year
>= 0 && year
< 10000) {
1069 write2(static_cast<int>(year
/ 100));
1070 write2(static_cast<int>(year
% 100));
1072 write_year_extended(year
);
1076 void write_utc_offset(long offset
) {
1084 write2(static_cast<int>(offset
/ 60));
1085 write2(static_cast<int>(offset
% 60));
1087 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_gmtoff
<T
>::value
)>
1088 void format_utc_offset_impl(const T
& tm
) {
1089 write_utc_offset(tm
.tm_gmtoff
);
1091 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_gmtoff
<T
>::value
)>
1092 void format_utc_offset_impl(const T
& tm
) {
1093 #if defined(_WIN32) && defined(_UCRT)
1098 _get_timezone(&offset
);
1101 _get_dstbias(&dstbias
);
1104 write_utc_offset(-offset
);
1107 format_localized('z');
1111 template <typename T
, FMT_ENABLE_IF(has_member_data_tm_zone
<T
>::value
)>
1112 void format_tz_name_impl(const T
& tm
) {
1114 out_
= write_tm_str
<Char
>(out_
, tm
.tm_zone
, loc_
);
1116 format_localized('Z');
1118 template <typename T
, FMT_ENABLE_IF(!has_member_data_tm_zone
<T
>::value
)>
1119 void format_tz_name_impl(const T
&) {
1120 format_localized('Z');
1123 void format_localized(char format
, char modifier
= 0) {
1124 out_
= write
<Char
>(out_
, tm_
, loc_
, format
, modifier
);
1128 tm_writer(const std::locale
& loc
, OutputIt out
, const std::tm
& tm
)
1130 is_classic_(loc_
== get_classic_locale()),
1134 OutputIt
out() const { return out_
; }
1136 FMT_CONSTEXPR
void on_text(const Char
* begin
, const Char
* end
) {
1137 out_
= copy_str
<Char
>(begin
, end
, out_
);
1140 void on_abbr_weekday() {
1142 out_
= write(out_
, tm_wday_short_name(tm_wday()));
1144 format_localized('a');
1146 void on_full_weekday() {
1148 out_
= write(out_
, tm_wday_full_name(tm_wday()));
1150 format_localized('A');
1152 void on_dec0_weekday(numeric_system ns
) {
1153 if (is_classic_
|| ns
== numeric_system::standard
) return write1(tm_wday());
1154 format_localized('w', 'O');
1156 void on_dec1_weekday(numeric_system ns
) {
1157 if (is_classic_
|| ns
== numeric_system::standard
) {
1158 auto wday
= tm_wday();
1159 write1(wday
== 0 ? days_per_week
: wday
);
1161 format_localized('u', 'O');
1165 void on_abbr_month() {
1167 out_
= write(out_
, tm_mon_short_name(tm_mon()));
1169 format_localized('b');
1171 void on_full_month() {
1173 out_
= write(out_
, tm_mon_full_name(tm_mon()));
1175 format_localized('B');
1178 void on_datetime(numeric_system ns
) {
1184 on_day_of_month_space(numeric_system::standard
);
1188 on_year(numeric_system::standard
);
1190 format_localized('c', ns
== numeric_system::standard
? '\0' : 'E');
1193 void on_loc_date(numeric_system ns
) {
1197 format_localized('x', ns
== numeric_system::standard
? '\0' : 'E');
1199 void on_loc_time(numeric_system ns
) {
1203 format_localized('X', ns
== numeric_system::standard
? '\0' : 'E');
1207 write_digit2_separated(buf
, to_unsigned(tm_mon() + 1),
1208 to_unsigned(tm_mday()),
1209 to_unsigned(split_year_lower(tm_year())), '/');
1210 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1212 void on_iso_date() {
1213 auto year
= tm_year();
1216 if (year
>= 0 && year
< 10000) {
1217 copy2(buf
, digits2(to_unsigned(year
/ 100)));
1220 write_year_extended(year
);
1223 write_digit2_separated(buf
+ 2, static_cast<unsigned>(year
% 100),
1224 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1226 out_
= copy_str
<Char
>(std::begin(buf
) + offset
, std::end(buf
), out_
);
1229 void on_utc_offset() { format_utc_offset_impl(tm_
); }
1230 void on_tz_name() { format_tz_name_impl(tm_
); }
1232 void on_year(numeric_system ns
) {
1233 if (is_classic_
|| ns
== numeric_system::standard
)
1234 return write_year(tm_year());
1235 format_localized('Y', 'E');
1237 void on_short_year(numeric_system ns
) {
1238 if (is_classic_
|| ns
== numeric_system::standard
)
1239 return write2(split_year_lower(tm_year()));
1240 format_localized('y', 'O');
1242 void on_offset_year() {
1243 if (is_classic_
) return write2(split_year_lower(tm_year()));
1244 format_localized('y', 'E');
1247 void on_century(numeric_system ns
) {
1248 if (is_classic_
|| ns
== numeric_system::standard
) {
1249 auto year
= tm_year();
1250 auto upper
= year
/ 100;
1251 if (year
>= -99 && year
< 0) {
1252 // Zero upper on negative year.
1255 } else if (upper
>= 0 && upper
< 100) {
1256 write2(static_cast<int>(upper
));
1258 out_
= write
<Char
>(out_
, upper
);
1261 format_localized('C', 'E');
1265 void on_dec_month(numeric_system ns
) {
1266 if (is_classic_
|| ns
== numeric_system::standard
)
1267 return write2(tm_mon() + 1);
1268 format_localized('m', 'O');
1271 void on_dec0_week_of_year(numeric_system ns
) {
1272 if (is_classic_
|| ns
== numeric_system::standard
)
1273 return write2((tm_yday() + days_per_week
- tm_wday()) / days_per_week
);
1274 format_localized('U', 'O');
1276 void on_dec1_week_of_year(numeric_system ns
) {
1277 if (is_classic_
|| ns
== numeric_system::standard
) {
1278 auto wday
= tm_wday();
1279 write2((tm_yday() + days_per_week
-
1280 (wday
== 0 ? (days_per_week
- 1) : (wday
- 1))) /
1283 format_localized('W', 'O');
1286 void on_iso_week_of_year(numeric_system ns
) {
1287 if (is_classic_
|| ns
== numeric_system::standard
)
1288 return write2(tm_iso_week_of_year());
1289 format_localized('V', 'O');
1292 void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
1293 void on_iso_week_based_short_year() {
1294 write2(split_year_lower(tm_iso_week_year()));
1297 void on_day_of_year() {
1298 auto yday
= tm_yday() + 1;
1302 void on_day_of_month(numeric_system ns
) {
1303 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_mday());
1304 format_localized('d', 'O');
1306 void on_day_of_month_space(numeric_system ns
) {
1307 if (is_classic_
|| ns
== numeric_system::standard
) {
1308 auto mday
= to_unsigned(tm_mday()) % 100;
1309 const char* d2
= digits2(mday
);
1310 *out_
++ = mday
< 10 ? ' ' : d2
[0];
1313 format_localized('e', 'O');
1317 void on_24_hour(numeric_system ns
) {
1318 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_hour());
1319 format_localized('H', 'O');
1321 void on_12_hour(numeric_system ns
) {
1322 if (is_classic_
|| ns
== numeric_system::standard
)
1323 return write2(tm_hour12());
1324 format_localized('I', 'O');
1326 void on_minute(numeric_system ns
) {
1327 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_min());
1328 format_localized('M', 'O');
1330 void on_second(numeric_system ns
) {
1331 if (is_classic_
|| ns
== numeric_system::standard
) return write2(tm_sec());
1332 format_localized('S', 'O');
1335 void on_12_hour_time() {
1338 write_digit2_separated(buf
, to_unsigned(tm_hour12()),
1339 to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
1340 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1344 format_localized('r');
1347 void on_24_hour_time() {
1352 void on_iso_time() {
1354 write_digit2_separated(buf
, to_unsigned(tm_hour()), to_unsigned(tm_min()),
1355 to_unsigned(tm_sec()), ':');
1356 out_
= copy_str
<Char
>(std::begin(buf
), std::end(buf
), out_
);
1361 *out_
++ = tm_hour() < 12 ? 'A' : 'P';
1364 format_localized('p');
1368 // These apply to chrono durations but not tm.
1369 void on_duration_value() {}
1370 void on_duration_unit() {}
1373 struct chrono_format_checker
: null_chrono_spec_handler
<chrono_format_checker
> {
1374 FMT_NORETURN
void unsupported() { FMT_THROW(format_error("no date")); }
1376 template <typename Char
>
1377 FMT_CONSTEXPR
void on_text(const Char
*, const Char
*) {}
1378 FMT_CONSTEXPR
void on_24_hour(numeric_system
) {}
1379 FMT_CONSTEXPR
void on_12_hour(numeric_system
) {}
1380 FMT_CONSTEXPR
void on_minute(numeric_system
) {}
1381 FMT_CONSTEXPR
void on_second(numeric_system
) {}
1382 FMT_CONSTEXPR
void on_12_hour_time() {}
1383 FMT_CONSTEXPR
void on_24_hour_time() {}
1384 FMT_CONSTEXPR
void on_iso_time() {}
1385 FMT_CONSTEXPR
void on_am_pm() {}
1386 FMT_CONSTEXPR
void on_duration_value() {}
1387 FMT_CONSTEXPR
void on_duration_unit() {}
1390 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1391 inline bool isnan(T
) {
1394 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
1395 inline bool isnan(T value
) {
1396 return std::isnan(value
);
1399 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1400 inline bool isfinite(T
) {
1404 // Converts value to Int and checks that it's in the range [0, upper).
1405 template <typename T
, typename Int
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1406 inline Int
to_nonnegative_int(T value
, Int upper
) {
1407 FMT_ASSERT(value
>= 0 && to_unsigned(value
) <= to_unsigned(upper
),
1410 return static_cast<Int
>(value
);
1412 template <typename T
, typename Int
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
1413 inline Int
to_nonnegative_int(T value
, Int upper
) {
1414 if (value
< 0 || value
> static_cast<T
>(upper
))
1415 FMT_THROW(format_error("invalid value"));
1416 return static_cast<Int
>(value
);
1419 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
1420 inline T
mod(T x
, int y
) {
1421 return x
% static_cast<T
>(y
);
1423 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
1424 inline T
mod(T x
, int y
) {
1425 return std::fmod(x
, static_cast<T
>(y
));
1428 // If T is an integral type, maps T to its unsigned counterpart, otherwise
1429 // leaves it unchanged (unlike std::make_unsigned).
1430 template <typename T
, bool INTEGRAL
= std::is_integral
<T
>::value
>
1431 struct make_unsigned_or_unchanged
{
1435 template <typename T
> struct make_unsigned_or_unchanged
<T
, true> {
1436 using type
= typename
std::make_unsigned
<T
>::type
;
1439 #if FMT_SAFE_DURATION_CAST
1440 // throwing version of safe_duration_cast
1441 template <typename To
, typename FromRep
, typename FromPeriod
>
1442 To
fmt_safe_duration_cast(std::chrono::duration
<FromRep
, FromPeriod
> from
) {
1444 To to
= safe_duration_cast::safe_duration_cast
<To
>(from
, ec
);
1445 if (ec
) FMT_THROW(format_error("cannot format duration"));
1450 template <typename Rep
, typename Period
,
1451 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1452 inline std::chrono::duration
<Rep
, std::milli
> get_milliseconds(
1453 std::chrono::duration
<Rep
, Period
> d
) {
1454 // this may overflow and/or the result may not fit in the
1456 #if FMT_SAFE_DURATION_CAST
1457 using CommonSecondsType
=
1458 typename
std::common_type
<decltype(d
), std::chrono::seconds
>::type
;
1459 const auto d_as_common
= fmt_safe_duration_cast
<CommonSecondsType
>(d
);
1460 const auto d_as_whole_seconds
=
1461 fmt_safe_duration_cast
<std::chrono::seconds
>(d_as_common
);
1462 // this conversion should be nonproblematic
1463 const auto diff
= d_as_common
- d_as_whole_seconds
;
1465 fmt_safe_duration_cast
<std::chrono::duration
<Rep
, std::milli
>>(diff
);
1468 auto s
= std::chrono::duration_cast
<std::chrono::seconds
>(d
);
1469 return std::chrono::duration_cast
<std::chrono::milliseconds
>(d
- s
);
1473 // Returns the number of fractional digits in the range [0, 18] according to the
1474 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
1475 // microseconds precision.
1476 constexpr int count_fractional_digits(long long num
, long long den
, int n
= 0) {
1477 return num
% den
== 0
1479 : (n
> 18 ? 6 : count_fractional_digits(num
* 10, den
, n
+ 1));
1482 constexpr long long pow10(std::uint32_t n
) {
1483 return n
== 0 ? 1 : 10 * pow10(n
- 1);
1486 template <class Rep
, class Period
,
1487 FMT_ENABLE_IF(std::numeric_limits
<Rep
>::is_signed
)>
1488 constexpr std::chrono::duration
<Rep
, Period
> abs(
1489 std::chrono::duration
<Rep
, Period
> d
) {
1490 // We need to compare the duration using the count() method directly
1491 // due to a compiler bug in clang-11 regarding the spaceship operator,
1492 // when -Wzero-as-null-pointer-constant is enabled.
1493 // In clang-12 the bug has been fixed. See
1494 // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
1495 // https://www.godbolt.org/z/Knbb5joYx.
1496 return d
.count() >= d
.zero().count() ? d
: -d
;
1499 template <class Rep
, class Period
,
1500 FMT_ENABLE_IF(!std::numeric_limits
<Rep
>::is_signed
)>
1501 constexpr std::chrono::duration
<Rep
, Period
> abs(
1502 std::chrono::duration
<Rep
, Period
> d
) {
1506 template <typename Char
, typename Rep
, typename OutputIt
,
1507 FMT_ENABLE_IF(std::is_integral
<Rep
>::value
)>
1508 OutputIt
format_duration_value(OutputIt out
, Rep val
, int) {
1509 return write
<Char
>(out
, val
);
1512 template <typename Char
, typename Rep
, typename OutputIt
,
1513 FMT_ENABLE_IF(std::is_floating_point
<Rep
>::value
)>
1514 OutputIt
format_duration_value(OutputIt out
, Rep val
, int precision
) {
1515 auto specs
= basic_format_specs
<Char
>();
1516 specs
.precision
= precision
;
1517 specs
.type
= precision
>= 0 ? presentation_type::fixed_lower
1518 : presentation_type::general_lower
;
1519 return write
<Char
>(out
, val
, specs
);
1522 template <typename Char
, typename OutputIt
>
1523 OutputIt
copy_unit(string_view unit
, OutputIt out
, Char
) {
1524 return std::copy(unit
.begin(), unit
.end(), out
);
1527 template <typename OutputIt
>
1528 OutputIt
copy_unit(string_view unit
, OutputIt out
, wchar_t) {
1529 // This works when wchar_t is UTF-32 because units only contain characters
1530 // that have the same representation in UTF-16 and UTF-32.
1531 utf8_to_utf16
u(unit
);
1532 return std::copy(u
.c_str(), u
.c_str() + u
.size(), out
);
1535 template <typename Char
, typename Period
, typename OutputIt
>
1536 OutputIt
format_duration_unit(OutputIt out
) {
1537 if (const char* unit
= get_units
<Period
>())
1538 return copy_unit(string_view(unit
), out
, Char());
1540 out
= write
<Char
>(out
, Period::num
);
1541 if (const_check(Period::den
!= 1)) {
1543 out
= write
<Char
>(out
, Period::den
);
1553 std::locale locale_
;
1555 bool has_locale_
= false;
1558 get_locale(bool localized
, locale_ref loc
) : has_locale_(localized
) {
1560 ::new (&locale_
) std::locale(loc
.template get
<std::locale
>());
1563 if (has_locale_
) locale_
.~locale();
1565 operator const std::locale
&() const {
1566 return has_locale_
? locale_
: get_classic_locale();
1570 template <typename FormatContext
, typename OutputIt
, typename Rep
,
1572 struct chrono_formatter
{
1573 FormatContext
& context
;
1576 bool localized
= false;
1577 // rep is unsigned to avoid overflow.
1579 conditional_t
<std::is_integral
<Rep
>::value
&& sizeof(Rep
) < sizeof(int),
1580 unsigned, typename make_unsigned_or_unchanged
<Rep
>::type
>;
1582 using seconds
= std::chrono::duration
<rep
>;
1584 using milliseconds
= std::chrono::duration
<rep
, std::milli
>;
1587 using char_type
= typename
FormatContext::char_type
;
1588 using tm_writer_type
= tm_writer
<OutputIt
, char_type
>;
1590 chrono_formatter(FormatContext
& ctx
, OutputIt o
,
1591 std::chrono::duration
<Rep
, Period
> d
)
1594 val(static_cast<rep
>(d
.count())),
1596 if (d
.count() < 0) {
1601 // this may overflow and/or the result may not fit in the
1603 #if FMT_SAFE_DURATION_CAST
1604 // might need checked conversion (rep!=Rep)
1605 auto tmpval
= std::chrono::duration
<rep
, Period
>(val
);
1606 s
= fmt_safe_duration_cast
<seconds
>(tmpval
);
1608 s
= std::chrono::duration_cast
<seconds
>(
1609 std::chrono::duration
<rep
, Period
>(val
));
1613 // returns true if nan or inf, writes to out.
1614 bool handle_nan_inf() {
1615 if (isfinite(val
)) {
1631 Rep
hour() const { return static_cast<Rep
>(mod((s
.count() / 3600), 24)); }
1633 Rep
hour12() const {
1634 Rep hour
= static_cast<Rep
>(mod((s
.count() / 3600), 12));
1635 return hour
<= 0 ? 12 : hour
;
1638 Rep
minute() const { return static_cast<Rep
>(mod((s
.count() / 60), 60)); }
1639 Rep
second() const { return static_cast<Rep
>(mod(s
.count(), 60)); }
1641 std::tm
time() const {
1642 auto time
= std::tm();
1643 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1644 time
.tm_min
= to_nonnegative_int(minute(), 60);
1645 time
.tm_sec
= to_nonnegative_int(second(), 60);
1656 void write(Rep value
, int width
) {
1658 if (isnan(value
)) return write_nan();
1659 uint32_or_64_or_128_t
<int> n
=
1660 to_unsigned(to_nonnegative_int(value
, max_value
<int>()));
1661 int num_digits
= detail::count_digits(n
);
1662 if (width
> num_digits
) out
= std::fill_n(out
, width
- num_digits
, '0');
1663 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1666 template <class Duration
> void write_fractional_seconds(Duration d
) {
1667 constexpr auto num_fractional_digits
=
1668 count_fractional_digits(Duration::period::num
, Duration::period::den
);
1670 using subsecond_precision
= std::chrono::duration
<
1671 typename
std::common_type
<typename
Duration::rep
,
1672 std::chrono::seconds::rep
>::type
,
1673 std::ratio
<1, detail::pow10(num_fractional_digits
)>>;
1674 if (std::ratio_less
<typename
subsecond_precision::period
,
1675 std::chrono::seconds::period
>::value
) {
1677 // Don't convert long double to integer seconds to avoid overflow.
1678 using sec
= conditional_t
<
1679 std::is_same
<typename
Duration::rep
, long double>::value
,
1680 std::chrono::duration
<long double>, std::chrono::seconds
>;
1681 auto fractional
= detail::abs(d
) - std::chrono::duration_cast
<sec
>(d
);
1682 const auto subseconds
=
1683 std::chrono::treat_as_floating_point
<
1684 typename
subsecond_precision::rep
>::value
1685 ? fractional
.count()
1686 : std::chrono::duration_cast
<subsecond_precision
>(fractional
)
1688 uint32_or_64_or_128_t
<long long> n
=
1689 to_unsigned(to_nonnegative_int(subseconds
, max_value
<long long>()));
1690 int num_digits
= detail::count_digits(n
);
1691 if (num_fractional_digits
> num_digits
)
1692 out
= std::fill_n(out
, num_fractional_digits
- num_digits
, '0');
1693 out
= format_decimal
<char_type
>(out
, n
, num_digits
).end
;
1697 void write_nan() { std::copy_n("nan", 3, out
); }
1698 void write_pinf() { std::copy_n("inf", 3, out
); }
1699 void write_ninf() { std::copy_n("-inf", 4, out
); }
1701 template <typename Callback
, typename
... Args
>
1702 void format_tm(const tm
& time
, Callback cb
, Args
... args
) {
1703 if (isnan(val
)) return write_nan();
1704 get_locale
loc(localized
, context
.locale());
1705 auto w
= tm_writer_type(loc
, out
, time
);
1710 void on_text(const char_type
* begin
, const char_type
* end
) {
1711 std::copy(begin
, end
, out
);
1714 // These are not implemented because durations don't have date information.
1715 void on_abbr_weekday() {}
1716 void on_full_weekday() {}
1717 void on_dec0_weekday(numeric_system
) {}
1718 void on_dec1_weekday(numeric_system
) {}
1719 void on_abbr_month() {}
1720 void on_full_month() {}
1721 void on_datetime(numeric_system
) {}
1722 void on_loc_date(numeric_system
) {}
1723 void on_loc_time(numeric_system
) {}
1724 void on_us_date() {}
1725 void on_iso_date() {}
1726 void on_utc_offset() {}
1727 void on_tz_name() {}
1728 void on_year(numeric_system
) {}
1729 void on_short_year(numeric_system
) {}
1730 void on_offset_year() {}
1731 void on_century(numeric_system
) {}
1732 void on_iso_week_based_year() {}
1733 void on_iso_week_based_short_year() {}
1734 void on_dec_month(numeric_system
) {}
1735 void on_dec0_week_of_year(numeric_system
) {}
1736 void on_dec1_week_of_year(numeric_system
) {}
1737 void on_iso_week_of_year(numeric_system
) {}
1738 void on_day_of_year() {}
1739 void on_day_of_month(numeric_system
) {}
1740 void on_day_of_month_space(numeric_system
) {}
1742 void on_24_hour(numeric_system ns
) {
1743 if (handle_nan_inf()) return;
1745 if (ns
== numeric_system::standard
) return write(hour(), 2);
1747 time
.tm_hour
= to_nonnegative_int(hour(), 24);
1748 format_tm(time
, &tm_writer_type::on_24_hour
, ns
);
1751 void on_12_hour(numeric_system ns
) {
1752 if (handle_nan_inf()) return;
1754 if (ns
== numeric_system::standard
) return write(hour12(), 2);
1756 time
.tm_hour
= to_nonnegative_int(hour12(), 12);
1757 format_tm(time
, &tm_writer_type::on_12_hour
, ns
);
1760 void on_minute(numeric_system ns
) {
1761 if (handle_nan_inf()) return;
1763 if (ns
== numeric_system::standard
) return write(minute(), 2);
1765 time
.tm_min
= to_nonnegative_int(minute(), 60);
1766 format_tm(time
, &tm_writer_type::on_minute
, ns
);
1769 void on_second(numeric_system ns
) {
1770 if (handle_nan_inf()) return;
1772 if (ns
== numeric_system::standard
) {
1774 write_fractional_seconds(std::chrono::duration
<rep
, Period
>{val
});
1778 time
.tm_sec
= to_nonnegative_int(second(), 60);
1779 format_tm(time
, &tm_writer_type::on_second
, ns
);
1782 void on_12_hour_time() {
1783 if (handle_nan_inf()) return;
1784 format_tm(time(), &tm_writer_type::on_12_hour_time
);
1787 void on_24_hour_time() {
1788 if (handle_nan_inf()) {
1799 void on_iso_time() {
1802 if (handle_nan_inf()) return;
1803 on_second(numeric_system::standard
);
1807 if (handle_nan_inf()) return;
1808 format_tm(time(), &tm_writer_type::on_am_pm
);
1811 void on_duration_value() {
1812 if (handle_nan_inf()) return;
1814 out
= format_duration_value
<char_type
>(out
, val
, precision
);
1817 void on_duration_unit() {
1818 out
= format_duration_unit
<char_type
, Period
>(out
);
1822 FMT_END_DETAIL_NAMESPACE
1824 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1825 using weekday
= std::chrono::weekday
;
1827 // A fallback version of weekday.
1830 unsigned char value
;
1833 weekday() = default;
1834 explicit constexpr weekday(unsigned wd
) noexcept
1835 : value(static_cast<unsigned char>(wd
!= 7 ? wd
: 0)) {}
1836 constexpr unsigned c_encoding() const noexcept
{ return value
; }
1839 class year_month_day
{};
1842 // A rudimentary weekday formatter.
1843 template <typename Char
> struct formatter
<weekday
, Char
> {
1845 bool localized
= false;
1848 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
1849 -> decltype(ctx
.begin()) {
1850 auto begin
= ctx
.begin(), end
= ctx
.end();
1851 if (begin
!= end
&& *begin
== 'L') {
1858 template <typename FormatContext
>
1859 auto format(weekday wd
, FormatContext
& ctx
) const -> decltype(ctx
.out()) {
1860 auto time
= std::tm();
1861 time
.tm_wday
= static_cast<int>(wd
.c_encoding());
1862 detail::get_locale
loc(localized
, ctx
.locale());
1863 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), time
);
1864 w
.on_abbr_weekday();
1869 template <typename Rep
, typename Period
, typename Char
>
1870 struct formatter
<std::chrono::duration
<Rep
, Period
>, Char
> {
1872 basic_format_specs
<Char
> specs
;
1874 using arg_ref_type
= detail::arg_ref
<Char
>;
1875 arg_ref_type width_ref
;
1876 arg_ref_type precision_ref
;
1877 bool localized
= false;
1878 basic_string_view
<Char
> format_str
;
1879 using duration
= std::chrono::duration
<Rep
, Period
>;
1881 struct spec_handler
{
1883 basic_format_parse_context
<Char
>& context
;
1884 basic_string_view
<Char
> format_str
;
1886 template <typename Id
> FMT_CONSTEXPR arg_ref_type
make_arg_ref(Id arg_id
) {
1887 context
.check_arg_id(arg_id
);
1888 return arg_ref_type(arg_id
);
1891 FMT_CONSTEXPR arg_ref_type
make_arg_ref(basic_string_view
<Char
> arg_id
) {
1892 context
.check_arg_id(arg_id
);
1893 return arg_ref_type(arg_id
);
1896 FMT_CONSTEXPR arg_ref_type
make_arg_ref(detail::auto_id
) {
1897 return arg_ref_type(context
.next_arg_id());
1900 void on_error(const char* msg
) { FMT_THROW(format_error(msg
)); }
1901 FMT_CONSTEXPR
void on_fill(basic_string_view
<Char
> fill
) {
1902 f
.specs
.fill
= fill
;
1904 FMT_CONSTEXPR
void on_align(align_t align
) { f
.specs
.align
= align
; }
1905 FMT_CONSTEXPR
void on_width(int width
) { f
.specs
.width
= width
; }
1906 FMT_CONSTEXPR
void on_precision(int _precision
) {
1907 f
.precision
= _precision
;
1909 FMT_CONSTEXPR
void end_precision() {}
1911 template <typename Id
> FMT_CONSTEXPR
void on_dynamic_width(Id arg_id
) {
1912 f
.width_ref
= make_arg_ref(arg_id
);
1915 template <typename Id
> FMT_CONSTEXPR
void on_dynamic_precision(Id arg_id
) {
1916 f
.precision_ref
= make_arg_ref(arg_id
);
1920 using iterator
= typename basic_format_parse_context
<Char
>::iterator
;
1921 struct parse_range
{
1926 FMT_CONSTEXPR parse_range
do_parse(basic_format_parse_context
<Char
>& ctx
) {
1927 auto begin
= ctx
.begin(), end
= ctx
.end();
1928 if (begin
== end
|| *begin
== '}') return {begin
, begin
};
1929 spec_handler handler
{*this, ctx
, format_str
};
1930 begin
= detail::parse_align(begin
, end
, handler
);
1931 if (begin
== end
) return {begin
, begin
};
1932 begin
= detail::parse_width(begin
, end
, handler
);
1933 if (begin
== end
) return {begin
, begin
};
1934 if (*begin
== '.') {
1935 if (std::is_floating_point
<Rep
>::value
)
1936 begin
= detail::parse_precision(begin
, end
, handler
);
1938 handler
.on_error("precision not allowed for this argument type");
1940 if (begin
!= end
&& *begin
== 'L') {
1944 end
= detail::parse_chrono_format(begin
, end
,
1945 detail::chrono_format_checker());
1946 return {begin
, end
};
1950 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
1951 -> decltype(ctx
.begin()) {
1952 auto range
= do_parse(ctx
);
1953 format_str
= basic_string_view
<Char
>(
1954 &*range
.begin
, detail::to_unsigned(range
.end
- range
.begin
));
1958 template <typename FormatContext
>
1959 auto format(const duration
& d
, FormatContext
& ctx
) const
1960 -> decltype(ctx
.out()) {
1961 auto specs_copy
= specs
;
1962 auto precision_copy
= precision
;
1963 auto begin
= format_str
.begin(), end
= format_str
.end();
1964 // As a possible future optimization, we could avoid extra copying if width
1965 // is not specified.
1966 basic_memory_buffer
<Char
> buf
;
1967 auto out
= std::back_inserter(buf
);
1968 detail::handle_dynamic_spec
<detail::width_checker
>(specs_copy
.width
,
1970 detail::handle_dynamic_spec
<detail::precision_checker
>(precision_copy
,
1971 precision_ref
, ctx
);
1972 if (begin
== end
|| *begin
== '}') {
1973 out
= detail::format_duration_value
<Char
>(out
, d
.count(), precision_copy
);
1974 detail::format_duration_unit
<Char
, Period
>(out
);
1976 detail::chrono_formatter
<FormatContext
, decltype(out
), Rep
, Period
> f(
1978 f
.precision
= precision_copy
;
1979 f
.localized
= localized
;
1980 detail::parse_chrono_format(begin
, end
, f
);
1982 return detail::write(
1983 ctx
.out(), basic_string_view
<Char
>(buf
.data(), buf
.size()), specs_copy
);
1987 template <typename Char
, typename Duration
>
1988 struct formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
1989 Char
> : formatter
<std::tm
, Char
> {
1990 FMT_CONSTEXPR
formatter() {
1991 this->do_parse(default_specs
,
1992 default_specs
+ sizeof(default_specs
) / sizeof(Char
));
1995 template <typename ParseContext
>
1996 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
1997 return this->do_parse(ctx
.begin(), ctx
.end(), true);
2000 template <typename FormatContext
>
2001 auto format(std::chrono::time_point
<std::chrono::system_clock
> val
,
2002 FormatContext
& ctx
) const -> decltype(ctx
.out()) {
2003 return formatter
<std::tm
, Char
>::format(localtime(val
), ctx
);
2006 static constexpr const Char default_specs
[] = {'%', 'F', ' ', '%', 'T'};
2009 template <typename Char
, typename Duration
>
2010 constexpr const Char
2011 formatter
<std::chrono::time_point
<std::chrono::system_clock
, Duration
>,
2012 Char
>::default_specs
[];
2014 template <typename Char
> struct formatter
<std::tm
, Char
> {
2021 spec spec_
= spec::unknown
;
2022 basic_string_view
<Char
> specs
;
2025 template <typename It
>
2026 FMT_CONSTEXPR
auto do_parse(It begin
, It end
, bool with_default
= false)
2028 if (begin
!= end
&& *begin
== ':') ++begin
;
2029 end
= detail::parse_chrono_format(begin
, end
, detail::tm_format_checker());
2030 if (!with_default
|| end
!= begin
)
2031 specs
= {begin
, detail::to_unsigned(end
- begin
)};
2032 // basic_string_view<>::compare isn't constexpr before C++17.
2033 if (specs
.size() == 2 && specs
[0] == Char('%')) {
2034 if (specs
[1] == Char('F'))
2035 spec_
= spec::year_month_day
;
2036 else if (specs
[1] == Char('T'))
2037 spec_
= spec::hh_mm_ss
;
2043 template <typename ParseContext
>
2044 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
2045 return this->do_parse(ctx
.begin(), ctx
.end());
2048 template <typename FormatContext
>
2049 auto format(const std::tm
& tm
, FormatContext
& ctx
) const
2050 -> decltype(ctx
.out()) {
2051 const auto loc_ref
= ctx
.locale();
2052 detail::get_locale
loc(static_cast<bool>(loc_ref
), loc_ref
);
2053 auto w
= detail::tm_writer
<decltype(ctx
.out()), Char
>(loc
, ctx
.out(), tm
);
2054 if (spec_
== spec::year_month_day
)
2056 else if (spec_
== spec::hh_mm_ss
)
2059 detail::parse_chrono_format(specs
.begin(), specs
.end(), w
);
2064 FMT_MODULE_EXPORT_END
2067 #endif // FMT_CHRONO_H_