Add vendor/fmt
[lttng-tools.git] / src / 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 <ctime>
14 #include <iterator>
15 #include <locale>
16 #include <ostream>
17 #include <type_traits>
18
19 #include "format.h"
20
21 FMT_BEGIN_NAMESPACE
22
23 // Enable tzset.
24 #ifndef FMT_USE_TZSET
25 // UWP doesn't provide _tzset.
26 # if FMT_HAS_INCLUDE("winapifamily.h")
27 # include <winapifamily.h>
28 # endif
29 # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
30 (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
31 # define FMT_USE_TZSET 1
32 # else
33 # define FMT_USE_TZSET 0
34 # endif
35 #endif
36
37 // Enable safe chrono durations, unless explicitly disabled.
38 #ifndef FMT_SAFE_DURATION_CAST
39 # define FMT_SAFE_DURATION_CAST 1
40 #endif
41 #if FMT_SAFE_DURATION_CAST
42
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
47 //
48 // Copyright Paul Dreik 2019
49 namespace safe_duration_cast {
50
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) {
56 ec = 0;
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");
61
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.
65 } else {
66 // From does not always fit in To, resort to a dynamic check.
67 if (from < (T::min)() || from > (T::max)()) {
68 // outside range.
69 ec = 1;
70 return {};
71 }
72 }
73 return static_cast<To>(from);
74 }
75
76 /**
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.
79 */
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) {
85 ec = 0;
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");
90
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)) {
94 ec = 1;
95 return {};
96 }
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>())) {
100 ec = 1;
101 return {};
102 }
103 }
104
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>())) {
108 ec = 1;
109 return {};
110 }
111 return static_cast<To>(from); // Lossless conversion.
112 }
113
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) {
117 ec = 0;
118 return from;
119 } // function
120
121 // clang-format off
122 /**
123 * converts From to To if possible, otherwise ec is set.
124 *
125 * input | output
126 * ---------------------------------|---------------
127 * NaN | NaN
128 * Inf | Inf
129 * normal, fits in output | converted (possibly lossy)
130 * normal, does not fit in output | ec is set
131 * subnormal | best effort
132 * -Inf | -Inf
133 */
134 // clang-format on
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) {
138 ec = 0;
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");
142
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);
147 }
148 // not within range.
149 ec = 1;
150 return {};
151 }
152
153 // nan and inf will be preserved
154 return static_cast<To>(from);
155 } // function
156
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) {
160 ec = 0;
161 static_assert(std::is_floating_point<From>::value, "From must be floating");
162 return from;
163 }
164
165 /**
166 * safe duration cast between integral durations
167 */
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,
172 int& ec) {
173 using From = std::chrono::duration<FromRep, FromPeriod>;
174 ec = 0;
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:
177 struct Factor
178 : std::ratio_divide<typename From::period, typename To::period> {};
179
180 static_assert(Factor::num > 0, "num must be positive");
181 static_assert(Factor::den > 0, "den must be positive");
182
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;
190
191 // safe conversion to IntermediateRep
192 IntermediateRep count =
193 lossless_integral_conversion<IntermediateRep>(from.count(), ec);
194 if (ec) return {};
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;
198 if (count > max1) {
199 ec = 1;
200 return {};
201 }
202 const auto min1 =
203 (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
204 if (count < min1) {
205 ec = 1;
206 return {};
207 }
208 count *= Factor::num;
209 }
210
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);
214 }
215
216 /**
217 * safe duration_cast between floating point durations
218 */
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,
223 int& ec) {
224 using From = std::chrono::duration<FromRep, FromPeriod>;
225 ec = 0;
226 if (std::isnan(from.count())) {
227 // nan in, gives nan out. easy.
228 return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
229 }
230 // maybe we should also check if from is denormal, and decide what to do about
231 // it.
232
233 // +-inf should be preserved.
234 if (std::isinf(from.count())) {
235 return To{from.count()};
236 }
237
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:
240 struct Factor
241 : std::ratio_divide<typename From::period, typename To::period> {};
242
243 static_assert(Factor::num > 0, "num must be positive");
244 static_assert(Factor::den > 0, "den must be positive");
245
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;
253
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);
258 if (ec) {
259 return {};
260 }
261
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);
266 if (count > max1) {
267 ec = 1;
268 return {};
269 }
270 constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
271 static_cast<IntermediateRep>(Factor::num);
272 if (count < min1) {
273 ec = 1;
274 return {};
275 }
276 count *= static_cast<IntermediateRep>(Factor::num);
277 }
278
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);
283 }
284
285 // convert to the to type, safely
286 using ToRep = typename To::rep;
287
288 const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
289 if (ec) {
290 return {};
291 }
292 return To{tocount};
293 }
294 } // namespace safe_duration_cast
295 #endif
296
297 // Prevents expansion of a preceding token as a function-style macro.
298 // Usage: f FMT_NOMACRO()
299 #define FMT_NOMACRO
300
301 namespace detail {
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<>(); }
307
308 inline const std::locale& get_classic_locale() {
309 static const auto& locale = std::locale::classic();
310 return locale;
311 }
312
313 template <typename CodeUnit> struct codecvt_result {
314 static constexpr const size_t max_size = 32;
315 CodeUnit buf[max_size];
316 CodeUnit* end;
317 };
318 template <typename CodeUnit>
319 constexpr const size_t codecvt_result<CodeUnit>::max_size;
320
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
330 #else
331 auto& f = std::use_facet<codecvt>(loc);
332 #endif
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"));
339 }
340
341 template <typename OutputIt>
342 auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
343 -> OutputIt {
344 if (detail::is_utf8() && loc != get_classic_locale()) {
345 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
346 // gcc-4.
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
350 // and newer.
351 using code_unit = wchar_t;
352 #else
353 using code_unit = char32_t;
354 #endif
355
356 using unit_t = codecvt_result<code_unit>;
357 unit_t 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) {
364 // surrogate pair
365 ++p;
366 if (p == unit.end || (c & 0xfc00) != 0xd800 ||
367 (*p & 0xfc00) != 0xdc00) {
368 FMT_THROW(format_error("failed to format time"));
369 }
370 c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
371 }
372 if (c < 0x80) {
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)));
386 } else {
387 FMT_THROW(format_error("failed to format time"));
388 }
389 }
390 return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
391 }
392 return copy_str<char>(in.data(), in.data() + in.size(), out);
393 }
394
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)
398 -> OutputIt {
399 codecvt_result<Char> unit;
400 write_codecvt(unit, sv, loc);
401 return copy_str<Char>(unit.buf, unit.end, out);
402 }
403
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)
407 -> OutputIt {
408 return write_encoded_tm_str(out, sv, loc);
409 }
410
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);
416 os.imbue(loc);
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"));
421 }
422
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);
429 return buf.out();
430 }
431
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);
439 }
440
441 } // namespace detail
442
443 FMT_MODULE_EXPORT_BEGIN
444
445 /**
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.
449 */
450 inline std::tm localtime(std::time_t time) {
451 struct dispatcher {
452 std::time_t time_;
453 std::tm tm_;
454
455 dispatcher(std::time_t t) : time_(t) {}
456
457 bool run() {
458 using namespace fmt::detail;
459 return handle(localtime_r(&time_, &tm_));
460 }
461
462 bool handle(std::tm* tm) { return tm != nullptr; }
463
464 bool handle(detail::null<>) {
465 using namespace fmt::detail;
466 return fallback(localtime_s(&tm_, &time_));
467 }
468
469 bool fallback(int res) { return res == 0; }
470
471 #if !FMT_MSC_VER
472 bool fallback(detail::null<>) {
473 using namespace fmt::detail;
474 std::tm* tm = std::localtime(&time_);
475 if (tm) tm_ = *tm;
476 return tm != nullptr;
477 }
478 #endif
479 };
480 dispatcher lt(time);
481 // Too big time values may be unsupported.
482 if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
483 return lt.tm_;
484 }
485
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));
489 }
490
491 /**
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.
495 */
496 inline std::tm gmtime(std::time_t time) {
497 struct dispatcher {
498 std::time_t time_;
499 std::tm tm_;
500
501 dispatcher(std::time_t t) : time_(t) {}
502
503 bool run() {
504 using namespace fmt::detail;
505 return handle(gmtime_r(&time_, &tm_));
506 }
507
508 bool handle(std::tm* tm) { return tm != nullptr; }
509
510 bool handle(detail::null<>) {
511 using namespace fmt::detail;
512 return fallback(gmtime_s(&tm_, &time_));
513 }
514
515 bool fallback(int res) { return res == 0; }
516
517 #if !FMT_MSC_VER
518 bool fallback(detail::null<>) {
519 std::tm* tm = std::gmtime(&time_);
520 if (tm) tm_ = *tm;
521 return tm != nullptr;
522 }
523 #endif
524 };
525 dispatcher gt(time);
526 // Too big time values may be unsupported.
527 if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
528 return gt.tm_;
529 }
530
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));
534 }
535
536 FMT_BEGIN_DETAIL_NAMESPACE
537
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.
547 // The difference is
548 // y - x = a * 6
549 // a can be found from x:
550 // a = floor(x / 10)
551 // then
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);
561
562 constexpr const size_t len = 8;
563 if (const_check(is_big_endian())) {
564 char tmp[len];
565 memcpy(tmp, &digits, len);
566 std::reverse_copy(tmp, tmp + len, buf);
567 } else {
568 memcpy(buf, &digits, len);
569 }
570 }
571
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";
592 return nullptr;
593 }
594
595 enum class numeric_system {
596 standard,
597 // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
598 alternative
599 };
600
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,
604 const Char* end,
605 Handler&& handler) {
606 auto ptr = begin;
607 while (ptr != end) {
608 auto c = *ptr;
609 if (c == '}') break;
610 if (c != '%') {
611 ++ptr;
612 continue;
613 }
614 if (begin != ptr) handler.on_text(begin, ptr);
615 ++ptr; // consume '%'
616 if (ptr == end) FMT_THROW(format_error("invalid format"));
617 c = *ptr++;
618 switch (c) {
619 case '%':
620 handler.on_text(ptr - 1, ptr);
621 break;
622 case 'n': {
623 const Char newline[] = {'\n'};
624 handler.on_text(newline, newline + 1);
625 break;
626 }
627 case 't': {
628 const Char tab[] = {'\t'};
629 handler.on_text(tab, tab + 1);
630 break;
631 }
632 // Year:
633 case 'Y':
634 handler.on_year(numeric_system::standard);
635 break;
636 case 'y':
637 handler.on_short_year(numeric_system::standard);
638 break;
639 case 'C':
640 handler.on_century(numeric_system::standard);
641 break;
642 case 'G':
643 handler.on_iso_week_based_year();
644 break;
645 case 'g':
646 handler.on_iso_week_based_short_year();
647 break;
648 // Day of the week:
649 case 'a':
650 handler.on_abbr_weekday();
651 break;
652 case 'A':
653 handler.on_full_weekday();
654 break;
655 case 'w':
656 handler.on_dec0_weekday(numeric_system::standard);
657 break;
658 case 'u':
659 handler.on_dec1_weekday(numeric_system::standard);
660 break;
661 // Month:
662 case 'b':
663 case 'h':
664 handler.on_abbr_month();
665 break;
666 case 'B':
667 handler.on_full_month();
668 break;
669 case 'm':
670 handler.on_dec_month(numeric_system::standard);
671 break;
672 // Day of the year/month:
673 case 'U':
674 handler.on_dec0_week_of_year(numeric_system::standard);
675 break;
676 case 'W':
677 handler.on_dec1_week_of_year(numeric_system::standard);
678 break;
679 case 'V':
680 handler.on_iso_week_of_year(numeric_system::standard);
681 break;
682 case 'j':
683 handler.on_day_of_year();
684 break;
685 case 'd':
686 handler.on_day_of_month(numeric_system::standard);
687 break;
688 case 'e':
689 handler.on_day_of_month_space(numeric_system::standard);
690 break;
691 // Hour, minute, second:
692 case 'H':
693 handler.on_24_hour(numeric_system::standard);
694 break;
695 case 'I':
696 handler.on_12_hour(numeric_system::standard);
697 break;
698 case 'M':
699 handler.on_minute(numeric_system::standard);
700 break;
701 case 'S':
702 handler.on_second(numeric_system::standard);
703 break;
704 // Other:
705 case 'c':
706 handler.on_datetime(numeric_system::standard);
707 break;
708 case 'x':
709 handler.on_loc_date(numeric_system::standard);
710 break;
711 case 'X':
712 handler.on_loc_time(numeric_system::standard);
713 break;
714 case 'D':
715 handler.on_us_date();
716 break;
717 case 'F':
718 handler.on_iso_date();
719 break;
720 case 'r':
721 handler.on_12_hour_time();
722 break;
723 case 'R':
724 handler.on_24_hour_time();
725 break;
726 case 'T':
727 handler.on_iso_time();
728 break;
729 case 'p':
730 handler.on_am_pm();
731 break;
732 case 'Q':
733 handler.on_duration_value();
734 break;
735 case 'q':
736 handler.on_duration_unit();
737 break;
738 case 'z':
739 handler.on_utc_offset();
740 break;
741 case 'Z':
742 handler.on_tz_name();
743 break;
744 // Alternative representation:
745 case 'E': {
746 if (ptr == end) FMT_THROW(format_error("invalid format"));
747 c = *ptr++;
748 switch (c) {
749 case 'Y':
750 handler.on_year(numeric_system::alternative);
751 break;
752 case 'y':
753 handler.on_offset_year();
754 break;
755 case 'C':
756 handler.on_century(numeric_system::alternative);
757 break;
758 case 'c':
759 handler.on_datetime(numeric_system::alternative);
760 break;
761 case 'x':
762 handler.on_loc_date(numeric_system::alternative);
763 break;
764 case 'X':
765 handler.on_loc_time(numeric_system::alternative);
766 break;
767 default:
768 FMT_THROW(format_error("invalid format"));
769 }
770 break;
771 }
772 case 'O':
773 if (ptr == end) FMT_THROW(format_error("invalid format"));
774 c = *ptr++;
775 switch (c) {
776 case 'y':
777 handler.on_short_year(numeric_system::alternative);
778 break;
779 case 'm':
780 handler.on_dec_month(numeric_system::alternative);
781 break;
782 case 'U':
783 handler.on_dec0_week_of_year(numeric_system::alternative);
784 break;
785 case 'W':
786 handler.on_dec1_week_of_year(numeric_system::alternative);
787 break;
788 case 'V':
789 handler.on_iso_week_of_year(numeric_system::alternative);
790 break;
791 case 'd':
792 handler.on_day_of_month(numeric_system::alternative);
793 break;
794 case 'e':
795 handler.on_day_of_month_space(numeric_system::alternative);
796 break;
797 case 'w':
798 handler.on_dec0_weekday(numeric_system::alternative);
799 break;
800 case 'u':
801 handler.on_dec1_weekday(numeric_system::alternative);
802 break;
803 case 'H':
804 handler.on_24_hour(numeric_system::alternative);
805 break;
806 case 'I':
807 handler.on_12_hour(numeric_system::alternative);
808 break;
809 case 'M':
810 handler.on_minute(numeric_system::alternative);
811 break;
812 case 'S':
813 handler.on_second(numeric_system::alternative);
814 break;
815 default:
816 FMT_THROW(format_error("invalid format"));
817 }
818 break;
819 default:
820 FMT_THROW(format_error("invalid format"));
821 }
822 begin = ptr;
823 }
824 if (begin != ptr) handler.on_text(begin, ptr);
825 return ptr;
826 }
827
828 template <typename Derived> struct null_chrono_spec_handler {
829 FMT_CONSTEXPR void unsupported() {
830 static_cast<Derived*>(this)->unsupported();
831 }
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(); }
868 };
869
870 struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
871 FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
872
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() {}
909 };
910
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] : "?";
916 }
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] : "???";
921 }
922
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] : "?";
928 }
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",
933 };
934 return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
935 }
936
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)>>
941 : std::true_type {};
942
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)>>
947 : std::true_type {};
948
949 #if FMT_USE_TZSET
950 inline void tzset_once() {
951 static bool init = []() -> bool {
952 _tzset();
953 return true;
954 }();
955 ignore_unused(init);
956 }
957 #endif
958
959 template <typename OutputIt, typename Char> class tm_writer {
960 private:
961 static constexpr int days_per_week = 7;
962
963 const std::locale& loc_;
964 const bool is_classic_;
965 OutputIt out_;
966 const std::tm& tm_;
967
968 auto tm_sec() const noexcept -> int {
969 FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
970 return tm_.tm_sec;
971 }
972 auto tm_min() const noexcept -> int {
973 FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
974 return tm_.tm_min;
975 }
976 auto tm_hour() const noexcept -> int {
977 FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
978 return tm_.tm_hour;
979 }
980 auto tm_mday() const noexcept -> int {
981 FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
982 return tm_.tm_mday;
983 }
984 auto tm_mon() const noexcept -> int {
985 FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
986 return tm_.tm_mon;
987 }
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, "");
991 return tm_.tm_wday;
992 }
993 auto tm_yday() const noexcept -> int {
994 FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
995 return tm_.tm_yday;
996 }
997
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;
1002 }
1003
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);
1012 }
1013
1014 // Algorithm:
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;
1018 const auto curr_p =
1019 (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
1020 days_per_week;
1021 const auto prev_p =
1022 (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
1023 days_per_week;
1024 return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
1025 }
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)) /
1028 days_per_week;
1029 }
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;
1035 return year;
1036 }
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;
1042 return w;
1043 }
1044
1045 void write1(int value) {
1046 *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
1047 }
1048 void write2(int value) {
1049 const char* d = digits2(to_unsigned(value) % 100);
1050 *out_++ = *d++;
1051 *out_++ = *d;
1052 }
1053
1054 void write_year_extended(long long year) {
1055 // At least 4 characters.
1056 int width = 4;
1057 if (year < 0) {
1058 *out_++ = '-';
1059 year = 0 - year;
1060 --width;
1061 }
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;
1066 }
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));
1071 } else {
1072 write_year_extended(year);
1073 }
1074 }
1075
1076 void write_utc_offset(long offset) {
1077 if (offset < 0) {
1078 *out_++ = '-';
1079 offset = -offset;
1080 } else {
1081 *out_++ = '+';
1082 }
1083 offset /= 60;
1084 write2(static_cast<int>(offset / 60));
1085 write2(static_cast<int>(offset % 60));
1086 }
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);
1090 }
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)
1094 # if FMT_USE_TZSET
1095 tzset_once();
1096 # endif
1097 long offset = 0;
1098 _get_timezone(&offset);
1099 if (tm.tm_isdst) {
1100 long dstbias = 0;
1101 _get_dstbias(&dstbias);
1102 offset += dstbias;
1103 }
1104 write_utc_offset(-offset);
1105 #else
1106 ignore_unused(tm);
1107 format_localized('z');
1108 #endif
1109 }
1110
1111 template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
1112 void format_tz_name_impl(const T& tm) {
1113 if (is_classic_)
1114 out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
1115 else
1116 format_localized('Z');
1117 }
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');
1121 }
1122
1123 void format_localized(char format, char modifier = 0) {
1124 out_ = write<Char>(out_, tm_, loc_, format, modifier);
1125 }
1126
1127 public:
1128 tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
1129 : loc_(loc),
1130 is_classic_(loc_ == get_classic_locale()),
1131 out_(out),
1132 tm_(tm) {}
1133
1134 OutputIt out() const { return out_; }
1135
1136 FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
1137 out_ = copy_str<Char>(begin, end, out_);
1138 }
1139
1140 void on_abbr_weekday() {
1141 if (is_classic_)
1142 out_ = write(out_, tm_wday_short_name(tm_wday()));
1143 else
1144 format_localized('a');
1145 }
1146 void on_full_weekday() {
1147 if (is_classic_)
1148 out_ = write(out_, tm_wday_full_name(tm_wday()));
1149 else
1150 format_localized('A');
1151 }
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');
1155 }
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);
1160 } else {
1161 format_localized('u', 'O');
1162 }
1163 }
1164
1165 void on_abbr_month() {
1166 if (is_classic_)
1167 out_ = write(out_, tm_mon_short_name(tm_mon()));
1168 else
1169 format_localized('b');
1170 }
1171 void on_full_month() {
1172 if (is_classic_)
1173 out_ = write(out_, tm_mon_full_name(tm_mon()));
1174 else
1175 format_localized('B');
1176 }
1177
1178 void on_datetime(numeric_system ns) {
1179 if (is_classic_) {
1180 on_abbr_weekday();
1181 *out_++ = ' ';
1182 on_abbr_month();
1183 *out_++ = ' ';
1184 on_day_of_month_space(numeric_system::standard);
1185 *out_++ = ' ';
1186 on_iso_time();
1187 *out_++ = ' ';
1188 on_year(numeric_system::standard);
1189 } else {
1190 format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
1191 }
1192 }
1193 void on_loc_date(numeric_system ns) {
1194 if (is_classic_)
1195 on_us_date();
1196 else
1197 format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
1198 }
1199 void on_loc_time(numeric_system ns) {
1200 if (is_classic_)
1201 on_iso_time();
1202 else
1203 format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
1204 }
1205 void on_us_date() {
1206 char buf[8];
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_);
1211 }
1212 void on_iso_date() {
1213 auto year = tm_year();
1214 char buf[10];
1215 size_t offset = 0;
1216 if (year >= 0 && year < 10000) {
1217 copy2(buf, digits2(to_unsigned(year / 100)));
1218 } else {
1219 offset = 4;
1220 write_year_extended(year);
1221 year = 0;
1222 }
1223 write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
1224 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1225 '-');
1226 out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
1227 }
1228
1229 void on_utc_offset() { format_utc_offset_impl(tm_); }
1230 void on_tz_name() { format_tz_name_impl(tm_); }
1231
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');
1236 }
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');
1241 }
1242 void on_offset_year() {
1243 if (is_classic_) return write2(split_year_lower(tm_year()));
1244 format_localized('y', 'E');
1245 }
1246
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.
1253 *out_++ = '-';
1254 *out_++ = '0';
1255 } else if (upper >= 0 && upper < 100) {
1256 write2(static_cast<int>(upper));
1257 } else {
1258 out_ = write<Char>(out_, upper);
1259 }
1260 } else {
1261 format_localized('C', 'E');
1262 }
1263 }
1264
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');
1269 }
1270
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');
1275 }
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))) /
1281 days_per_week);
1282 } else {
1283 format_localized('W', 'O');
1284 }
1285 }
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');
1290 }
1291
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()));
1295 }
1296
1297 void on_day_of_year() {
1298 auto yday = tm_yday() + 1;
1299 write1(yday / 100);
1300 write2(yday % 100);
1301 }
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');
1305 }
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];
1311 *out_++ = d2[1];
1312 } else {
1313 format_localized('e', 'O');
1314 }
1315 }
1316
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');
1320 }
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');
1325 }
1326 void on_minute(numeric_system ns) {
1327 if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
1328 format_localized('M', 'O');
1329 }
1330 void on_second(numeric_system ns) {
1331 if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
1332 format_localized('S', 'O');
1333 }
1334
1335 void on_12_hour_time() {
1336 if (is_classic_) {
1337 char buf[8];
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_);
1341 *out_++ = ' ';
1342 on_am_pm();
1343 } else {
1344 format_localized('r');
1345 }
1346 }
1347 void on_24_hour_time() {
1348 write2(tm_hour());
1349 *out_++ = ':';
1350 write2(tm_min());
1351 }
1352 void on_iso_time() {
1353 char buf[8];
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_);
1357 }
1358
1359 void on_am_pm() {
1360 if (is_classic_) {
1361 *out_++ = tm_hour() < 12 ? 'A' : 'P';
1362 *out_++ = 'M';
1363 } else {
1364 format_localized('p');
1365 }
1366 }
1367
1368 // These apply to chrono durations but not tm.
1369 void on_duration_value() {}
1370 void on_duration_unit() {}
1371 };
1372
1373 struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
1374 FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
1375
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() {}
1388 };
1389
1390 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
1391 inline bool isnan(T) {
1392 return false;
1393 }
1394 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
1395 inline bool isnan(T value) {
1396 return std::isnan(value);
1397 }
1398
1399 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
1400 inline bool isfinite(T) {
1401 return true;
1402 }
1403
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),
1408 "invalid value");
1409 (void)upper;
1410 return static_cast<Int>(value);
1411 }
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);
1417 }
1418
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);
1422 }
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));
1426 }
1427
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 {
1432 using type = T;
1433 };
1434
1435 template <typename T> struct make_unsigned_or_unchanged<T, true> {
1436 using type = typename std::make_unsigned<T>::type;
1437 };
1438
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) {
1443 int ec;
1444 To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
1445 if (ec) FMT_THROW(format_error("cannot format duration"));
1446 return to;
1447 }
1448 #endif
1449
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
1455 // target type.
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;
1464 const auto ms =
1465 fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
1466 return ms;
1467 #else
1468 auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
1469 return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
1470 #endif
1471 }
1472
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
1478 ? n
1479 : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1));
1480 }
1481
1482 constexpr long long pow10(std::uint32_t n) {
1483 return n == 0 ? 1 : 10 * pow10(n - 1);
1484 }
1485
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;
1497 }
1498
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) {
1503 return d;
1504 }
1505
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);
1510 }
1511
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);
1520 }
1521
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);
1525 }
1526
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);
1533 }
1534
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());
1539 *out++ = '[';
1540 out = write<Char>(out, Period::num);
1541 if (const_check(Period::den != 1)) {
1542 *out++ = '/';
1543 out = write<Char>(out, Period::den);
1544 }
1545 *out++ = ']';
1546 *out++ = 's';
1547 return out;
1548 }
1549
1550 class get_locale {
1551 private:
1552 union {
1553 std::locale locale_;
1554 };
1555 bool has_locale_ = false;
1556
1557 public:
1558 get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
1559 if (localized)
1560 ::new (&locale_) std::locale(loc.template get<std::locale>());
1561 }
1562 ~get_locale() {
1563 if (has_locale_) locale_.~locale();
1564 }
1565 operator const std::locale&() const {
1566 return has_locale_ ? locale_ : get_classic_locale();
1567 }
1568 };
1569
1570 template <typename FormatContext, typename OutputIt, typename Rep,
1571 typename Period>
1572 struct chrono_formatter {
1573 FormatContext& context;
1574 OutputIt out;
1575 int precision;
1576 bool localized = false;
1577 // rep is unsigned to avoid overflow.
1578 using rep =
1579 conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
1580 unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
1581 rep val;
1582 using seconds = std::chrono::duration<rep>;
1583 seconds s;
1584 using milliseconds = std::chrono::duration<rep, std::milli>;
1585 bool negative;
1586
1587 using char_type = typename FormatContext::char_type;
1588 using tm_writer_type = tm_writer<OutputIt, char_type>;
1589
1590 chrono_formatter(FormatContext& ctx, OutputIt o,
1591 std::chrono::duration<Rep, Period> d)
1592 : context(ctx),
1593 out(o),
1594 val(static_cast<rep>(d.count())),
1595 negative(false) {
1596 if (d.count() < 0) {
1597 val = 0 - val;
1598 negative = true;
1599 }
1600
1601 // this may overflow and/or the result may not fit in the
1602 // target type.
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);
1607 #else
1608 s = std::chrono::duration_cast<seconds>(
1609 std::chrono::duration<rep, Period>(val));
1610 #endif
1611 }
1612
1613 // returns true if nan or inf, writes to out.
1614 bool handle_nan_inf() {
1615 if (isfinite(val)) {
1616 return false;
1617 }
1618 if (isnan(val)) {
1619 write_nan();
1620 return true;
1621 }
1622 // must be +-inf
1623 if (val > 0) {
1624 write_pinf();
1625 } else {
1626 write_ninf();
1627 }
1628 return true;
1629 }
1630
1631 Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
1632
1633 Rep hour12() const {
1634 Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
1635 return hour <= 0 ? 12 : hour;
1636 }
1637
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)); }
1640
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);
1646 return time;
1647 }
1648
1649 void write_sign() {
1650 if (negative) {
1651 *out++ = '-';
1652 negative = false;
1653 }
1654 }
1655
1656 void write(Rep value, int width) {
1657 write_sign();
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;
1664 }
1665
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);
1669
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) {
1676 *out++ = '.';
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)
1687 .count();
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;
1694 }
1695 }
1696
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); }
1700
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);
1706 (w.*cb)(args...);
1707 out = w.out();
1708 }
1709
1710 void on_text(const char_type* begin, const char_type* end) {
1711 std::copy(begin, end, out);
1712 }
1713
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) {}
1741
1742 void on_24_hour(numeric_system ns) {
1743 if (handle_nan_inf()) return;
1744
1745 if (ns == numeric_system::standard) return write(hour(), 2);
1746 auto time = tm();
1747 time.tm_hour = to_nonnegative_int(hour(), 24);
1748 format_tm(time, &tm_writer_type::on_24_hour, ns);
1749 }
1750
1751 void on_12_hour(numeric_system ns) {
1752 if (handle_nan_inf()) return;
1753
1754 if (ns == numeric_system::standard) return write(hour12(), 2);
1755 auto time = tm();
1756 time.tm_hour = to_nonnegative_int(hour12(), 12);
1757 format_tm(time, &tm_writer_type::on_12_hour, ns);
1758 }
1759
1760 void on_minute(numeric_system ns) {
1761 if (handle_nan_inf()) return;
1762
1763 if (ns == numeric_system::standard) return write(minute(), 2);
1764 auto time = tm();
1765 time.tm_min = to_nonnegative_int(minute(), 60);
1766 format_tm(time, &tm_writer_type::on_minute, ns);
1767 }
1768
1769 void on_second(numeric_system ns) {
1770 if (handle_nan_inf()) return;
1771
1772 if (ns == numeric_system::standard) {
1773 write(second(), 2);
1774 write_fractional_seconds(std::chrono::duration<rep, Period>{val});
1775 return;
1776 }
1777 auto time = tm();
1778 time.tm_sec = to_nonnegative_int(second(), 60);
1779 format_tm(time, &tm_writer_type::on_second, ns);
1780 }
1781
1782 void on_12_hour_time() {
1783 if (handle_nan_inf()) return;
1784 format_tm(time(), &tm_writer_type::on_12_hour_time);
1785 }
1786
1787 void on_24_hour_time() {
1788 if (handle_nan_inf()) {
1789 *out++ = ':';
1790 handle_nan_inf();
1791 return;
1792 }
1793
1794 write(hour(), 2);
1795 *out++ = ':';
1796 write(minute(), 2);
1797 }
1798
1799 void on_iso_time() {
1800 on_24_hour_time();
1801 *out++ = ':';
1802 if (handle_nan_inf()) return;
1803 on_second(numeric_system::standard);
1804 }
1805
1806 void on_am_pm() {
1807 if (handle_nan_inf()) return;
1808 format_tm(time(), &tm_writer_type::on_am_pm);
1809 }
1810
1811 void on_duration_value() {
1812 if (handle_nan_inf()) return;
1813 write_sign();
1814 out = format_duration_value<char_type>(out, val, precision);
1815 }
1816
1817 void on_duration_unit() {
1818 out = format_duration_unit<char_type, Period>(out);
1819 }
1820 };
1821
1822 FMT_END_DETAIL_NAMESPACE
1823
1824 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1825 using weekday = std::chrono::weekday;
1826 #else
1827 // A fallback version of weekday.
1828 class weekday {
1829 private:
1830 unsigned char value;
1831
1832 public:
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; }
1837 };
1838
1839 class year_month_day {};
1840 #endif
1841
1842 // A rudimentary weekday formatter.
1843 template <typename Char> struct formatter<weekday, Char> {
1844 private:
1845 bool localized = false;
1846
1847 public:
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') {
1852 ++begin;
1853 localized = true;
1854 }
1855 return begin;
1856 }
1857
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();
1865 return w.out();
1866 }
1867 };
1868
1869 template <typename Rep, typename Period, typename Char>
1870 struct formatter<std::chrono::duration<Rep, Period>, Char> {
1871 private:
1872 basic_format_specs<Char> specs;
1873 int precision = -1;
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>;
1880
1881 struct spec_handler {
1882 formatter& f;
1883 basic_format_parse_context<Char>& context;
1884 basic_string_view<Char> format_str;
1885
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);
1889 }
1890
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);
1894 }
1895
1896 FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
1897 return arg_ref_type(context.next_arg_id());
1898 }
1899
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;
1903 }
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;
1908 }
1909 FMT_CONSTEXPR void end_precision() {}
1910
1911 template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
1912 f.width_ref = make_arg_ref(arg_id);
1913 }
1914
1915 template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
1916 f.precision_ref = make_arg_ref(arg_id);
1917 }
1918 };
1919
1920 using iterator = typename basic_format_parse_context<Char>::iterator;
1921 struct parse_range {
1922 iterator begin;
1923 iterator end;
1924 };
1925
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);
1937 else
1938 handler.on_error("precision not allowed for this argument type");
1939 }
1940 if (begin != end && *begin == 'L') {
1941 ++begin;
1942 localized = true;
1943 }
1944 end = detail::parse_chrono_format(begin, end,
1945 detail::chrono_format_checker());
1946 return {begin, end};
1947 }
1948
1949 public:
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));
1955 return range.end;
1956 }
1957
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,
1969 width_ref, ctx);
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);
1975 } else {
1976 detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
1977 ctx, out, d);
1978 f.precision = precision_copy;
1979 f.localized = localized;
1980 detail::parse_chrono_format(begin, end, f);
1981 }
1982 return detail::write(
1983 ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
1984 }
1985 };
1986
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));
1993 }
1994
1995 template <typename ParseContext>
1996 FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
1997 return this->do_parse(ctx.begin(), ctx.end(), true);
1998 }
1999
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);
2004 }
2005
2006 static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'};
2007 };
2008
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[];
2013
2014 template <typename Char> struct formatter<std::tm, Char> {
2015 private:
2016 enum class spec {
2017 unknown,
2018 year_month_day,
2019 hh_mm_ss,
2020 };
2021 spec spec_ = spec::unknown;
2022 basic_string_view<Char> specs;
2023
2024 protected:
2025 template <typename It>
2026 FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false)
2027 -> It {
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;
2038 }
2039 return end;
2040 }
2041
2042 public:
2043 template <typename ParseContext>
2044 FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
2045 return this->do_parse(ctx.begin(), ctx.end());
2046 }
2047
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)
2055 w.on_iso_date();
2056 else if (spec_ == spec::hh_mm_ss)
2057 w.on_iso_time();
2058 else
2059 detail::parse_chrono_format(specs.begin(), specs.end(), w);
2060 return w.out();
2061 }
2062 };
2063
2064 FMT_MODULE_EXPORT_END
2065 FMT_END_NAMESPACE
2066
2067 #endif // FMT_CHRONO_H_
This page took 0.103666 seconds and 5 git commands to generate.