Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:20

0001 // Formatting library for C++ - chrono support
0002 //
0003 // Copyright (c) 2012 - present, Victor Zverovich
0004 // All rights reserved.
0005 //
0006 // For the license information refer to format.h.
0007 
0008 #ifndef FMT_CHRONO_H_
0009 #define FMT_CHRONO_H_
0010 
0011 #include <algorithm>
0012 #include <chrono>
0013 #include <cmath>    // std::isfinite
0014 #include <cstring>  // std::memcpy
0015 #include <ctime>
0016 #include <iterator>
0017 #include <locale>
0018 #include <ostream>
0019 #include <type_traits>
0020 
0021 #include "format.h"
0022 
0023 FMT_BEGIN_NAMESPACE
0024 
0025 // Enable tzset.
0026 #ifndef FMT_USE_TZSET
0027 // UWP doesn't provide _tzset.
0028 #  if FMT_HAS_INCLUDE("winapifamily.h")
0029 #    include <winapifamily.h>
0030 #  endif
0031 #  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
0032                           (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
0033 #    define FMT_USE_TZSET 1
0034 #  else
0035 #    define FMT_USE_TZSET 0
0036 #  endif
0037 #endif
0038 
0039 // Enable safe chrono durations, unless explicitly disabled.
0040 #ifndef FMT_SAFE_DURATION_CAST
0041 #  define FMT_SAFE_DURATION_CAST 1
0042 #endif
0043 #if FMT_SAFE_DURATION_CAST
0044 
0045 // For conversion between std::chrono::durations without undefined
0046 // behaviour or erroneous results.
0047 // This is a stripped down version of duration_cast, for inclusion in fmt.
0048 // See https://github.com/pauldreik/safe_duration_cast
0049 //
0050 // Copyright Paul Dreik 2019
0051 namespace safe_duration_cast {
0052 
0053 template <typename To, typename From,
0054           FMT_ENABLE_IF(!std::is_same<From, To>::value &&
0055                         std::numeric_limits<From>::is_signed ==
0056                             std::numeric_limits<To>::is_signed)>
0057 FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
0058   ec = 0;
0059   using F = std::numeric_limits<From>;
0060   using T = std::numeric_limits<To>;
0061   static_assert(F::is_integer, "From must be integral");
0062   static_assert(T::is_integer, "To must be integral");
0063 
0064   // A and B are both signed, or both unsigned.
0065   if (detail::const_check(F::digits <= T::digits)) {
0066     // From fits in To without any problem.
0067   } else {
0068     // From does not always fit in To, resort to a dynamic check.
0069     if (from < (T::min)() || from > (T::max)()) {
0070       // outside range.
0071       ec = 1;
0072       return {};
0073     }
0074   }
0075   return static_cast<To>(from);
0076 }
0077 
0078 /**
0079  * converts From to To, without loss. If the dynamic value of from
0080  * can't be converted to To without loss, ec is set.
0081  */
0082 template <typename To, typename From,
0083           FMT_ENABLE_IF(!std::is_same<From, To>::value &&
0084                         std::numeric_limits<From>::is_signed !=
0085                             std::numeric_limits<To>::is_signed)>
0086 FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
0087   ec = 0;
0088   using F = std::numeric_limits<From>;
0089   using T = std::numeric_limits<To>;
0090   static_assert(F::is_integer, "From must be integral");
0091   static_assert(T::is_integer, "To must be integral");
0092 
0093   if (detail::const_check(F::is_signed && !T::is_signed)) {
0094     // From may be negative, not allowed!
0095     if (fmt::detail::is_negative(from)) {
0096       ec = 1;
0097       return {};
0098     }
0099     // From is positive. Can it always fit in To?
0100     if (detail::const_check(F::digits > T::digits) &&
0101         from > static_cast<From>(detail::max_value<To>())) {
0102       ec = 1;
0103       return {};
0104     }
0105   }
0106 
0107   if (detail::const_check(!F::is_signed && T::is_signed &&
0108                           F::digits >= T::digits) &&
0109       from > static_cast<From>(detail::max_value<To>())) {
0110     ec = 1;
0111     return {};
0112   }
0113   return static_cast<To>(from);  // Lossless conversion.
0114 }
0115 
0116 template <typename To, typename From,
0117           FMT_ENABLE_IF(std::is_same<From, To>::value)>
0118 FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
0119   ec = 0;
0120   return from;
0121 }  // function
0122 
0123 // clang-format off
0124 /**
0125  * converts From to To if possible, otherwise ec is set.
0126  *
0127  * input                            |    output
0128  * ---------------------------------|---------------
0129  * NaN                              | NaN
0130  * Inf                              | Inf
0131  * normal, fits in output           | converted (possibly lossy)
0132  * normal, does not fit in output   | ec is set
0133  * subnormal                        | best effort
0134  * -Inf                             | -Inf
0135  */
0136 // clang-format on
0137 template <typename To, typename From,
0138           FMT_ENABLE_IF(!std::is_same<From, To>::value)>
0139 FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
0140   ec = 0;
0141   using T = std::numeric_limits<To>;
0142   static_assert(std::is_floating_point<From>::value, "From must be floating");
0143   static_assert(std::is_floating_point<To>::value, "To must be floating");
0144 
0145   // catch the only happy case
0146   if (std::isfinite(from)) {
0147     if (from >= T::lowest() && from <= (T::max)()) {
0148       return static_cast<To>(from);
0149     }
0150     // not within range.
0151     ec = 1;
0152     return {};
0153   }
0154 
0155   // nan and inf will be preserved
0156   return static_cast<To>(from);
0157 }  // function
0158 
0159 template <typename To, typename From,
0160           FMT_ENABLE_IF(std::is_same<From, To>::value)>
0161 FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
0162   ec = 0;
0163   static_assert(std::is_floating_point<From>::value, "From must be floating");
0164   return from;
0165 }
0166 
0167 /**
0168  * safe duration cast between integral durations
0169  */
0170 template <typename To, typename FromRep, typename FromPeriod,
0171           FMT_ENABLE_IF(std::is_integral<FromRep>::value),
0172           FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
0173 To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
0174                       int& ec) {
0175   using From = std::chrono::duration<FromRep, FromPeriod>;
0176   ec = 0;
0177   // the basic idea is that we need to convert from count() in the from type
0178   // to count() in the To type, by multiplying it with this:
0179   struct Factor
0180       : std::ratio_divide<typename From::period, typename To::period> {};
0181 
0182   static_assert(Factor::num > 0, "num must be positive");
0183   static_assert(Factor::den > 0, "den must be positive");
0184 
0185   // the conversion is like this: multiply from.count() with Factor::num
0186   // /Factor::den and convert it to To::rep, all this without
0187   // overflow/underflow. let's start by finding a suitable type that can hold
0188   // both To, From and Factor::num
0189   using IntermediateRep =
0190       typename std::common_type<typename From::rep, typename To::rep,
0191                                 decltype(Factor::num)>::type;
0192 
0193   // safe conversion to IntermediateRep
0194   IntermediateRep count =
0195       lossless_integral_conversion<IntermediateRep>(from.count(), ec);
0196   if (ec) return {};
0197   // multiply with Factor::num without overflow or underflow
0198   if (detail::const_check(Factor::num != 1)) {
0199     const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
0200     if (count > max1) {
0201       ec = 1;
0202       return {};
0203     }
0204     const auto min1 =
0205         (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
0206     if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
0207       ec = 1;
0208       return {};
0209     }
0210     count *= Factor::num;
0211   }
0212 
0213   if (detail::const_check(Factor::den != 1)) count /= Factor::den;
0214   auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
0215   return ec ? To() : To(tocount);
0216 }
0217 
0218 /**
0219  * safe duration_cast between floating point durations
0220  */
0221 template <typename To, typename FromRep, typename FromPeriod,
0222           FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
0223           FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
0224 To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
0225                       int& ec) {
0226   using From = std::chrono::duration<FromRep, FromPeriod>;
0227   ec = 0;
0228   if (std::isnan(from.count())) {
0229     // nan in, gives nan out. easy.
0230     return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
0231   }
0232   // maybe we should also check if from is denormal, and decide what to do about
0233   // it.
0234 
0235   // +-inf should be preserved.
0236   if (std::isinf(from.count())) {
0237     return To{from.count()};
0238   }
0239 
0240   // the basic idea is that we need to convert from count() in the from type
0241   // to count() in the To type, by multiplying it with this:
0242   struct Factor
0243       : std::ratio_divide<typename From::period, typename To::period> {};
0244 
0245   static_assert(Factor::num > 0, "num must be positive");
0246   static_assert(Factor::den > 0, "den must be positive");
0247 
0248   // the conversion is like this: multiply from.count() with Factor::num
0249   // /Factor::den and convert it to To::rep, all this without
0250   // overflow/underflow. let's start by finding a suitable type that can hold
0251   // both To, From and Factor::num
0252   using IntermediateRep =
0253       typename std::common_type<typename From::rep, typename To::rep,
0254                                 decltype(Factor::num)>::type;
0255 
0256   // force conversion of From::rep -> IntermediateRep to be safe,
0257   // even if it will never happen be narrowing in this context.
0258   IntermediateRep count =
0259       safe_float_conversion<IntermediateRep>(from.count(), ec);
0260   if (ec) {
0261     return {};
0262   }
0263 
0264   // multiply with Factor::num without overflow or underflow
0265   if (detail::const_check(Factor::num != 1)) {
0266     constexpr auto max1 = detail::max_value<IntermediateRep>() /
0267                           static_cast<IntermediateRep>(Factor::num);
0268     if (count > max1) {
0269       ec = 1;
0270       return {};
0271     }
0272     constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
0273                           static_cast<IntermediateRep>(Factor::num);
0274     if (count < min1) {
0275       ec = 1;
0276       return {};
0277     }
0278     count *= static_cast<IntermediateRep>(Factor::num);
0279   }
0280 
0281   // this can't go wrong, right? den>0 is checked earlier.
0282   if (detail::const_check(Factor::den != 1)) {
0283     using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
0284     count /= static_cast<common_t>(Factor::den);
0285   }
0286 
0287   // convert to the to type, safely
0288   using ToRep = typename To::rep;
0289 
0290   const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
0291   if (ec) {
0292     return {};
0293   }
0294   return To{tocount};
0295 }
0296 }  // namespace safe_duration_cast
0297 #endif
0298 
0299 // Prevents expansion of a preceding token as a function-style macro.
0300 // Usage: f FMT_NOMACRO()
0301 #define FMT_NOMACRO
0302 
0303 namespace detail {
0304 template <typename T = void> struct null {};
0305 inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
0306 inline null<> localtime_s(...) { return null<>(); }
0307 inline null<> gmtime_r(...) { return null<>(); }
0308 inline null<> gmtime_s(...) { return null<>(); }
0309 
0310 inline const std::locale& get_classic_locale() {
0311   static const auto& locale = std::locale::classic();
0312   return locale;
0313 }
0314 
0315 template <typename CodeUnit> struct codecvt_result {
0316   static constexpr const size_t max_size = 32;
0317   CodeUnit buf[max_size];
0318   CodeUnit* end;
0319 };
0320 template <typename CodeUnit>
0321 constexpr const size_t codecvt_result<CodeUnit>::max_size;
0322 
0323 template <typename CodeUnit>
0324 void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
0325                    const std::locale& loc) {
0326 #if FMT_CLANG_VERSION
0327 #  pragma clang diagnostic push
0328 #  pragma clang diagnostic ignored "-Wdeprecated"
0329   auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
0330 #  pragma clang diagnostic pop
0331 #else
0332   auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
0333 #endif
0334   auto mb = std::mbstate_t();
0335   const char* from_next = nullptr;
0336   auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
0337                      std::begin(out.buf), std::end(out.buf), out.end);
0338   if (result != std::codecvt_base::ok)
0339     FMT_THROW(format_error("failed to format time"));
0340 }
0341 
0342 template <typename OutputIt>
0343 auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
0344     -> OutputIt {
0345   if (detail::is_utf8() && loc != get_classic_locale()) {
0346     // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
0347     // gcc-4.
0348 #if FMT_MSC_VERSION != 0 || \
0349     (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
0350     // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
0351     // and newer.
0352     using code_unit = wchar_t;
0353 #else
0354     using code_unit = char32_t;
0355 #endif
0356 
0357     using unit_t = codecvt_result<code_unit>;
0358     unit_t unit;
0359     write_codecvt(unit, in, loc);
0360     // In UTF-8 is used one to four one-byte code units.
0361     auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>();
0362     for (code_unit* p = unit.buf; p != unit.end; ++p) {
0363       uint32_t c = static_cast<uint32_t>(*p);
0364       if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
0365         // surrogate pair
0366         ++p;
0367         if (p == unit.end || (c & 0xfc00) != 0xd800 ||
0368             (*p & 0xfc00) != 0xdc00) {
0369           FMT_THROW(format_error("failed to format time"));
0370         }
0371         c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
0372       }
0373       if (c < 0x80) {
0374         buf.push_back(static_cast<char>(c));
0375       } else if (c < 0x800) {
0376         buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
0377         buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
0378       } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
0379         buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
0380         buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
0381         buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
0382       } else if (c >= 0x10000 && c <= 0x10ffff) {
0383         buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
0384         buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
0385         buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
0386         buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
0387       } else {
0388         FMT_THROW(format_error("failed to format time"));
0389       }
0390     }
0391     return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
0392   }
0393   return copy_str<char>(in.data(), in.data() + in.size(), out);
0394 }
0395 
0396 template <typename Char, typename OutputIt,
0397           FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
0398 auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
0399     -> OutputIt {
0400   codecvt_result<Char> unit;
0401   write_codecvt(unit, sv, loc);
0402   return copy_str<Char>(unit.buf, unit.end, out);
0403 }
0404 
0405 template <typename Char, typename OutputIt,
0406           FMT_ENABLE_IF(std::is_same<Char, char>::value)>
0407 auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
0408     -> OutputIt {
0409   return write_encoded_tm_str(out, sv, loc);
0410 }
0411 
0412 template <typename Char>
0413 inline void do_write(buffer<Char>& buf, const std::tm& time,
0414                      const std::locale& loc, char format, char modifier) {
0415   auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
0416   auto&& os = std::basic_ostream<Char>(&format_buf);
0417   os.imbue(loc);
0418   using iterator = std::ostreambuf_iterator<Char>;
0419   const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
0420   auto end = facet.put(os, os, Char(' '), &time, format, modifier);
0421   if (end.failed()) FMT_THROW(format_error("failed to format time"));
0422 }
0423 
0424 template <typename Char, typename OutputIt,
0425           FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
0426 auto write(OutputIt out, const std::tm& time, const std::locale& loc,
0427            char format, char modifier = 0) -> OutputIt {
0428   auto&& buf = get_buffer<Char>(out);
0429   do_write<Char>(buf, time, loc, format, modifier);
0430   return buf.out();
0431 }
0432 
0433 template <typename Char, typename OutputIt,
0434           FMT_ENABLE_IF(std::is_same<Char, char>::value)>
0435 auto write(OutputIt out, const std::tm& time, const std::locale& loc,
0436            char format, char modifier = 0) -> OutputIt {
0437   auto&& buf = basic_memory_buffer<Char>();
0438   do_write<char>(buf, time, loc, format, modifier);
0439   return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
0440 }
0441 
0442 }  // namespace detail
0443 
0444 FMT_MODULE_EXPORT_BEGIN
0445 
0446 /**
0447   Converts given time since epoch as ``std::time_t`` value into calendar time,
0448   expressed in local time. Unlike ``std::localtime``, this function is
0449   thread-safe on most platforms.
0450  */
0451 inline std::tm localtime(std::time_t time) {
0452   struct dispatcher {
0453     std::time_t time_;
0454     std::tm tm_;
0455 
0456     dispatcher(std::time_t t) : time_(t) {}
0457 
0458     bool run() {
0459       using namespace fmt::detail;
0460       return handle(localtime_r(&time_, &tm_));
0461     }
0462 
0463     bool handle(std::tm* tm) { return tm != nullptr; }
0464 
0465     bool handle(detail::null<>) {
0466       using namespace fmt::detail;
0467       return fallback(localtime_s(&tm_, &time_));
0468     }
0469 
0470     bool fallback(int res) { return res == 0; }
0471 
0472 #if !FMT_MSC_VERSION
0473     bool fallback(detail::null<>) {
0474       using namespace fmt::detail;
0475       std::tm* tm = std::localtime(&time_);
0476       if (tm) tm_ = *tm;
0477       return tm != nullptr;
0478     }
0479 #endif
0480   };
0481   dispatcher lt(time);
0482   // Too big time values may be unsupported.
0483   if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
0484   return lt.tm_;
0485 }
0486 
0487 inline std::tm localtime(
0488     std::chrono::time_point<std::chrono::system_clock> time_point) {
0489   return localtime(std::chrono::system_clock::to_time_t(time_point));
0490 }
0491 
0492 /**
0493   Converts given time since epoch as ``std::time_t`` value into calendar time,
0494   expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
0495   function is thread-safe on most platforms.
0496  */
0497 inline std::tm gmtime(std::time_t time) {
0498   struct dispatcher {
0499     std::time_t time_;
0500     std::tm tm_;
0501 
0502     dispatcher(std::time_t t) : time_(t) {}
0503 
0504     bool run() {
0505       using namespace fmt::detail;
0506       return handle(gmtime_r(&time_, &tm_));
0507     }
0508 
0509     bool handle(std::tm* tm) { return tm != nullptr; }
0510 
0511     bool handle(detail::null<>) {
0512       using namespace fmt::detail;
0513       return fallback(gmtime_s(&tm_, &time_));
0514     }
0515 
0516     bool fallback(int res) { return res == 0; }
0517 
0518 #if !FMT_MSC_VERSION
0519     bool fallback(detail::null<>) {
0520       std::tm* tm = std::gmtime(&time_);
0521       if (tm) tm_ = *tm;
0522       return tm != nullptr;
0523     }
0524 #endif
0525   };
0526   dispatcher gt(time);
0527   // Too big time values may be unsupported.
0528   if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
0529   return gt.tm_;
0530 }
0531 
0532 inline std::tm gmtime(
0533     std::chrono::time_point<std::chrono::system_clock> time_point) {
0534   return gmtime(std::chrono::system_clock::to_time_t(time_point));
0535 }
0536 
0537 FMT_BEGIN_DETAIL_NAMESPACE
0538 
0539 // Writes two-digit numbers a, b and c separated by sep to buf.
0540 // The method by Pavel Novikov based on
0541 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
0542 inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
0543                                    unsigned c, char sep) {
0544   unsigned long long digits =
0545       a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
0546   // Convert each value to BCD.
0547   // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
0548   // The difference is
0549   //   y - x = a * 6
0550   // a can be found from x:
0551   //   a = floor(x / 10)
0552   // then
0553   //   y = x + a * 6 = x + floor(x / 10) * 6
0554   // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
0555   digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
0556   // Put low nibbles to high bytes and high nibbles to low bytes.
0557   digits = ((digits & 0x00f00000f00000f0) >> 4) |
0558            ((digits & 0x000f00000f00000f) << 8);
0559   auto usep = static_cast<unsigned long long>(sep);
0560   // Add ASCII '0' to each digit byte and insert separators.
0561   digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
0562 
0563   constexpr const size_t len = 8;
0564   if (const_check(is_big_endian())) {
0565     char tmp[len];
0566     std::memcpy(tmp, &digits, len);
0567     std::reverse_copy(tmp, tmp + len, buf);
0568   } else {
0569     std::memcpy(buf, &digits, len);
0570   }
0571 }
0572 
0573 template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
0574   if (std::is_same<Period, std::atto>::value) return "as";
0575   if (std::is_same<Period, std::femto>::value) return "fs";
0576   if (std::is_same<Period, std::pico>::value) return "ps";
0577   if (std::is_same<Period, std::nano>::value) return "ns";
0578   if (std::is_same<Period, std::micro>::value) return "µs";
0579   if (std::is_same<Period, std::milli>::value) return "ms";
0580   if (std::is_same<Period, std::centi>::value) return "cs";
0581   if (std::is_same<Period, std::deci>::value) return "ds";
0582   if (std::is_same<Period, std::ratio<1>>::value) return "s";
0583   if (std::is_same<Period, std::deca>::value) return "das";
0584   if (std::is_same<Period, std::hecto>::value) return "hs";
0585   if (std::is_same<Period, std::kilo>::value) return "ks";
0586   if (std::is_same<Period, std::mega>::value) return "Ms";
0587   if (std::is_same<Period, std::giga>::value) return "Gs";
0588   if (std::is_same<Period, std::tera>::value) return "Ts";
0589   if (std::is_same<Period, std::peta>::value) return "Ps";
0590   if (std::is_same<Period, std::exa>::value) return "Es";
0591   if (std::is_same<Period, std::ratio<60>>::value) return "m";
0592   if (std::is_same<Period, std::ratio<3600>>::value) return "h";
0593   return nullptr;
0594 }
0595 
0596 enum class numeric_system {
0597   standard,
0598   // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
0599   alternative
0600 };
0601 
0602 // Parses a put_time-like format string and invokes handler actions.
0603 template <typename Char, typename Handler>
0604 FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
0605                                               const Char* end,
0606                                               Handler&& handler) {
0607   auto ptr = begin;
0608   while (ptr != end) {
0609     auto c = *ptr;
0610     if (c == '}') break;
0611     if (c != '%') {
0612       ++ptr;
0613       continue;
0614     }
0615     if (begin != ptr) handler.on_text(begin, ptr);
0616     ++ptr;  // consume '%'
0617     if (ptr == end) FMT_THROW(format_error("invalid format"));
0618     c = *ptr++;
0619     switch (c) {
0620     case '%':
0621       handler.on_text(ptr - 1, ptr);
0622       break;
0623     case 'n': {
0624       const Char newline[] = {'\n'};
0625       handler.on_text(newline, newline + 1);
0626       break;
0627     }
0628     case 't': {
0629       const Char tab[] = {'\t'};
0630       handler.on_text(tab, tab + 1);
0631       break;
0632     }
0633     // Year:
0634     case 'Y':
0635       handler.on_year(numeric_system::standard);
0636       break;
0637     case 'y':
0638       handler.on_short_year(numeric_system::standard);
0639       break;
0640     case 'C':
0641       handler.on_century(numeric_system::standard);
0642       break;
0643     case 'G':
0644       handler.on_iso_week_based_year();
0645       break;
0646     case 'g':
0647       handler.on_iso_week_based_short_year();
0648       break;
0649     // Day of the week:
0650     case 'a':
0651       handler.on_abbr_weekday();
0652       break;
0653     case 'A':
0654       handler.on_full_weekday();
0655       break;
0656     case 'w':
0657       handler.on_dec0_weekday(numeric_system::standard);
0658       break;
0659     case 'u':
0660       handler.on_dec1_weekday(numeric_system::standard);
0661       break;
0662     // Month:
0663     case 'b':
0664     case 'h':
0665       handler.on_abbr_month();
0666       break;
0667     case 'B':
0668       handler.on_full_month();
0669       break;
0670     case 'm':
0671       handler.on_dec_month(numeric_system::standard);
0672       break;
0673     // Day of the year/month:
0674     case 'U':
0675       handler.on_dec0_week_of_year(numeric_system::standard);
0676       break;
0677     case 'W':
0678       handler.on_dec1_week_of_year(numeric_system::standard);
0679       break;
0680     case 'V':
0681       handler.on_iso_week_of_year(numeric_system::standard);
0682       break;
0683     case 'j':
0684       handler.on_day_of_year();
0685       break;
0686     case 'd':
0687       handler.on_day_of_month(numeric_system::standard);
0688       break;
0689     case 'e':
0690       handler.on_day_of_month_space(numeric_system::standard);
0691       break;
0692     // Hour, minute, second:
0693     case 'H':
0694       handler.on_24_hour(numeric_system::standard);
0695       break;
0696     case 'I':
0697       handler.on_12_hour(numeric_system::standard);
0698       break;
0699     case 'M':
0700       handler.on_minute(numeric_system::standard);
0701       break;
0702     case 'S':
0703       handler.on_second(numeric_system::standard);
0704       break;
0705     // Other:
0706     case 'c':
0707       handler.on_datetime(numeric_system::standard);
0708       break;
0709     case 'x':
0710       handler.on_loc_date(numeric_system::standard);
0711       break;
0712     case 'X':
0713       handler.on_loc_time(numeric_system::standard);
0714       break;
0715     case 'D':
0716       handler.on_us_date();
0717       break;
0718     case 'F':
0719       handler.on_iso_date();
0720       break;
0721     case 'r':
0722       handler.on_12_hour_time();
0723       break;
0724     case 'R':
0725       handler.on_24_hour_time();
0726       break;
0727     case 'T':
0728       handler.on_iso_time();
0729       break;
0730     case 'p':
0731       handler.on_am_pm();
0732       break;
0733     case 'Q':
0734       handler.on_duration_value();
0735       break;
0736     case 'q':
0737       handler.on_duration_unit();
0738       break;
0739     case 'z':
0740       handler.on_utc_offset();
0741       break;
0742     case 'Z':
0743       handler.on_tz_name();
0744       break;
0745     // Alternative representation:
0746     case 'E': {
0747       if (ptr == end) FMT_THROW(format_error("invalid format"));
0748       c = *ptr++;
0749       switch (c) {
0750       case 'Y':
0751         handler.on_year(numeric_system::alternative);
0752         break;
0753       case 'y':
0754         handler.on_offset_year();
0755         break;
0756       case 'C':
0757         handler.on_century(numeric_system::alternative);
0758         break;
0759       case 'c':
0760         handler.on_datetime(numeric_system::alternative);
0761         break;
0762       case 'x':
0763         handler.on_loc_date(numeric_system::alternative);
0764         break;
0765       case 'X':
0766         handler.on_loc_time(numeric_system::alternative);
0767         break;
0768       default:
0769         FMT_THROW(format_error("invalid format"));
0770       }
0771       break;
0772     }
0773     case 'O':
0774       if (ptr == end) FMT_THROW(format_error("invalid format"));
0775       c = *ptr++;
0776       switch (c) {
0777       case 'y':
0778         handler.on_short_year(numeric_system::alternative);
0779         break;
0780       case 'm':
0781         handler.on_dec_month(numeric_system::alternative);
0782         break;
0783       case 'U':
0784         handler.on_dec0_week_of_year(numeric_system::alternative);
0785         break;
0786       case 'W':
0787         handler.on_dec1_week_of_year(numeric_system::alternative);
0788         break;
0789       case 'V':
0790         handler.on_iso_week_of_year(numeric_system::alternative);
0791         break;
0792       case 'd':
0793         handler.on_day_of_month(numeric_system::alternative);
0794         break;
0795       case 'e':
0796         handler.on_day_of_month_space(numeric_system::alternative);
0797         break;
0798       case 'w':
0799         handler.on_dec0_weekday(numeric_system::alternative);
0800         break;
0801       case 'u':
0802         handler.on_dec1_weekday(numeric_system::alternative);
0803         break;
0804       case 'H':
0805         handler.on_24_hour(numeric_system::alternative);
0806         break;
0807       case 'I':
0808         handler.on_12_hour(numeric_system::alternative);
0809         break;
0810       case 'M':
0811         handler.on_minute(numeric_system::alternative);
0812         break;
0813       case 'S':
0814         handler.on_second(numeric_system::alternative);
0815         break;
0816       default:
0817         FMT_THROW(format_error("invalid format"));
0818       }
0819       break;
0820     default:
0821       FMT_THROW(format_error("invalid format"));
0822     }
0823     begin = ptr;
0824   }
0825   if (begin != ptr) handler.on_text(begin, ptr);
0826   return ptr;
0827 }
0828 
0829 template <typename Derived> struct null_chrono_spec_handler {
0830   FMT_CONSTEXPR void unsupported() {
0831     static_cast<Derived*>(this)->unsupported();
0832   }
0833   FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
0834   FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
0835   FMT_CONSTEXPR void on_offset_year() { unsupported(); }
0836   FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
0837   FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
0838   FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
0839   FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
0840   FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
0841   FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
0842   FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
0843   FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
0844   FMT_CONSTEXPR void on_full_month() { unsupported(); }
0845   FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
0846   FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
0847   FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
0848   FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
0849   FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
0850   FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
0851   FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
0852   FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
0853   FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
0854   FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
0855   FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
0856   FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
0857   FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
0858   FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
0859   FMT_CONSTEXPR void on_us_date() { unsupported(); }
0860   FMT_CONSTEXPR void on_iso_date() { unsupported(); }
0861   FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
0862   FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
0863   FMT_CONSTEXPR void on_iso_time() { unsupported(); }
0864   FMT_CONSTEXPR void on_am_pm() { unsupported(); }
0865   FMT_CONSTEXPR void on_duration_value() { unsupported(); }
0866   FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
0867   FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
0868   FMT_CONSTEXPR void on_tz_name() { unsupported(); }
0869 };
0870 
0871 struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
0872   FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
0873 
0874   template <typename Char>
0875   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
0876   FMT_CONSTEXPR void on_year(numeric_system) {}
0877   FMT_CONSTEXPR void on_short_year(numeric_system) {}
0878   FMT_CONSTEXPR void on_offset_year() {}
0879   FMT_CONSTEXPR void on_century(numeric_system) {}
0880   FMT_CONSTEXPR void on_iso_week_based_year() {}
0881   FMT_CONSTEXPR void on_iso_week_based_short_year() {}
0882   FMT_CONSTEXPR void on_abbr_weekday() {}
0883   FMT_CONSTEXPR void on_full_weekday() {}
0884   FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
0885   FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
0886   FMT_CONSTEXPR void on_abbr_month() {}
0887   FMT_CONSTEXPR void on_full_month() {}
0888   FMT_CONSTEXPR void on_dec_month(numeric_system) {}
0889   FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
0890   FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
0891   FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
0892   FMT_CONSTEXPR void on_day_of_year() {}
0893   FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
0894   FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
0895   FMT_CONSTEXPR void on_24_hour(numeric_system) {}
0896   FMT_CONSTEXPR void on_12_hour(numeric_system) {}
0897   FMT_CONSTEXPR void on_minute(numeric_system) {}
0898   FMT_CONSTEXPR void on_second(numeric_system) {}
0899   FMT_CONSTEXPR void on_datetime(numeric_system) {}
0900   FMT_CONSTEXPR void on_loc_date(numeric_system) {}
0901   FMT_CONSTEXPR void on_loc_time(numeric_system) {}
0902   FMT_CONSTEXPR void on_us_date() {}
0903   FMT_CONSTEXPR void on_iso_date() {}
0904   FMT_CONSTEXPR void on_12_hour_time() {}
0905   FMT_CONSTEXPR void on_24_hour_time() {}
0906   FMT_CONSTEXPR void on_iso_time() {}
0907   FMT_CONSTEXPR void on_am_pm() {}
0908   FMT_CONSTEXPR void on_utc_offset() {}
0909   FMT_CONSTEXPR void on_tz_name() {}
0910 };
0911 
0912 inline const char* tm_wday_full_name(int wday) {
0913   static constexpr const char* full_name_list[] = {
0914       "Sunday",   "Monday", "Tuesday", "Wednesday",
0915       "Thursday", "Friday", "Saturday"};
0916   return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
0917 }
0918 inline const char* tm_wday_short_name(int wday) {
0919   static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
0920                                                     "Thu", "Fri", "Sat"};
0921   return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
0922 }
0923 
0924 inline const char* tm_mon_full_name(int mon) {
0925   static constexpr const char* full_name_list[] = {
0926       "January", "February", "March",     "April",   "May",      "June",
0927       "July",    "August",   "September", "October", "November", "December"};
0928   return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
0929 }
0930 inline const char* tm_mon_short_name(int mon) {
0931   static constexpr const char* short_name_list[] = {
0932       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
0933       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
0934   };
0935   return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
0936 }
0937 
0938 template <typename T, typename = void>
0939 struct has_member_data_tm_gmtoff : std::false_type {};
0940 template <typename T>
0941 struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
0942     : std::true_type {};
0943 
0944 template <typename T, typename = void>
0945 struct has_member_data_tm_zone : std::false_type {};
0946 template <typename T>
0947 struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
0948     : std::true_type {};
0949 
0950 #if FMT_USE_TZSET
0951 inline void tzset_once() {
0952   static bool init = []() -> bool {
0953     _tzset();
0954     return true;
0955   }();
0956   ignore_unused(init);
0957 }
0958 #endif
0959 
0960 template <typename OutputIt, typename Char> class tm_writer {
0961  private:
0962   static constexpr int days_per_week = 7;
0963 
0964   const std::locale& loc_;
0965   const bool is_classic_;
0966   OutputIt out_;
0967   const std::tm& tm_;
0968 
0969   auto tm_sec() const noexcept -> int {
0970     FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
0971     return tm_.tm_sec;
0972   }
0973   auto tm_min() const noexcept -> int {
0974     FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
0975     return tm_.tm_min;
0976   }
0977   auto tm_hour() const noexcept -> int {
0978     FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
0979     return tm_.tm_hour;
0980   }
0981   auto tm_mday() const noexcept -> int {
0982     FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
0983     return tm_.tm_mday;
0984   }
0985   auto tm_mon() const noexcept -> int {
0986     FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
0987     return tm_.tm_mon;
0988   }
0989   auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
0990   auto tm_wday() const noexcept -> int {
0991     FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
0992     return tm_.tm_wday;
0993   }
0994   auto tm_yday() const noexcept -> int {
0995     FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
0996     return tm_.tm_yday;
0997   }
0998 
0999   auto tm_hour12() const noexcept -> int {
1000     const auto h = tm_hour();
1001     const auto z = h < 12 ? h : h - 12;
1002     return z == 0 ? 12 : z;
1003   }
1004 
1005   // POSIX and the C Standard are unclear or inconsistent about what %C and %y
1006   // do if the year is negative or exceeds 9999. Use the convention that %C
1007   // concatenated with %y yields the same output as %Y, and that %Y contains at
1008   // least 4 characters, with more only if necessary.
1009   auto split_year_lower(long long year) const noexcept -> int {
1010     auto l = year % 100;
1011     if (l < 0) l = -l;  // l in [0, 99]
1012     return static_cast<int>(l);
1013   }
1014 
1015   // Algorithm:
1016   // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
1017   auto iso_year_weeks(long long curr_year) const noexcept -> int {
1018     const auto prev_year = curr_year - 1;
1019     const auto curr_p =
1020         (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
1021         days_per_week;
1022     const auto prev_p =
1023         (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
1024         days_per_week;
1025     return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
1026   }
1027   auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
1028     return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
1029            days_per_week;
1030   }
1031   auto tm_iso_week_year() const noexcept -> long long {
1032     const auto year = tm_year();
1033     const auto w = iso_week_num(tm_yday(), tm_wday());
1034     if (w < 1) return year - 1;
1035     if (w > iso_year_weeks(year)) return year + 1;
1036     return year;
1037   }
1038   auto tm_iso_week_of_year() const noexcept -> int {
1039     const auto year = tm_year();
1040     const auto w = iso_week_num(tm_yday(), tm_wday());
1041     if (w < 1) return iso_year_weeks(year - 1);
1042     if (w > iso_year_weeks(year)) return 1;
1043     return w;
1044   }
1045 
1046   void write1(int value) {
1047     *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
1048   }
1049   void write2(int value) {
1050     const char* d = digits2(to_unsigned(value) % 100);
1051     *out_++ = *d++;
1052     *out_++ = *d;
1053   }
1054 
1055   void write_year_extended(long long year) {
1056     // At least 4 characters.
1057     int width = 4;
1058     if (year < 0) {
1059       *out_++ = '-';
1060       year = 0 - year;
1061       --width;
1062     }
1063     uint32_or_64_or_128_t<long long> n = to_unsigned(year);
1064     const int num_digits = count_digits(n);
1065     if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
1066     out_ = format_decimal<Char>(out_, n, num_digits).end;
1067   }
1068   void write_year(long long year) {
1069     if (year >= 0 && year < 10000) {
1070       write2(static_cast<int>(year / 100));
1071       write2(static_cast<int>(year % 100));
1072     } else {
1073       write_year_extended(year);
1074     }
1075   }
1076 
1077   void write_utc_offset(long offset) {
1078     if (offset < 0) {
1079       *out_++ = '-';
1080       offset = -offset;
1081     } else {
1082       *out_++ = '+';
1083     }
1084     offset /= 60;
1085     write2(static_cast<int>(offset / 60));
1086     write2(static_cast<int>(offset % 60));
1087   }
1088   template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
1089   void format_utc_offset_impl(const T& tm) {
1090     write_utc_offset(tm.tm_gmtoff);
1091   }
1092   template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
1093   void format_utc_offset_impl(const T& tm) {
1094 #if defined(_WIN32) && defined(_UCRT)
1095 #  if FMT_USE_TZSET
1096     tzset_once();
1097 #  endif
1098     long offset = 0;
1099     _get_timezone(&offset);
1100     if (tm.tm_isdst) {
1101       long dstbias = 0;
1102       _get_dstbias(&dstbias);
1103       offset += dstbias;
1104     }
1105     write_utc_offset(-offset);
1106 #else
1107     ignore_unused(tm);
1108     format_localized('z');
1109 #endif
1110   }
1111 
1112   template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
1113   void format_tz_name_impl(const T& tm) {
1114     if (is_classic_)
1115       out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
1116     else
1117       format_localized('Z');
1118   }
1119   template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
1120   void format_tz_name_impl(const T&) {
1121     format_localized('Z');
1122   }
1123 
1124   void format_localized(char format, char modifier = 0) {
1125     out_ = write<Char>(out_, tm_, loc_, format, modifier);
1126   }
1127 
1128  public:
1129   tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
1130       : loc_(loc),
1131         is_classic_(loc_ == get_classic_locale()),
1132         out_(out),
1133         tm_(tm) {}
1134 
1135   OutputIt out() const { return out_; }
1136 
1137   FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
1138     out_ = copy_str<Char>(begin, end, out_);
1139   }
1140 
1141   void on_abbr_weekday() {
1142     if (is_classic_)
1143       out_ = write(out_, tm_wday_short_name(tm_wday()));
1144     else
1145       format_localized('a');
1146   }
1147   void on_full_weekday() {
1148     if (is_classic_)
1149       out_ = write(out_, tm_wday_full_name(tm_wday()));
1150     else
1151       format_localized('A');
1152   }
1153   void on_dec0_weekday(numeric_system ns) {
1154     if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
1155     format_localized('w', 'O');
1156   }
1157   void on_dec1_weekday(numeric_system ns) {
1158     if (is_classic_ || ns == numeric_system::standard) {
1159       auto wday = tm_wday();
1160       write1(wday == 0 ? days_per_week : wday);
1161     } else {
1162       format_localized('u', 'O');
1163     }
1164   }
1165 
1166   void on_abbr_month() {
1167     if (is_classic_)
1168       out_ = write(out_, tm_mon_short_name(tm_mon()));
1169     else
1170       format_localized('b');
1171   }
1172   void on_full_month() {
1173     if (is_classic_)
1174       out_ = write(out_, tm_mon_full_name(tm_mon()));
1175     else
1176       format_localized('B');
1177   }
1178 
1179   void on_datetime(numeric_system ns) {
1180     if (is_classic_) {
1181       on_abbr_weekday();
1182       *out_++ = ' ';
1183       on_abbr_month();
1184       *out_++ = ' ';
1185       on_day_of_month_space(numeric_system::standard);
1186       *out_++ = ' ';
1187       on_iso_time();
1188       *out_++ = ' ';
1189       on_year(numeric_system::standard);
1190     } else {
1191       format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
1192     }
1193   }
1194   void on_loc_date(numeric_system ns) {
1195     if (is_classic_)
1196       on_us_date();
1197     else
1198       format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
1199   }
1200   void on_loc_time(numeric_system ns) {
1201     if (is_classic_)
1202       on_iso_time();
1203     else
1204       format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
1205   }
1206   void on_us_date() {
1207     char buf[8];
1208     write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
1209                            to_unsigned(tm_mday()),
1210                            to_unsigned(split_year_lower(tm_year())), '/');
1211     out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
1212   }
1213   void on_iso_date() {
1214     auto year = tm_year();
1215     char buf[10];
1216     size_t offset = 0;
1217     if (year >= 0 && year < 10000) {
1218       copy2(buf, digits2(static_cast<size_t>(year / 100)));
1219     } else {
1220       offset = 4;
1221       write_year_extended(year);
1222       year = 0;
1223     }
1224     write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
1225                            to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1226                            '-');
1227     out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
1228   }
1229 
1230   void on_utc_offset() { format_utc_offset_impl(tm_); }
1231   void on_tz_name() { format_tz_name_impl(tm_); }
1232 
1233   void on_year(numeric_system ns) {
1234     if (is_classic_ || ns == numeric_system::standard)
1235       return write_year(tm_year());
1236     format_localized('Y', 'E');
1237   }
1238   void on_short_year(numeric_system ns) {
1239     if (is_classic_ || ns == numeric_system::standard)
1240       return write2(split_year_lower(tm_year()));
1241     format_localized('y', 'O');
1242   }
1243   void on_offset_year() {
1244     if (is_classic_) return write2(split_year_lower(tm_year()));
1245     format_localized('y', 'E');
1246   }
1247 
1248   void on_century(numeric_system ns) {
1249     if (is_classic_ || ns == numeric_system::standard) {
1250       auto year = tm_year();
1251       auto upper = year / 100;
1252       if (year >= -99 && year < 0) {
1253         // Zero upper on negative year.
1254         *out_++ = '-';
1255         *out_++ = '0';
1256       } else if (upper >= 0 && upper < 100) {
1257         write2(static_cast<int>(upper));
1258       } else {
1259         out_ = write<Char>(out_, upper);
1260       }
1261     } else {
1262       format_localized('C', 'E');
1263     }
1264   }
1265 
1266   void on_dec_month(numeric_system ns) {
1267     if (is_classic_ || ns == numeric_system::standard)
1268       return write2(tm_mon() + 1);
1269     format_localized('m', 'O');
1270   }
1271 
1272   void on_dec0_week_of_year(numeric_system ns) {
1273     if (is_classic_ || ns == numeric_system::standard)
1274       return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
1275     format_localized('U', 'O');
1276   }
1277   void on_dec1_week_of_year(numeric_system ns) {
1278     if (is_classic_ || ns == numeric_system::standard) {
1279       auto wday = tm_wday();
1280       write2((tm_yday() + days_per_week -
1281               (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
1282              days_per_week);
1283     } else {
1284       format_localized('W', 'O');
1285     }
1286   }
1287   void on_iso_week_of_year(numeric_system ns) {
1288     if (is_classic_ || ns == numeric_system::standard)
1289       return write2(tm_iso_week_of_year());
1290     format_localized('V', 'O');
1291   }
1292 
1293   void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
1294   void on_iso_week_based_short_year() {
1295     write2(split_year_lower(tm_iso_week_year()));
1296   }
1297 
1298   void on_day_of_year() {
1299     auto yday = tm_yday() + 1;
1300     write1(yday / 100);
1301     write2(yday % 100);
1302   }
1303   void on_day_of_month(numeric_system ns) {
1304     if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
1305     format_localized('d', 'O');
1306   }
1307   void on_day_of_month_space(numeric_system ns) {
1308     if (is_classic_ || ns == numeric_system::standard) {
1309       auto mday = to_unsigned(tm_mday()) % 100;
1310       const char* d2 = digits2(mday);
1311       *out_++ = mday < 10 ? ' ' : d2[0];
1312       *out_++ = d2[1];
1313     } else {
1314       format_localized('e', 'O');
1315     }
1316   }
1317 
1318   void on_24_hour(numeric_system ns) {
1319     if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
1320     format_localized('H', 'O');
1321   }
1322   void on_12_hour(numeric_system ns) {
1323     if (is_classic_ || ns == numeric_system::standard)
1324       return write2(tm_hour12());
1325     format_localized('I', 'O');
1326   }
1327   void on_minute(numeric_system ns) {
1328     if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
1329     format_localized('M', 'O');
1330   }
1331   void on_second(numeric_system ns) {
1332     if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
1333     format_localized('S', 'O');
1334   }
1335 
1336   void on_12_hour_time() {
1337     if (is_classic_) {
1338       char buf[8];
1339       write_digit2_separated(buf, to_unsigned(tm_hour12()),
1340                              to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
1341       out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
1342       *out_++ = ' ';
1343       on_am_pm();
1344     } else {
1345       format_localized('r');
1346     }
1347   }
1348   void on_24_hour_time() {
1349     write2(tm_hour());
1350     *out_++ = ':';
1351     write2(tm_min());
1352   }
1353   void on_iso_time() {
1354     char buf[8];
1355     write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
1356                            to_unsigned(tm_sec()), ':');
1357     out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
1358   }
1359 
1360   void on_am_pm() {
1361     if (is_classic_) {
1362       *out_++ = tm_hour() < 12 ? 'A' : 'P';
1363       *out_++ = 'M';
1364     } else {
1365       format_localized('p');
1366     }
1367   }
1368 
1369   // These apply to chrono durations but not tm.
1370   void on_duration_value() {}
1371   void on_duration_unit() {}
1372 };
1373 
1374 struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
1375   FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
1376 
1377   template <typename Char>
1378   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
1379   FMT_CONSTEXPR void on_24_hour(numeric_system) {}
1380   FMT_CONSTEXPR void on_12_hour(numeric_system) {}
1381   FMT_CONSTEXPR void on_minute(numeric_system) {}
1382   FMT_CONSTEXPR void on_second(numeric_system) {}
1383   FMT_CONSTEXPR void on_12_hour_time() {}
1384   FMT_CONSTEXPR void on_24_hour_time() {}
1385   FMT_CONSTEXPR void on_iso_time() {}
1386   FMT_CONSTEXPR void on_am_pm() {}
1387   FMT_CONSTEXPR void on_duration_value() {}
1388   FMT_CONSTEXPR void on_duration_unit() {}
1389 };
1390 
1391 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
1392 inline bool isfinite(T) {
1393   return true;
1394 }
1395 
1396 // Converts value to Int and checks that it's in the range [0, upper).
1397 template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
1398 inline Int to_nonnegative_int(T value, Int upper) {
1399   FMT_ASSERT(std::is_unsigned<Int>::value ||
1400              (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
1401              "invalid value");
1402   (void)upper;
1403   return static_cast<Int>(value);
1404 }
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   if (value < 0 || value > static_cast<T>(upper))
1408     FMT_THROW(format_error("invalid value"));
1409   return static_cast<Int>(value);
1410 }
1411 
1412 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
1413 inline T mod(T x, int y) {
1414   return x % static_cast<T>(y);
1415 }
1416 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
1417 inline T mod(T x, int y) {
1418   return std::fmod(x, static_cast<T>(y));
1419 }
1420 
1421 // If T is an integral type, maps T to its unsigned counterpart, otherwise
1422 // leaves it unchanged (unlike std::make_unsigned).
1423 template <typename T, bool INTEGRAL = std::is_integral<T>::value>
1424 struct make_unsigned_or_unchanged {
1425   using type = T;
1426 };
1427 
1428 template <typename T> struct make_unsigned_or_unchanged<T, true> {
1429   using type = typename std::make_unsigned<T>::type;
1430 };
1431 
1432 #if FMT_SAFE_DURATION_CAST
1433 // throwing version of safe_duration_cast
1434 template <typename To, typename FromRep, typename FromPeriod>
1435 To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
1436   int ec;
1437   To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
1438   if (ec) FMT_THROW(format_error("cannot format duration"));
1439   return to;
1440 }
1441 #endif
1442 
1443 template <typename Rep, typename Period,
1444           FMT_ENABLE_IF(std::is_integral<Rep>::value)>
1445 inline std::chrono::duration<Rep, std::milli> get_milliseconds(
1446     std::chrono::duration<Rep, Period> d) {
1447   // this may overflow and/or the result may not fit in the
1448   // target type.
1449 #if FMT_SAFE_DURATION_CAST
1450   using CommonSecondsType =
1451       typename std::common_type<decltype(d), std::chrono::seconds>::type;
1452   const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
1453   const auto d_as_whole_seconds =
1454       fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
1455   // this conversion should be nonproblematic
1456   const auto diff = d_as_common - d_as_whole_seconds;
1457   const auto ms =
1458       fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
1459   return ms;
1460 #else
1461   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
1462   return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
1463 #endif
1464 }
1465 
1466 // Counts the number of fractional digits in the range [0, 18] according to the
1467 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
1468 // microseconds precision.
1469 template <long long Num, long long Den, int N = 0,
1470           bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
1471 struct count_fractional_digits {
1472   static constexpr int value =
1473       Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
1474 };
1475 
1476 // Base case that doesn't instantiate any more templates
1477 // in order to avoid overflow.
1478 template <long long Num, long long Den, int N>
1479 struct count_fractional_digits<Num, Den, N, false> {
1480   static constexpr int value = (Num % Den == 0) ? N : 6;
1481 };
1482 
1483 constexpr long long pow10(std::uint32_t n) {
1484   return n == 0 ? 1 : 10 * pow10(n - 1);
1485 }
1486 
1487 template <class Rep, class Period,
1488           FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
1489 constexpr std::chrono::duration<Rep, Period> abs(
1490     std::chrono::duration<Rep, Period> d) {
1491   // We need to compare the duration using the count() method directly
1492   // due to a compiler bug in clang-11 regarding the spaceship operator,
1493   // when -Wzero-as-null-pointer-constant is enabled.
1494   // In clang-12 the bug has been fixed. See
1495   // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
1496   // https://www.godbolt.org/z/Knbb5joYx.
1497   return d.count() >= d.zero().count() ? d : -d;
1498 }
1499 
1500 template <class Rep, class Period,
1501           FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
1502 constexpr std::chrono::duration<Rep, Period> abs(
1503     std::chrono::duration<Rep, Period> d) {
1504   return d;
1505 }
1506 
1507 template <typename Char, typename Rep, typename OutputIt,
1508           FMT_ENABLE_IF(std::is_integral<Rep>::value)>
1509 OutputIt format_duration_value(OutputIt out, Rep val, int) {
1510   return write<Char>(out, val);
1511 }
1512 
1513 template <typename Char, typename Rep, typename OutputIt,
1514           FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
1515 OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
1516   auto specs = basic_format_specs<Char>();
1517   specs.precision = precision;
1518   specs.type = precision >= 0 ? presentation_type::fixed_lower
1519                               : presentation_type::general_lower;
1520   return write<Char>(out, val, specs);
1521 }
1522 
1523 template <typename Char, typename OutputIt>
1524 OutputIt copy_unit(string_view unit, OutputIt out, Char) {
1525   return std::copy(unit.begin(), unit.end(), out);
1526 }
1527 
1528 template <typename OutputIt>
1529 OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
1530   // This works when wchar_t is UTF-32 because units only contain characters
1531   // that have the same representation in UTF-16 and UTF-32.
1532   utf8_to_utf16 u(unit);
1533   return std::copy(u.c_str(), u.c_str() + u.size(), out);
1534 }
1535 
1536 template <typename Char, typename Period, typename OutputIt>
1537 OutputIt format_duration_unit(OutputIt out) {
1538   if (const char* unit = get_units<Period>())
1539     return copy_unit(string_view(unit), out, Char());
1540   *out++ = '[';
1541   out = write<Char>(out, Period::num);
1542   if (const_check(Period::den != 1)) {
1543     *out++ = '/';
1544     out = write<Char>(out, Period::den);
1545   }
1546   *out++ = ']';
1547   *out++ = 's';
1548   return out;
1549 }
1550 
1551 class get_locale {
1552  private:
1553   union {
1554     std::locale locale_;
1555   };
1556   bool has_locale_ = false;
1557 
1558  public:
1559   get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
1560     if (localized)
1561       ::new (&locale_) std::locale(loc.template get<std::locale>());
1562   }
1563   ~get_locale() {
1564     if (has_locale_) locale_.~locale();
1565   }
1566   operator const std::locale&() const {
1567     return has_locale_ ? locale_ : get_classic_locale();
1568   }
1569 };
1570 
1571 template <typename FormatContext, typename OutputIt, typename Rep,
1572           typename Period>
1573 struct chrono_formatter {
1574   FormatContext& context;
1575   OutputIt out;
1576   int precision;
1577   bool localized = false;
1578   // rep is unsigned to avoid overflow.
1579   using rep =
1580       conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
1581                     unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
1582   rep val;
1583   using seconds = std::chrono::duration<rep>;
1584   seconds s;
1585   using milliseconds = std::chrono::duration<rep, std::milli>;
1586   bool negative;
1587 
1588   using char_type = typename FormatContext::char_type;
1589   using tm_writer_type = tm_writer<OutputIt, char_type>;
1590 
1591   chrono_formatter(FormatContext& ctx, OutputIt o,
1592                    std::chrono::duration<Rep, Period> d)
1593       : context(ctx),
1594         out(o),
1595         val(static_cast<rep>(d.count())),
1596         negative(false) {
1597     if (d.count() < 0) {
1598       val = 0 - val;
1599       negative = true;
1600     }
1601 
1602     // this may overflow and/or the result may not fit in the
1603     // target type.
1604 #if FMT_SAFE_DURATION_CAST
1605     // might need checked conversion (rep!=Rep)
1606     auto tmpval = std::chrono::duration<rep, Period>(val);
1607     s = fmt_safe_duration_cast<seconds>(tmpval);
1608 #else
1609     s = std::chrono::duration_cast<seconds>(
1610         std::chrono::duration<rep, Period>(val));
1611 #endif
1612   }
1613 
1614   // returns true if nan or inf, writes to out.
1615   bool handle_nan_inf() {
1616     if (isfinite(val)) {
1617       return false;
1618     }
1619     if (isnan(val)) {
1620       write_nan();
1621       return true;
1622     }
1623     // must be +-inf
1624     if (val > 0) {
1625       write_pinf();
1626     } else {
1627       write_ninf();
1628     }
1629     return true;
1630   }
1631 
1632   Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
1633 
1634   Rep hour12() const {
1635     Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
1636     return hour <= 0 ? 12 : hour;
1637   }
1638 
1639   Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
1640   Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
1641 
1642   std::tm time() const {
1643     auto time = std::tm();
1644     time.tm_hour = to_nonnegative_int(hour(), 24);
1645     time.tm_min = to_nonnegative_int(minute(), 60);
1646     time.tm_sec = to_nonnegative_int(second(), 60);
1647     return time;
1648   }
1649 
1650   void write_sign() {
1651     if (negative) {
1652       *out++ = '-';
1653       negative = false;
1654     }
1655   }
1656 
1657   void write(Rep value, int width) {
1658     write_sign();
1659     if (isnan(value)) return write_nan();
1660     uint32_or_64_or_128_t<int> n =
1661         to_unsigned(to_nonnegative_int(value, max_value<int>()));
1662     int num_digits = detail::count_digits(n);
1663     if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
1664     out = format_decimal<char_type>(out, n, num_digits).end;
1665   }
1666 
1667   template <typename Duration> void write_fractional_seconds(Duration d) {
1668     FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
1669     constexpr auto num_fractional_digits =
1670         count_fractional_digits<Duration::period::num,
1671                                 Duration::period::den>::value;
1672 
1673     using subsecond_precision = std::chrono::duration<
1674         typename std::common_type<typename Duration::rep,
1675                                   std::chrono::seconds::rep>::type,
1676         std::ratio<1, detail::pow10(num_fractional_digits)>>;
1677     if (std::ratio_less<typename subsecond_precision::period,
1678                         std::chrono::seconds::period>::value) {
1679       *out++ = '.';
1680       auto fractional =
1681           detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
1682       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       if (std::is_floating_point<rep>::value) {
1774         constexpr auto num_fractional_digits =
1775             count_fractional_digits<Period::num, Period::den>::value;
1776         auto buf = memory_buffer();
1777         format_to(std::back_inserter(buf), runtime("{:.{}f}"),
1778                   std::fmod(val * static_cast<rep>(Period::num) /
1779                                 static_cast<rep>(Period::den),
1780                             static_cast<rep>(60)),
1781                   num_fractional_digits);
1782         if (negative) *out++ = '-';
1783         if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
1784         out = std::copy(buf.begin(), buf.end(), out);
1785       } else {
1786         write(second(), 2);
1787         write_fractional_seconds(std::chrono::duration<rep, Period>(val));
1788       }
1789       return;
1790     }
1791     auto time = tm();
1792     time.tm_sec = to_nonnegative_int(second(), 60);
1793     format_tm(time, &tm_writer_type::on_second, ns);
1794   }
1795 
1796   void on_12_hour_time() {
1797     if (handle_nan_inf()) return;
1798     format_tm(time(), &tm_writer_type::on_12_hour_time);
1799   }
1800 
1801   void on_24_hour_time() {
1802     if (handle_nan_inf()) {
1803       *out++ = ':';
1804       handle_nan_inf();
1805       return;
1806     }
1807 
1808     write(hour(), 2);
1809     *out++ = ':';
1810     write(minute(), 2);
1811   }
1812 
1813   void on_iso_time() {
1814     on_24_hour_time();
1815     *out++ = ':';
1816     if (handle_nan_inf()) return;
1817     on_second(numeric_system::standard);
1818   }
1819 
1820   void on_am_pm() {
1821     if (handle_nan_inf()) return;
1822     format_tm(time(), &tm_writer_type::on_am_pm);
1823   }
1824 
1825   void on_duration_value() {
1826     if (handle_nan_inf()) return;
1827     write_sign();
1828     out = format_duration_value<char_type>(out, val, precision);
1829   }
1830 
1831   void on_duration_unit() {
1832     out = format_duration_unit<char_type, Period>(out);
1833   }
1834 };
1835 
1836 FMT_END_DETAIL_NAMESPACE
1837 
1838 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
1839 using weekday = std::chrono::weekday;
1840 #else
1841 // A fallback version of weekday.
1842 class weekday {
1843  private:
1844   unsigned char value;
1845 
1846  public:
1847   weekday() = default;
1848   explicit constexpr weekday(unsigned wd) noexcept
1849       : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
1850   constexpr unsigned c_encoding() const noexcept { return value; }
1851 };
1852 
1853 class year_month_day {};
1854 #endif
1855 
1856 // A rudimentary weekday formatter.
1857 template <typename Char> struct formatter<weekday, Char> {
1858  private:
1859   bool localized = false;
1860 
1861  public:
1862   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
1863       -> decltype(ctx.begin()) {
1864     auto begin = ctx.begin(), end = ctx.end();
1865     if (begin != end && *begin == 'L') {
1866       ++begin;
1867       localized = true;
1868     }
1869     return begin;
1870   }
1871 
1872   template <typename FormatContext>
1873   auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
1874     auto time = std::tm();
1875     time.tm_wday = static_cast<int>(wd.c_encoding());
1876     detail::get_locale loc(localized, ctx.locale());
1877     auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
1878     w.on_abbr_weekday();
1879     return w.out();
1880   }
1881 };
1882 
1883 template <typename Rep, typename Period, typename Char>
1884 struct formatter<std::chrono::duration<Rep, Period>, Char> {
1885  private:
1886   basic_format_specs<Char> specs;
1887   int precision = -1;
1888   using arg_ref_type = detail::arg_ref<Char>;
1889   arg_ref_type width_ref;
1890   arg_ref_type precision_ref;
1891   bool localized = false;
1892   basic_string_view<Char> format_str;
1893   using duration = std::chrono::duration<Rep, Period>;
1894 
1895   struct spec_handler {
1896     formatter& f;
1897     basic_format_parse_context<Char>& context;
1898     basic_string_view<Char> format_str;
1899 
1900     template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
1901       context.check_arg_id(arg_id);
1902       return arg_ref_type(arg_id);
1903     }
1904 
1905     FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
1906       context.check_arg_id(arg_id);
1907       return arg_ref_type(arg_id);
1908     }
1909 
1910     FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
1911       return arg_ref_type(context.next_arg_id());
1912     }
1913 
1914     void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
1915     FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
1916       f.specs.fill = fill;
1917     }
1918     FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
1919     FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
1920     FMT_CONSTEXPR void on_precision(int _precision) {
1921       f.precision = _precision;
1922     }
1923     FMT_CONSTEXPR void end_precision() {}
1924 
1925     template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
1926       f.width_ref = make_arg_ref(arg_id);
1927     }
1928 
1929     template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
1930       f.precision_ref = make_arg_ref(arg_id);
1931     }
1932   };
1933 
1934   using iterator = typename basic_format_parse_context<Char>::iterator;
1935   struct parse_range {
1936     iterator begin;
1937     iterator end;
1938   };
1939 
1940   FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
1941     auto begin = ctx.begin(), end = ctx.end();
1942     if (begin == end || *begin == '}') return {begin, begin};
1943     spec_handler handler{*this, ctx, format_str};
1944     begin = detail::parse_align(begin, end, handler);
1945     if (begin == end) return {begin, begin};
1946     begin = detail::parse_width(begin, end, handler);
1947     if (begin == end) return {begin, begin};
1948     if (*begin == '.') {
1949       if (std::is_floating_point<Rep>::value)
1950         begin = detail::parse_precision(begin, end, handler);
1951       else
1952         handler.on_error("precision not allowed for this argument type");
1953     }
1954     if (begin != end && *begin == 'L') {
1955       ++begin;
1956       localized = true;
1957     }
1958     end = detail::parse_chrono_format(begin, end,
1959                                       detail::chrono_format_checker());
1960     return {begin, end};
1961   }
1962 
1963  public:
1964   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
1965       -> decltype(ctx.begin()) {
1966     auto range = do_parse(ctx);
1967     format_str = basic_string_view<Char>(
1968         &*range.begin, detail::to_unsigned(range.end - range.begin));
1969     return range.end;
1970   }
1971 
1972   template <typename FormatContext>
1973   auto format(const duration& d, FormatContext& ctx) const
1974       -> decltype(ctx.out()) {
1975     auto specs_copy = specs;
1976     auto precision_copy = precision;
1977     auto begin = format_str.begin(), end = format_str.end();
1978     // As a possible future optimization, we could avoid extra copying if width
1979     // is not specified.
1980     basic_memory_buffer<Char> buf;
1981     auto out = std::back_inserter(buf);
1982     detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
1983                                                        width_ref, ctx);
1984     detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
1985                                                            precision_ref, ctx);
1986     if (begin == end || *begin == '}') {
1987       out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
1988       detail::format_duration_unit<Char, Period>(out);
1989     } else {
1990       detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
1991           ctx, out, d);
1992       f.precision = precision_copy;
1993       f.localized = localized;
1994       detail::parse_chrono_format(begin, end, f);
1995     }
1996     return detail::write(
1997         ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
1998   }
1999 };
2000 
2001 template <typename Char, typename Duration>
2002 struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
2003                  Char> : formatter<std::tm, Char> {
2004   FMT_CONSTEXPR formatter() {
2005     basic_string_view<Char> default_specs =
2006         detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
2007     this->do_parse(default_specs.begin(), default_specs.end());
2008   }
2009 
2010   template <typename FormatContext>
2011   auto format(std::chrono::time_point<std::chrono::system_clock> val,
2012               FormatContext& ctx) const -> decltype(ctx.out()) {
2013     return formatter<std::tm, Char>::format(localtime(val), ctx);
2014   }
2015 };
2016 
2017 template <typename Char> struct formatter<std::tm, Char> {
2018  private:
2019   enum class spec {
2020     unknown,
2021     year_month_day,
2022     hh_mm_ss,
2023   };
2024   spec spec_ = spec::unknown;
2025   basic_string_view<Char> specs;
2026 
2027  protected:
2028   template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
2029     if (begin != end && *begin == ':') ++begin;
2030     end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
2031     // Replace default spec only if the new spec is not empty.
2032     if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
2033     return end;
2034   }
2035 
2036  public:
2037   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
2038       -> decltype(ctx.begin()) {
2039     auto end = this->do_parse(ctx.begin(), ctx.end());
2040     // basic_string_view<>::compare isn't constexpr before C++17.
2041     if (specs.size() == 2 && specs[0] == Char('%')) {
2042       if (specs[1] == Char('F'))
2043         spec_ = spec::year_month_day;
2044       else if (specs[1] == Char('T'))
2045         spec_ = spec::hh_mm_ss;
2046     }
2047     return end;
2048   }
2049 
2050   template <typename FormatContext>
2051   auto format(const std::tm& tm, FormatContext& ctx) const
2052       -> decltype(ctx.out()) {
2053     const auto loc_ref = ctx.locale();
2054     detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
2055     auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
2056     if (spec_ == spec::year_month_day)
2057       w.on_iso_date();
2058     else if (spec_ == spec::hh_mm_ss)
2059       w.on_iso_time();
2060     else
2061       detail::parse_chrono_format(specs.begin(), specs.end(), w);
2062     return w.out();
2063   }
2064 };
2065 
2066 FMT_MODULE_EXPORT_END
2067 FMT_END_NAMESPACE
2068 
2069 #endif  // FMT_CHRONO_H_