cpp-common/bt2c/fmt.hpp: use `wise_enum::string_type` in `EnableIfIsWiseEnum` definition
[babeltrace.git] / src / cpp-common / vendor / fmt / chrono.h
1 // Formatting library for C++ - chrono support
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7
8 #ifndef FMT_CHRONO_H_
9 #define FMT_CHRONO_H_
10
11 #include <algorithm>
12 #include <chrono>
13 #include <cmath> // std::isfinite
14 #include <cstring> // std::memcpy
15 #include <ctime>
16 #include <iterator>
17 #include <locale>
18 #include <ostream>
19 #include <type_traits>
20
21 #include "format.h"
22
23 FMT_BEGIN_NAMESPACE
24
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)
29 # else
30 # define FMT_USE_LOCAL_TIME 0
31 # endif
32 #endif
33
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)
38 # else
39 # define FMT_USE_UTC_TIME 0
40 # endif
41 #endif
42
43 // Enable tzset.
44 #ifndef FMT_USE_TZSET
45 // UWP doesn't provide _tzset.
46 # if FMT_HAS_INCLUDE("winapifamily.h")
47 # include <winapifamily.h>
48 # endif
49 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
50 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
51 # define FMT_USE_TZSET 1
52 # else
53 # define FMT_USE_TZSET 0
54 # endif
55 #endif
56
57 // Enable safe chrono durations, unless explicitly disabled.
58 #ifndef FMT_SAFE_DURATION_CAST
59 # define FMT_SAFE_DURATION_CAST 1
60 #endif
61 #if FMT_SAFE_DURATION_CAST
62
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
67 //
68 // Copyright Paul Dreik 2019
69 namespace safe_duration_cast {
70
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) {
76 ec = 0;
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");
81
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.
85 } else {
86 // From does not always fit in To, resort to a dynamic check.
87 if (from < (T::min)() || from > (T::max)()) {
88 // outside range.
89 ec = 1;
90 return {};
91 }
92 }
93 return static_cast<To>(from);
94 }
95
96 /**
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.
99 */
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) {
105 ec = 0;
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");
110
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)) {
114 ec = 1;
115 return {};
116 }
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>())) {
120 ec = 1;
121 return {};
122 }
123 }
124
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>())) {
128 ec = 1;
129 return {};
130 }
131 return static_cast<To>(from); // Lossless conversion.
132 }
133
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) {
137 ec = 0;
138 return from;
139 } // function
140
141 // clang-format off
142 /**
143 * converts From to To if possible, otherwise ec is set.
144 *
145 * input | output
146 * ---------------------------------|---------------
147 * NaN | NaN
148 * Inf | Inf
149 * normal, fits in output | converted (possibly lossy)
150 * normal, does not fit in output | ec is set
151 * subnormal | best effort
152 * -Inf | -Inf
153 */
154 // clang-format on
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) {
158 ec = 0;
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");
162
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);
167 }
168 // not within range.
169 ec = 1;
170 return {};
171 }
172
173 // nan and inf will be preserved
174 return static_cast<To>(from);
175 } // function
176
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) {
180 ec = 0;
181 static_assert(std::is_floating_point<From>::value, "From must be floating");
182 return from;
183 }
184
185 /**
186 * safe duration cast between integral durations
187 */
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,
192 int& ec) {
193 using From = std::chrono::duration<FromRep, FromPeriod>;
194 ec = 0;
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:
197 struct Factor
198 : std::ratio_divide<typename From::period, typename To::period> {};
199
200 static_assert(Factor::num > 0, "num must be positive");
201 static_assert(Factor::den > 0, "den must be positive");
202
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;
210
211 // safe conversion to IntermediateRep
212 IntermediateRep count =
213 lossless_integral_conversion<IntermediateRep>(from.count(), ec);
214 if (ec) return {};
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;
218 if (count > max1) {
219 ec = 1;
220 return {};
221 }
222 const auto min1 =
223 (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
224 if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
225 count < min1) {
226 ec = 1;
227 return {};
228 }
229 count *= Factor::num;
230 }
231
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);
235 }
236
237 /**
238 * safe duration_cast between floating point durations
239 */
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,
244 int& ec) {
245 using From = std::chrono::duration<FromRep, FromPeriod>;
246 ec = 0;
247 if (std::isnan(from.count())) {
248 // nan in, gives nan out. easy.
249 return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
250 }
251 // maybe we should also check if from is denormal, and decide what to do about
252 // it.
253
254 // +-inf should be preserved.
255 if (std::isinf(from.count())) {
256 return To{from.count()};
257 }
258
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:
261 struct Factor
262 : std::ratio_divide<typename From::period, typename To::period> {};
263
264 static_assert(Factor::num > 0, "num must be positive");
265 static_assert(Factor::den > 0, "den must be positive");
266
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;
274
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);
279 if (ec) {
280 return {};
281 }
282
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);
287 if (count > max1) {
288 ec = 1;
289 return {};
290 }
291 constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
292 static_cast<IntermediateRep>(Factor::num);
293 if (count < min1) {
294 ec = 1;
295 return {};
296 }
297 count *= static_cast<IntermediateRep>(Factor::num);
298 }
299
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);
304 }
305
306 // convert to the to type, safely
307 using ToRep = typename To::rep;
308
309 const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
310 if (ec) {
311 return {};
312 }
313 return To{tocount};
314 }
315 } // namespace safe_duration_cast
316 #endif
317
318 // Prevents expansion of a preceding token as a function-style macro.
319 // Usage: f FMT_NOMACRO()
320 #define FMT_NOMACRO
321
322 namespace detail {
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<>(); }
328
329 inline const std::locale& get_classic_locale() {
330 static const auto& locale = std::locale::classic();
331 return locale;
332 }
333
334 template <typename CodeUnit> struct codecvt_result {
335 static constexpr const size_t max_size = 32;
336 CodeUnit buf[max_size];
337 CodeUnit* end;
338 };
339 template <typename CodeUnit>
340 constexpr const size_t codecvt_result<CodeUnit>::max_size;
341
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
350 #else
351 auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
352 #endif
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"));
359 }
360
361 template <typename OutputIt>
362 auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
363 -> OutputIt {
364 if (detail::is_utf8() && loc != get_classic_locale()) {
365 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
366 // gcc-4.
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
370 // and newer.
371 using code_unit = wchar_t;
372 #else
373 using code_unit = char32_t;
374 #endif
375
376 using unit_t = codecvt_result<code_unit>;
377 unit_t unit;
378 write_codecvt(unit, in, loc);
379 // In UTF-8 is used one to four one-byte code units.
380 auto u =
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);
385 }
386 return copy_str<char>(in.data(), in.data() + in.size(), out);
387 }
388
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)
392 -> OutputIt {
393 codecvt_result<Char> unit;
394 write_codecvt(unit, sv, loc);
395 return copy_str<Char>(unit.buf, unit.end, out);
396 }
397
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)
401 -> OutputIt {
402 return write_encoded_tm_str(out, sv, loc);
403 }
404
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);
410 os.imbue(loc);
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"));
415 }
416
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);
424 }
425
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);
433 }
434
435 } // namespace detail
436
437 FMT_BEGIN_EXPORT
438
439 /**
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.
443 */
444 inline std::tm localtime(std::time_t time) {
445 struct dispatcher {
446 std::time_t time_;
447 std::tm tm_;
448
449 dispatcher(std::time_t t) : time_(t) {}
450
451 bool run() {
452 using namespace fmt::detail;
453 return handle(localtime_r(&time_, &tm_));
454 }
455
456 bool handle(std::tm* tm) { return tm != nullptr; }
457
458 bool handle(detail::null<>) {
459 using namespace fmt::detail;
460 return fallback(localtime_s(&tm_, &time_));
461 }
462
463 bool fallback(int res) { return res == 0; }
464
465 #if !FMT_MSC_VERSION
466 bool fallback(detail::null<>) {
467 using namespace fmt::detail;
468 std::tm* tm = std::localtime(&time_);
469 if (tm) tm_ = *tm;
470 return tm != nullptr;
471 }
472 #endif
473 };
474 dispatcher lt(time);
475 // Too big time values may be unsupported.
476 if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
477 return lt.tm_;
478 }
479
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)));
485 }
486 #endif
487
488 /**
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.
492 */
493 inline std::tm gmtime(std::time_t time) {
494 struct dispatcher {
495 std::time_t time_;
496 std::tm tm_;
497
498 dispatcher(std::time_t t) : time_(t) {}
499
500 bool run() {
501 using namespace fmt::detail;
502 return handle(gmtime_r(&time_, &tm_));
503 }
504
505 bool handle(std::tm* tm) { return tm != nullptr; }
506
507 bool handle(detail::null<>) {
508 using namespace fmt::detail;
509 return fallback(gmtime_s(&tm_, &time_));
510 }
511
512 bool fallback(int res) { return res == 0; }
513
514 #if !FMT_MSC_VERSION
515 bool fallback(detail::null<>) {
516 std::tm* tm = std::gmtime(&time_);
517 if (tm) tm_ = *tm;
518 return tm != nullptr;
519 }
520 #endif
521 };
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"));
525 return gt.tm_;
526 }
527
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));
531 }
532
533 namespace detail {
534
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.
544 // The difference is
545 // y - x = a * 6
546 // a can be found from x:
547 // a = floor(x / 10)
548 // then
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);
558
559 constexpr const size_t len = 8;
560 if (const_check(is_big_endian())) {
561 char tmp[len];
562 std::memcpy(tmp, &digits, len);
563 std::reverse_copy(tmp, tmp + len, buf);
564 } else {
565 std::memcpy(buf, &digits, len);
566 }
567 }
568
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";
589 return nullptr;
590 }
591
592 enum class numeric_system {
593 standard,
594 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
595 alternative
596 };
597
598 // Glibc extensions for formatting numeric values.
599 enum class pad_type {
600 unspecified,
601 // Do not pad a numeric result string.
602 none,
603 // Pad a numeric result string with zeros even if the conversion specifier
604 // character uses space-padding by default.
605 zero,
606 // Pad a numeric result string with spaces.
607 space,
608 };
609
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');
614 }
615
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';
619 return out;
620 }
621
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,
625 const Char* end,
626 Handler&& handler) {
627 if (begin == end || *begin == '}') return begin;
628 if (*begin != '%') FMT_THROW(format_error("invalid format"));
629 auto ptr = begin;
630 pad_type pad = pad_type::unspecified;
631 while (ptr != end) {
632 auto c = *ptr;
633 if (c == '}') break;
634 if (c != '%') {
635 ++ptr;
636 continue;
637 }
638 if (begin != ptr) handler.on_text(begin, ptr);
639 ++ptr; // consume '%'
640 if (ptr == end) FMT_THROW(format_error("invalid format"));
641 c = *ptr;
642 switch (c) {
643 case '_':
644 pad = pad_type::space;
645 ++ptr;
646 break;
647 case '-':
648 pad = pad_type::none;
649 ++ptr;
650 break;
651 case '0':
652 pad = pad_type::zero;
653 ++ptr;
654 break;
655 }
656 if (ptr == end) FMT_THROW(format_error("invalid format"));
657 c = *ptr++;
658 switch (c) {
659 case '%':
660 handler.on_text(ptr - 1, ptr);
661 break;
662 case 'n': {
663 const Char newline[] = {'\n'};
664 handler.on_text(newline, newline + 1);
665 break;
666 }
667 case 't': {
668 const Char tab[] = {'\t'};
669 handler.on_text(tab, tab + 1);
670 break;
671 }
672 // Year:
673 case 'Y':
674 handler.on_year(numeric_system::standard);
675 break;
676 case 'y':
677 handler.on_short_year(numeric_system::standard);
678 break;
679 case 'C':
680 handler.on_century(numeric_system::standard);
681 break;
682 case 'G':
683 handler.on_iso_week_based_year();
684 break;
685 case 'g':
686 handler.on_iso_week_based_short_year();
687 break;
688 // Day of the week:
689 case 'a':
690 handler.on_abbr_weekday();
691 break;
692 case 'A':
693 handler.on_full_weekday();
694 break;
695 case 'w':
696 handler.on_dec0_weekday(numeric_system::standard);
697 break;
698 case 'u':
699 handler.on_dec1_weekday(numeric_system::standard);
700 break;
701 // Month:
702 case 'b':
703 case 'h':
704 handler.on_abbr_month();
705 break;
706 case 'B':
707 handler.on_full_month();
708 break;
709 case 'm':
710 handler.on_dec_month(numeric_system::standard);
711 break;
712 // Day of the year/month:
713 case 'U':
714 handler.on_dec0_week_of_year(numeric_system::standard);
715 break;
716 case 'W':
717 handler.on_dec1_week_of_year(numeric_system::standard);
718 break;
719 case 'V':
720 handler.on_iso_week_of_year(numeric_system::standard);
721 break;
722 case 'j':
723 handler.on_day_of_year();
724 break;
725 case 'd':
726 handler.on_day_of_month(numeric_system::standard);
727 break;
728 case 'e':
729 handler.on_day_of_month_space(numeric_system::standard);
730 break;
731 // Hour, minute, second:
732 case 'H':
733 handler.on_24_hour(numeric_system::standard, pad);
734 break;
735 case 'I':
736 handler.on_12_hour(numeric_system::standard, pad);
737 break;
738 case 'M':
739 handler.on_minute(numeric_system::standard, pad);
740 break;
741 case 'S':
742 handler.on_second(numeric_system::standard, pad);
743 break;
744 // Other:
745 case 'c':
746 handler.on_datetime(numeric_system::standard);
747 break;
748 case 'x':
749 handler.on_loc_date(numeric_system::standard);
750 break;
751 case 'X':
752 handler.on_loc_time(numeric_system::standard);
753 break;
754 case 'D':
755 handler.on_us_date();
756 break;
757 case 'F':
758 handler.on_iso_date();
759 break;
760 case 'r':
761 handler.on_12_hour_time();
762 break;
763 case 'R':
764 handler.on_24_hour_time();
765 break;
766 case 'T':
767 handler.on_iso_time();
768 break;
769 case 'p':
770 handler.on_am_pm();
771 break;
772 case 'Q':
773 handler.on_duration_value();
774 break;
775 case 'q':
776 handler.on_duration_unit();
777 break;
778 case 'z':
779 handler.on_utc_offset(numeric_system::standard);
780 break;
781 case 'Z':
782 handler.on_tz_name();
783 break;
784 // Alternative representation:
785 case 'E': {
786 if (ptr == end) FMT_THROW(format_error("invalid format"));
787 c = *ptr++;
788 switch (c) {
789 case 'Y':
790 handler.on_year(numeric_system::alternative);
791 break;
792 case 'y':
793 handler.on_offset_year();
794 break;
795 case 'C':
796 handler.on_century(numeric_system::alternative);
797 break;
798 case 'c':
799 handler.on_datetime(numeric_system::alternative);
800 break;
801 case 'x':
802 handler.on_loc_date(numeric_system::alternative);
803 break;
804 case 'X':
805 handler.on_loc_time(numeric_system::alternative);
806 break;
807 case 'z':
808 handler.on_utc_offset(numeric_system::alternative);
809 break;
810 default:
811 FMT_THROW(format_error("invalid format"));
812 }
813 break;
814 }
815 case 'O':
816 if (ptr == end) FMT_THROW(format_error("invalid format"));
817 c = *ptr++;
818 switch (c) {
819 case 'y':
820 handler.on_short_year(numeric_system::alternative);
821 break;
822 case 'm':
823 handler.on_dec_month(numeric_system::alternative);
824 break;
825 case 'U':
826 handler.on_dec0_week_of_year(numeric_system::alternative);
827 break;
828 case 'W':
829 handler.on_dec1_week_of_year(numeric_system::alternative);
830 break;
831 case 'V':
832 handler.on_iso_week_of_year(numeric_system::alternative);
833 break;
834 case 'd':
835 handler.on_day_of_month(numeric_system::alternative);
836 break;
837 case 'e':
838 handler.on_day_of_month_space(numeric_system::alternative);
839 break;
840 case 'w':
841 handler.on_dec0_weekday(numeric_system::alternative);
842 break;
843 case 'u':
844 handler.on_dec1_weekday(numeric_system::alternative);
845 break;
846 case 'H':
847 handler.on_24_hour(numeric_system::alternative, pad);
848 break;
849 case 'I':
850 handler.on_12_hour(numeric_system::alternative, pad);
851 break;
852 case 'M':
853 handler.on_minute(numeric_system::alternative, pad);
854 break;
855 case 'S':
856 handler.on_second(numeric_system::alternative, pad);
857 break;
858 case 'z':
859 handler.on_utc_offset(numeric_system::alternative);
860 break;
861 default:
862 FMT_THROW(format_error("invalid format"));
863 }
864 break;
865 default:
866 FMT_THROW(format_error("invalid format"));
867 }
868 begin = ptr;
869 }
870 if (begin != ptr) handler.on_text(begin, ptr);
871 return ptr;
872 }
873
874 template <typename Derived> struct null_chrono_spec_handler {
875 FMT_CONSTEXPR void unsupported() {
876 static_cast<Derived*>(this)->unsupported();
877 }
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(); }
914 };
915
916 struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
917 FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
918
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() {}
955 };
956
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] : "?";
962 }
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] : "???";
967 }
968
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] : "?";
974 }
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",
979 };
980 return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
981 }
982
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)>>
987 : std::true_type {};
988
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)>>
993 : std::true_type {};
994
995 #if FMT_USE_TZSET
996 inline void tzset_once() {
997 static bool init = []() -> bool {
998 _tzset();
999 return true;
1000 }();
1001 ignore_unused(init);
1002 }
1003 #endif
1004
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)),
1010 "invalid value");
1011 (void)upper;
1012 return static_cast<Int>(value);
1013 }
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);
1019 }
1020
1021 constexpr long long pow10(std::uint32_t n) {
1022 return n == 0 ? 1 : 10 * pow10(n - 1);
1023 }
1024
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;
1033 };
1034
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;
1040 };
1041
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;
1049
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)>>;
1054
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);
1064
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) {
1070 *out++ = '.';
1071 out = std::fill_n(out, leading_zeroes, '0');
1072 out = format_decimal<Char>(out, n, num_digits).end;
1073 }
1074 } else {
1075 *out++ = '.';
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;
1082 return;
1083 }
1084 out = format_decimal<Char>(out, n, num_digits).end;
1085 remaining -= num_digits;
1086 out = std::fill_n(out, remaining, '0');
1087 }
1088 }
1089
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, "");
1098
1099 auto val = duration.count();
1100
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;
1110 }
1111
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);
1117 }
1118
1119 template <typename OutputIt, typename Char,
1120 typename Duration = std::chrono::seconds>
1121 class tm_writer {
1122 private:
1123 static constexpr int days_per_week = 7;
1124
1125 const std::locale& loc_;
1126 const bool is_classic_;
1127 OutputIt out_;
1128 const Duration* subsecs_;
1129 const std::tm& tm_;
1130
1131 auto tm_sec() const noexcept -> int {
1132 FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
1133 return tm_.tm_sec;
1134 }
1135 auto tm_min() const noexcept -> int {
1136 FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
1137 return tm_.tm_min;
1138 }
1139 auto tm_hour() const noexcept -> int {
1140 FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
1141 return tm_.tm_hour;
1142 }
1143 auto tm_mday() const noexcept -> int {
1144 FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
1145 return tm_.tm_mday;
1146 }
1147 auto tm_mon() const noexcept -> int {
1148 FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
1149 return tm_.tm_mon;
1150 }
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, "");
1154 return tm_.tm_wday;
1155 }
1156 auto tm_yday() const noexcept -> int {
1157 FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
1158 return tm_.tm_yday;
1159 }
1160
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;
1165 }
1166
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);
1175 }
1176
1177 // Algorithm:
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;
1181 const auto curr_p =
1182 (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
1183 days_per_week;
1184 const auto prev_p =
1185 (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
1186 days_per_week;
1187 return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
1188 }
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)) /
1191 days_per_week;
1192 }
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;
1198 return year;
1199 }
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;
1205 return w;
1206 }
1207
1208 void write1(int value) {
1209 *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
1210 }
1211 void write2(int value) {
1212 const char* d = digits2(to_unsigned(value) % 100);
1213 *out_++ = *d++;
1214 *out_++ = *d;
1215 }
1216 void write2(int value, pad_type pad) {
1217 unsigned int v = to_unsigned(value) % 100;
1218 if (v >= 10) {
1219 const char* d = digits2(v);
1220 *out_++ = *d++;
1221 *out_++ = *d;
1222 } else {
1223 out_ = detail::write_padding(out_, pad);
1224 *out_++ = static_cast<char>('0' + v);
1225 }
1226 }
1227
1228 void write_year_extended(long long year) {
1229 // At least 4 characters.
1230 int width = 4;
1231 if (year < 0) {
1232 *out_++ = '-';
1233 year = 0 - year;
1234 --width;
1235 }
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;
1240 }
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));
1245 } else {
1246 write_year_extended(year);
1247 }
1248 }
1249
1250 void write_utc_offset(long offset, numeric_system ns) {
1251 if (offset < 0) {
1252 *out_++ = '-';
1253 offset = -offset;
1254 } else {
1255 *out_++ = '+';
1256 }
1257 offset /= 60;
1258 write2(static_cast<int>(offset / 60));
1259 if (ns != numeric_system::standard) *out_++ = ':';
1260 write2(static_cast<int>(offset % 60));
1261 }
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);
1265 }
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)
1269 # if FMT_USE_TZSET
1270 tzset_once();
1271 # endif
1272 long offset = 0;
1273 _get_timezone(&offset);
1274 if (tm.tm_isdst) {
1275 long dstbias = 0;
1276 _get_dstbias(&dstbias);
1277 offset += dstbias;
1278 }
1279 write_utc_offset(-offset, ns);
1280 #else
1281 if (ns == numeric_system::standard) return format_localized('z');
1282
1283 // Extract timezone offset from timezone conversion functions.
1284 std::tm gtm = tm;
1285 std::time_t gt = std::mktime(&gtm);
1286 std::tm ltm = gmtime(gt);
1287 std::time_t lt = std::mktime(&ltm);
1288 long offset = gt - lt;
1289 write_utc_offset(offset, ns);
1290 #endif
1291 }
1292
1293 template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
1294 void format_tz_name_impl(const T& tm) {
1295 if (is_classic_)
1296 out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
1297 else
1298 format_localized('Z');
1299 }
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');
1303 }
1304
1305 void format_localized(char format, char modifier = 0) {
1306 out_ = write<Char>(out_, tm_, loc_, format, modifier);
1307 }
1308
1309 public:
1310 tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
1311 const Duration* subsecs = nullptr)
1312 : loc_(loc),
1313 is_classic_(loc_ == get_classic_locale()),
1314 out_(out),
1315 subsecs_(subsecs),
1316 tm_(tm) {}
1317
1318 OutputIt out() const { return out_; }
1319
1320 FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
1321 out_ = copy_str<Char>(begin, end, out_);
1322 }
1323
1324 void on_abbr_weekday() {
1325 if (is_classic_)
1326 out_ = write(out_, tm_wday_short_name(tm_wday()));
1327 else
1328 format_localized('a');
1329 }
1330 void on_full_weekday() {
1331 if (is_classic_)
1332 out_ = write(out_, tm_wday_full_name(tm_wday()));
1333 else
1334 format_localized('A');
1335 }
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');
1339 }
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);
1344 } else {
1345 format_localized('u', 'O');
1346 }
1347 }
1348
1349 void on_abbr_month() {
1350 if (is_classic_)
1351 out_ = write(out_, tm_mon_short_name(tm_mon()));
1352 else
1353 format_localized('b');
1354 }
1355 void on_full_month() {
1356 if (is_classic_)
1357 out_ = write(out_, tm_mon_full_name(tm_mon()));
1358 else
1359 format_localized('B');
1360 }
1361
1362 void on_datetime(numeric_system ns) {
1363 if (is_classic_) {
1364 on_abbr_weekday();
1365 *out_++ = ' ';
1366 on_abbr_month();
1367 *out_++ = ' ';
1368 on_day_of_month_space(numeric_system::standard);
1369 *out_++ = ' ';
1370 on_iso_time();
1371 *out_++ = ' ';
1372 on_year(numeric_system::standard);
1373 } else {
1374 format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
1375 }
1376 }
1377 void on_loc_date(numeric_system ns) {
1378 if (is_classic_)
1379 on_us_date();
1380 else
1381 format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
1382 }
1383 void on_loc_time(numeric_system ns) {
1384 if (is_classic_)
1385 on_iso_time();
1386 else
1387 format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
1388 }
1389 void on_us_date() {
1390 char buf[8];
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_);
1395 }
1396 void on_iso_date() {
1397 auto year = tm_year();
1398 char buf[10];
1399 size_t offset = 0;
1400 if (year >= 0 && year < 10000) {
1401 copy2(buf, digits2(static_cast<size_t>(year / 100)));
1402 } else {
1403 offset = 4;
1404 write_year_extended(year);
1405 year = 0;
1406 }
1407 write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
1408 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1409 '-');
1410 out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
1411 }
1412
1413 void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
1414 void on_tz_name() { format_tz_name_impl(tm_); }
1415
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');
1420 }
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');
1425 }
1426 void on_offset_year() {
1427 if (is_classic_) return write2(split_year_lower(tm_year()));
1428 format_localized('y', 'E');
1429 }
1430
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.
1437 *out_++ = '-';
1438 *out_++ = '0';
1439 } else if (upper >= 0 && upper < 100) {
1440 write2(static_cast<int>(upper));
1441 } else {
1442 out_ = write<Char>(out_, upper);
1443 }
1444 } else {
1445 format_localized('C', 'E');
1446 }
1447 }
1448
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');
1453 }
1454
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');
1459 }
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))) /
1465 days_per_week);
1466 } else {
1467 format_localized('W', 'O');
1468 }
1469 }
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');
1474 }
1475
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()));
1479 }
1480
1481 void on_day_of_year() {
1482 auto yday = tm_yday() + 1;
1483 write1(yday / 100);
1484 write2(yday % 100);
1485 }
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');
1489 }
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];
1495 *out_++ = d2[1];
1496 } else {
1497 format_localized('e', 'O');
1498 }
1499 }
1500
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');
1505 }
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');
1510 }
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');
1515 }
1516
1517 void on_second(numeric_system ns, pad_type pad) {
1518 if (is_classic_ || ns == numeric_system::standard) {
1519 write2(tm_sec(), pad);
1520 if (subsecs_) {
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_);
1527 }
1528 } else {
1529 write_fractional_seconds<Char>(out_, *subsecs_);
1530 }
1531 }
1532 } else {
1533 // Currently no formatting of subseconds when a locale is set.
1534 format_localized('S', 'O');
1535 }
1536 }
1537
1538 void on_12_hour_time() {
1539 if (is_classic_) {
1540 char buf[8];
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_);
1544 *out_++ = ' ';
1545 on_am_pm();
1546 } else {
1547 format_localized('r');
1548 }
1549 }
1550 void on_24_hour_time() {
1551 write2(tm_hour());
1552 *out_++ = ':';
1553 write2(tm_min());
1554 }
1555 void on_iso_time() {
1556 on_24_hour_time();
1557 *out_++ = ':';
1558 on_second(numeric_system::standard, pad_type::unspecified);
1559 }
1560
1561 void on_am_pm() {
1562 if (is_classic_) {
1563 *out_++ = tm_hour() < 12 ? 'A' : 'P';
1564 *out_++ = 'M';
1565 } else {
1566 format_localized('p');
1567 }
1568 }
1569
1570 // These apply to chrono durations but not tm.
1571 void on_duration_value() {}
1572 void on_duration_unit() {}
1573 };
1574
1575 struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
1576 bool has_precision_integral = false;
1577
1578 FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
1579
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"));
1593 }
1594 }
1595 FMT_CONSTEXPR void on_duration_unit() {}
1596 };
1597
1598 template <typename T,
1599 FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
1600 inline bool isfinite(T) {
1601 return true;
1602 }
1603
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);
1607 }
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));
1611 }
1612
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 {
1617 using type = T;
1618 };
1619
1620 template <typename T> struct make_unsigned_or_unchanged<T, true> {
1621 using type = typename std::make_unsigned<T>::type;
1622 };
1623
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) {
1628 int ec;
1629 To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
1630 if (ec) FMT_THROW(format_error("cannot format duration"));
1631 return to;
1632 }
1633 #endif
1634
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
1640 // target type.
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;
1649 const auto ms =
1650 fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
1651 return ms;
1652 #else
1653 auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
1654 return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
1655 #endif
1656 }
1657
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);
1662 }
1663
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);
1672 }
1673
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);
1677 }
1678
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);
1685 }
1686
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());
1691 *out++ = '[';
1692 out = write<Char>(out, Period::num);
1693 if (const_check(Period::den != 1)) {
1694 *out++ = '/';
1695 out = write<Char>(out, Period::den);
1696 }
1697 *out++ = ']';
1698 *out++ = 's';
1699 return out;
1700 }
1701
1702 class get_locale {
1703 private:
1704 union {
1705 std::locale locale_;
1706 };
1707 bool has_locale_ = false;
1708
1709 public:
1710 get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
1711 if (localized)
1712 ::new (&locale_) std::locale(loc.template get<std::locale>());
1713 }
1714 ~get_locale() {
1715 if (has_locale_) locale_.~locale();
1716 }
1717 operator const std::locale&() const {
1718 return has_locale_ ? locale_ : get_classic_locale();
1719 }
1720 };
1721
1722 template <typename FormatContext, typename OutputIt, typename Rep,
1723 typename Period>
1724 struct chrono_formatter {
1725 FormatContext& context;
1726 OutputIt out;
1727 int precision;
1728 bool localized = false;
1729 // rep is unsigned to avoid overflow.
1730 using rep =
1731 conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
1732 unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
1733 rep val;
1734 using seconds = std::chrono::duration<rep>;
1735 seconds s;
1736 using milliseconds = std::chrono::duration<rep, std::milli>;
1737 bool negative;
1738
1739 using char_type = typename FormatContext::char_type;
1740 using tm_writer_type = tm_writer<OutputIt, char_type>;
1741
1742 chrono_formatter(FormatContext& ctx, OutputIt o,
1743 std::chrono::duration<Rep, Period> d)
1744 : context(ctx),
1745 out(o),
1746 val(static_cast<rep>(d.count())),
1747 negative(false) {
1748 if (d.count() < 0) {
1749 val = 0 - val;
1750 negative = true;
1751 }
1752
1753 // this may overflow and/or the result may not fit in the
1754 // target type.
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);
1759 #else
1760 s = std::chrono::duration_cast<seconds>(
1761 std::chrono::duration<rep, Period>(val));
1762 #endif
1763 }
1764
1765 // returns true if nan or inf, writes to out.
1766 bool handle_nan_inf() {
1767 if (isfinite(val)) {
1768 return false;
1769 }
1770 if (isnan(val)) {
1771 write_nan();
1772 return true;
1773 }
1774 // must be +-inf
1775 if (val > 0) {
1776 write_pinf();
1777 } else {
1778 write_ninf();
1779 }
1780 return true;
1781 }
1782
1783 Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
1784
1785 Rep hour12() const {
1786 Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
1787 return hour <= 0 ? 12 : hour;
1788 }
1789
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)); }
1792
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);
1798 return time;
1799 }
1800
1801 void write_sign() {
1802 if (negative) {
1803 *out++ = '-';
1804 negative = false;
1805 }
1806 }
1807
1808 void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
1809 write_sign();
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);
1816 }
1817 out = format_decimal<char_type>(out, n, num_digits).end;
1818 }
1819
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); }
1823
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);
1829 (w.*cb)(args...);
1830 out = w.out();
1831 }
1832
1833 void on_text(const char_type* begin, const char_type* end) {
1834 std::copy(begin, end, out);
1835 }
1836
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) {}
1864
1865 void on_24_hour(numeric_system ns, pad_type pad) {
1866 if (handle_nan_inf()) return;
1867
1868 if (ns == numeric_system::standard) return write(hour(), 2, pad);
1869 auto time = tm();
1870 time.tm_hour = to_nonnegative_int(hour(), 24);
1871 format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
1872 }
1873
1874 void on_12_hour(numeric_system ns, pad_type pad) {
1875 if (handle_nan_inf()) return;
1876
1877 if (ns == numeric_system::standard) return write(hour12(), 2, pad);
1878 auto time = tm();
1879 time.tm_hour = to_nonnegative_int(hour12(), 12);
1880 format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
1881 }
1882
1883 void on_minute(numeric_system ns, pad_type pad) {
1884 if (handle_nan_inf()) return;
1885
1886 if (ns == numeric_system::standard) return write(minute(), 2, pad);
1887 auto time = tm();
1888 time.tm_min = to_nonnegative_int(minute(), 60);
1889 format_tm(time, &tm_writer_type::on_minute, ns, pad);
1890 }
1891
1892 void on_second(numeric_system ns, pad_type pad) {
1893 if (handle_nan_inf()) return;
1894
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),
1899 precision);
1900 if (negative) *out++ = '-';
1901 if (buf.size() < 2 || buf[1] == '.') {
1902 out = detail::write_padding(out, pad);
1903 }
1904 out = std::copy(buf.begin(), buf.end(), out);
1905 } else {
1906 write(second(), 2, pad);
1907 write_fractional_seconds<char_type>(
1908 out, std::chrono::duration<rep, Period>(val), precision);
1909 }
1910 return;
1911 }
1912 auto time = tm();
1913 time.tm_sec = to_nonnegative_int(second(), 60);
1914 format_tm(time, &tm_writer_type::on_second, ns, pad);
1915 }
1916
1917 void on_12_hour_time() {
1918 if (handle_nan_inf()) return;
1919 format_tm(time(), &tm_writer_type::on_12_hour_time);
1920 }
1921
1922 void on_24_hour_time() {
1923 if (handle_nan_inf()) {
1924 *out++ = ':';
1925 handle_nan_inf();
1926 return;
1927 }
1928
1929 write(hour(), 2);
1930 *out++ = ':';
1931 write(minute(), 2);
1932 }
1933
1934 void on_iso_time() {
1935 on_24_hour_time();
1936 *out++ = ':';
1937 if (handle_nan_inf()) return;
1938 on_second(numeric_system::standard, pad_type::unspecified);
1939 }
1940
1941 void on_am_pm() {
1942 if (handle_nan_inf()) return;
1943 format_tm(time(), &tm_writer_type::on_am_pm);
1944 }
1945
1946 void on_duration_value() {
1947 if (handle_nan_inf()) return;
1948 write_sign();
1949 out = format_duration_value<char_type>(out, val, precision);
1950 }
1951
1952 void on_duration_unit() {
1953 out = format_duration_unit<char_type, Period>(out);
1954 }
1955 };
1956
1957 } // namespace detail
1958
1959 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1960 using weekday = std::chrono::weekday;
1961 #else
1962 // A fallback version of weekday.
1963 class weekday {
1964 private:
1965 unsigned char value;
1966
1967 public:
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; }
1972 };
1973
1974 class year_month_day {};
1975 #endif
1976
1977 // A rudimentary weekday formatter.
1978 template <typename Char> struct formatter<weekday, Char> {
1979 private:
1980 bool localized = false;
1981
1982 public:
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') {
1987 ++begin;
1988 localized = true;
1989 }
1990 return begin;
1991 }
1992
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();
2000 return w.out();
2001 }
2002 };
2003
2004 template <typename Rep, typename Period, typename Char>
2005 struct formatter<std::chrono::duration<Rep, Period>, Char> {
2006 private:
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_;
2012
2013 public:
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;
2018
2019 it = detail::parse_align(it, end, specs_);
2020 if (it == end) return it;
2021
2022 it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
2023 if (it == end) return it;
2024
2025 auto checker = detail::chrono_format_checker();
2026 if (*it == '.') {
2027 checker.has_precision_integral = !std::is_floating_point<Rep>::value;
2028 it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
2029 ctx);
2030 }
2031 if (it != end && *it == 'L') {
2032 localized_ = true;
2033 ++it;
2034 }
2035 end = detail::parse_chrono_format(it, end, checker);
2036 format_str_ = {it, detail::to_unsigned(end - it)};
2037 return end;
2038 }
2039
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_,
2052 ctx);
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);
2058 } else {
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);
2065 }
2066 return detail::write(
2067 ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
2068 }
2069 };
2070
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'>{};
2076 }
2077
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));
2088
2089 if (subsecs.count() < 0) {
2090 auto second =
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"));
2094 subsecs += second;
2095 val -= second;
2096 }
2097
2098 return formatter<std::tm, Char>::do_format(
2099 gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
2100 &subsecs);
2101 }
2102
2103 return formatter<std::tm, Char>::format(
2104 gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
2105 }
2106 };
2107
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'>{};
2114 }
2115
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));
2125
2126 return formatter<std::tm, Char>::do_format(
2127 localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
2128 ctx, &subsecs);
2129 }
2130
2131 return formatter<std::tm, Char>::format(
2132 localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
2133 ctx);
2134 }
2135 };
2136 #endif
2137
2138 #if FMT_USE_UTC_TIME
2139 template <typename Char, typename Duration>
2140 struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
2141 Char>
2142 : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
2143 Char> {
2144 template <typename FormatContext>
2145 auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
2146 FormatContext& ctx) const -> decltype(ctx.out()) {
2147 return formatter<
2148 std::chrono::time_point<std::chrono::system_clock, Duration>,
2149 Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
2150 }
2151 };
2152 #endif
2153
2154 template <typename Char> struct formatter<std::tm, Char> {
2155 private:
2156 format_specs<Char> specs_;
2157 detail::arg_ref<Char> width_ref_;
2158
2159 protected:
2160 basic_string_view<Char> format_str_;
2161
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_,
2169 ctx);
2170
2171 auto loc_ref = ctx.locale();
2172 detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
2173 auto w =
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);
2178 }
2179
2180 public:
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;
2185
2186 it = detail::parse_align(it, end, specs_);
2187 if (it == end) return it;
2188
2189 it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
2190 if (it == end) return it;
2191
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)};
2195 return end;
2196 }
2197
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);
2202 }
2203 };
2204
2205 FMT_END_EXPORT
2206 FMT_END_NAMESPACE
2207
2208 #endif // FMT_CHRONO_H_
This page took 0.124715 seconds and 4 git commands to generate.