You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2069 lines
66 KiB

  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. #ifndef FMT_CHRONO_H_
  8. #define FMT_CHRONO_H_
  9. #include <algorithm>
  10. #include <chrono>
  11. #include <cmath> // std::isfinite
  12. #include <cstring> // std::memcpy
  13. #include <ctime>
  14. #include <iterator>
  15. #include <locale>
  16. #include <ostream>
  17. #include <type_traits>
  18. #include "format.h"
  19. FMT_BEGIN_NAMESPACE
  20. // Enable tzset.
  21. #ifndef FMT_USE_TZSET
  22. // UWP doesn't provide _tzset.
  23. # if FMT_HAS_INCLUDE("winapifamily.h")
  24. # include <winapifamily.h>
  25. # endif
  26. # if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
  27. (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
  28. # define FMT_USE_TZSET 1
  29. # else
  30. # define FMT_USE_TZSET 0
  31. # endif
  32. #endif
  33. // Enable safe chrono durations, unless explicitly disabled.
  34. #ifndef FMT_SAFE_DURATION_CAST
  35. # define FMT_SAFE_DURATION_CAST 1
  36. #endif
  37. #if FMT_SAFE_DURATION_CAST
  38. // For conversion between std::chrono::durations without undefined
  39. // behaviour or erroneous results.
  40. // This is a stripped down version of duration_cast, for inclusion in fmt.
  41. // See https://github.com/pauldreik/safe_duration_cast
  42. //
  43. // Copyright Paul Dreik 2019
  44. namespace safe_duration_cast {
  45. template <typename To, typename From,
  46. FMT_ENABLE_IF(!std::is_same<From, To>::value &&
  47. std::numeric_limits<From>::is_signed ==
  48. std::numeric_limits<To>::is_signed)>
  49. FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
  50. ec = 0;
  51. using F = std::numeric_limits<From>;
  52. using T = std::numeric_limits<To>;
  53. static_assert(F::is_integer, "From must be integral");
  54. static_assert(T::is_integer, "To must be integral");
  55. // A and B are both signed, or both unsigned.
  56. if (detail::const_check(F::digits <= T::digits)) {
  57. // From fits in To without any problem.
  58. } else {
  59. // From does not always fit in To, resort to a dynamic check.
  60. if (from < (T::min)() || from > (T::max)()) {
  61. // outside range.
  62. ec = 1;
  63. return {};
  64. }
  65. }
  66. return static_cast<To>(from);
  67. }
  68. /**
  69. * converts From to To, without loss. If the dynamic value of from
  70. * can't be converted to To without loss, ec is set.
  71. */
  72. template <typename To, typename From,
  73. FMT_ENABLE_IF(!std::is_same<From, To>::value &&
  74. std::numeric_limits<From>::is_signed !=
  75. std::numeric_limits<To>::is_signed)>
  76. FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
  77. ec = 0;
  78. using F = std::numeric_limits<From>;
  79. using T = std::numeric_limits<To>;
  80. static_assert(F::is_integer, "From must be integral");
  81. static_assert(T::is_integer, "To must be integral");
  82. if (detail::const_check(F::is_signed && !T::is_signed)) {
  83. // From may be negative, not allowed!
  84. if (fmt::detail::is_negative(from)) {
  85. ec = 1;
  86. return {};
  87. }
  88. // From is positive. Can it always fit in To?
  89. if (detail::const_check(F::digits > T::digits) &&
  90. from > static_cast<From>(detail::max_value<To>())) {
  91. ec = 1;
  92. return {};
  93. }
  94. }
  95. if (detail::const_check(!F::is_signed && T::is_signed &&
  96. F::digits >= T::digits) &&
  97. from > static_cast<From>(detail::max_value<To>())) {
  98. ec = 1;
  99. return {};
  100. }
  101. return static_cast<To>(from); // Lossless conversion.
  102. }
  103. template <typename To, typename From,
  104. FMT_ENABLE_IF(std::is_same<From, To>::value)>
  105. FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
  106. ec = 0;
  107. return from;
  108. } // function
  109. // clang-format off
  110. /**
  111. * converts From to To if possible, otherwise ec is set.
  112. *
  113. * input | output
  114. * ---------------------------------|---------------
  115. * NaN | NaN
  116. * Inf | Inf
  117. * normal, fits in output | converted (possibly lossy)
  118. * normal, does not fit in output | ec is set
  119. * subnormal | best effort
  120. * -Inf | -Inf
  121. */
  122. // clang-format on
  123. template <typename To, typename From,
  124. FMT_ENABLE_IF(!std::is_same<From, To>::value)>
  125. FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
  126. ec = 0;
  127. using T = std::numeric_limits<To>;
  128. static_assert(std::is_floating_point<From>::value, "From must be floating");
  129. static_assert(std::is_floating_point<To>::value, "To must be floating");
  130. // catch the only happy case
  131. if (std::isfinite(from)) {
  132. if (from >= T::lowest() && from <= (T::max)()) {
  133. return static_cast<To>(from);
  134. }
  135. // not within range.
  136. ec = 1;
  137. return {};
  138. }
  139. // nan and inf will be preserved
  140. return static_cast<To>(from);
  141. } // function
  142. template <typename To, typename From,
  143. FMT_ENABLE_IF(std::is_same<From, To>::value)>
  144. FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
  145. ec = 0;
  146. static_assert(std::is_floating_point<From>::value, "From must be floating");
  147. return from;
  148. }
  149. /**
  150. * safe duration cast between integral durations
  151. */
  152. template <typename To, typename FromRep, typename FromPeriod,
  153. FMT_ENABLE_IF(std::is_integral<FromRep>::value),
  154. FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
  155. To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
  156. int& ec) {
  157. using From = std::chrono::duration<FromRep, FromPeriod>;
  158. ec = 0;
  159. // the basic idea is that we need to convert from count() in the from type
  160. // to count() in the To type, by multiplying it with this:
  161. struct Factor
  162. : std::ratio_divide<typename From::period, typename To::period> {};
  163. static_assert(Factor::num > 0, "num must be positive");
  164. static_assert(Factor::den > 0, "den must be positive");
  165. // the conversion is like this: multiply from.count() with Factor::num
  166. // /Factor::den and convert it to To::rep, all this without
  167. // overflow/underflow. let's start by finding a suitable type that can hold
  168. // both To, From and Factor::num
  169. using IntermediateRep =
  170. typename std::common_type<typename From::rep, typename To::rep,
  171. decltype(Factor::num)>::type;
  172. // safe conversion to IntermediateRep
  173. IntermediateRep count =
  174. lossless_integral_conversion<IntermediateRep>(from.count(), ec);
  175. if (ec) return {};
  176. // multiply with Factor::num without overflow or underflow
  177. if (detail::const_check(Factor::num != 1)) {
  178. const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
  179. if (count > max1) {
  180. ec = 1;
  181. return {};
  182. }
  183. const auto min1 =
  184. (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
  185. if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
  186. ec = 1;
  187. return {};
  188. }
  189. count *= Factor::num;
  190. }
  191. if (detail::const_check(Factor::den != 1)) count /= Factor::den;
  192. auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
  193. return ec ? To() : To(tocount);
  194. }
  195. /**
  196. * safe duration_cast between floating point durations
  197. */
  198. template <typename To, typename FromRep, typename FromPeriod,
  199. FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
  200. FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
  201. To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
  202. int& ec) {
  203. using From = std::chrono::duration<FromRep, FromPeriod>;
  204. ec = 0;
  205. if (std::isnan(from.count())) {
  206. // nan in, gives nan out. easy.
  207. return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
  208. }
  209. // maybe we should also check if from is denormal, and decide what to do about
  210. // it.
  211. // +-inf should be preserved.
  212. if (std::isinf(from.count())) {
  213. return To{from.count()};
  214. }
  215. // the basic idea is that we need to convert from count() in the from type
  216. // to count() in the To type, by multiplying it with this:
  217. struct Factor
  218. : std::ratio_divide<typename From::period, typename To::period> {};
  219. static_assert(Factor::num > 0, "num must be positive");
  220. static_assert(Factor::den > 0, "den must be positive");
  221. // the conversion is like this: multiply from.count() with Factor::num
  222. // /Factor::den and convert it to To::rep, all this without
  223. // overflow/underflow. let's start by finding a suitable type that can hold
  224. // both To, From and Factor::num
  225. using IntermediateRep =
  226. typename std::common_type<typename From::rep, typename To::rep,
  227. decltype(Factor::num)>::type;
  228. // force conversion of From::rep -> IntermediateRep to be safe,
  229. // even if it will never happen be narrowing in this context.
  230. IntermediateRep count =
  231. safe_float_conversion<IntermediateRep>(from.count(), ec);
  232. if (ec) {
  233. return {};
  234. }
  235. // multiply with Factor::num without overflow or underflow
  236. if (detail::const_check(Factor::num != 1)) {
  237. constexpr auto max1 = detail::max_value<IntermediateRep>() /
  238. static_cast<IntermediateRep>(Factor::num);
  239. if (count > max1) {
  240. ec = 1;
  241. return {};
  242. }
  243. constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
  244. static_cast<IntermediateRep>(Factor::num);
  245. if (count < min1) {
  246. ec = 1;
  247. return {};
  248. }
  249. count *= static_cast<IntermediateRep>(Factor::num);
  250. }
  251. // this can't go wrong, right? den>0 is checked earlier.
  252. if (detail::const_check(Factor::den != 1)) {
  253. using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
  254. count /= static_cast<common_t>(Factor::den);
  255. }
  256. // convert to the to type, safely
  257. using ToRep = typename To::rep;
  258. const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
  259. if (ec) {
  260. return {};
  261. }
  262. return To{tocount};
  263. }
  264. } // namespace safe_duration_cast
  265. #endif
  266. // Prevents expansion of a preceding token as a function-style macro.
  267. // Usage: f FMT_NOMACRO()
  268. #define FMT_NOMACRO
  269. namespace detail {
  270. template <typename T = void> struct null {};
  271. inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
  272. inline null<> localtime_s(...) { return null<>(); }
  273. inline null<> gmtime_r(...) { return null<>(); }
  274. inline null<> gmtime_s(...) { return null<>(); }
  275. inline const std::locale& get_classic_locale() {
  276. static const auto& locale = std::locale::classic();
  277. return locale;
  278. }
  279. template <typename CodeUnit> struct codecvt_result {
  280. static constexpr const size_t max_size = 32;
  281. CodeUnit buf[max_size];
  282. CodeUnit* end;
  283. };
  284. template <typename CodeUnit>
  285. constexpr const size_t codecvt_result<CodeUnit>::max_size;
  286. template <typename CodeUnit>
  287. void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
  288. const std::locale& loc) {
  289. #if FMT_CLANG_VERSION
  290. # pragma clang diagnostic push
  291. # pragma clang diagnostic ignored "-Wdeprecated"
  292. auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
  293. # pragma clang diagnostic pop
  294. #else
  295. auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
  296. #endif
  297. auto mb = std::mbstate_t();
  298. const char* from_next = nullptr;
  299. auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
  300. std::begin(out.buf), std::end(out.buf), out.end);
  301. if (result != std::codecvt_base::ok)
  302. FMT_THROW(format_error("failed to format time"));
  303. }
  304. template <typename OutputIt>
  305. auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
  306. -> OutputIt {
  307. if (detail::is_utf8() && loc != get_classic_locale()) {
  308. // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
  309. // gcc-4.
  310. #if FMT_MSC_VERSION != 0 || \
  311. (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
  312. // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
  313. // and newer.
  314. using code_unit = wchar_t;
  315. #else
  316. using code_unit = char32_t;
  317. #endif
  318. using unit_t = codecvt_result<code_unit>;
  319. unit_t unit;
  320. write_codecvt(unit, in, loc);
  321. // In UTF-8 is used one to four one-byte code units.
  322. auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>();
  323. for (code_unit* p = unit.buf; p != unit.end; ++p) {
  324. uint32_t c = static_cast<uint32_t>(*p);
  325. if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
  326. // surrogate pair
  327. ++p;
  328. if (p == unit.end || (c & 0xfc00) != 0xd800 ||
  329. (*p & 0xfc00) != 0xdc00) {
  330. FMT_THROW(format_error("failed to format time"));
  331. }
  332. c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
  333. }
  334. if (c < 0x80) {
  335. buf.push_back(static_cast<char>(c));
  336. } else if (c < 0x800) {
  337. buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
  338. buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
  339. } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
  340. buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
  341. buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
  342. buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
  343. } else if (c >= 0x10000 && c <= 0x10ffff) {
  344. buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
  345. buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
  346. buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
  347. buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
  348. } else {
  349. FMT_THROW(format_error("failed to format time"));
  350. }
  351. }
  352. return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
  353. }
  354. return copy_str<char>(in.data(), in.data() + in.size(), out);
  355. }
  356. template <typename Char, typename OutputIt,
  357. FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
  358. auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
  359. -> OutputIt {
  360. codecvt_result<Char> unit;
  361. write_codecvt(unit, sv, loc);
  362. return copy_str<Char>(unit.buf, unit.end, out);
  363. }
  364. template <typename Char, typename OutputIt,
  365. FMT_ENABLE_IF(std::is_same<Char, char>::value)>
  366. auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
  367. -> OutputIt {
  368. return write_encoded_tm_str(out, sv, loc);
  369. }
  370. template <typename Char>
  371. inline void do_write(buffer<Char>& buf, const std::tm& time,
  372. const std::locale& loc, char format, char modifier) {
  373. auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
  374. auto&& os = std::basic_ostream<Char>(&format_buf);
  375. os.imbue(loc);
  376. using iterator = std::ostreambuf_iterator<Char>;
  377. const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
  378. auto end = facet.put(os, os, Char(' '), &time, format, modifier);
  379. if (end.failed()) FMT_THROW(format_error("failed to format time"));
  380. }
  381. template <typename Char, typename OutputIt,
  382. FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
  383. auto write(OutputIt out, const std::tm& time, const std::locale& loc,
  384. char format, char modifier = 0) -> OutputIt {
  385. auto&& buf = get_buffer<Char>(out);
  386. do_write<Char>(buf, time, loc, format, modifier);
  387. return buf.out();
  388. }
  389. template <typename Char, typename OutputIt,
  390. FMT_ENABLE_IF(std::is_same<Char, char>::value)>
  391. auto write(OutputIt out, const std::tm& time, const std::locale& loc,
  392. char format, char modifier = 0) -> OutputIt {
  393. auto&& buf = basic_memory_buffer<Char>();
  394. do_write<char>(buf, time, loc, format, modifier);
  395. return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
  396. }
  397. } // namespace detail
  398. FMT_MODULE_EXPORT_BEGIN
  399. /**
  400. Converts given time since epoch as ``std::time_t`` value into calendar time,
  401. expressed in local time. Unlike ``std::localtime``, this function is
  402. thread-safe on most platforms.
  403. */
  404. inline std::tm localtime(std::time_t time) {
  405. struct dispatcher {
  406. std::time_t time_;
  407. std::tm tm_;
  408. dispatcher(std::time_t t) : time_(t) {}
  409. bool run() {
  410. using namespace fmt::detail;
  411. return handle(localtime_r(&time_, &tm_));
  412. }
  413. bool handle(std::tm* tm) { return tm != nullptr; }
  414. bool handle(detail::null<>) {
  415. using namespace fmt::detail;
  416. return fallback(localtime_s(&tm_, &time_));
  417. }
  418. bool fallback(int res) { return res == 0; }
  419. #if !FMT_MSC_VERSION
  420. bool fallback(detail::null<>) {
  421. using namespace fmt::detail;
  422. std::tm* tm = std::localtime(&time_);
  423. if (tm) tm_ = *tm;
  424. return tm != nullptr;
  425. }
  426. #endif
  427. };
  428. dispatcher lt(time);
  429. // Too big time values may be unsupported.
  430. if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
  431. return lt.tm_;
  432. }
  433. inline std::tm localtime(
  434. std::chrono::time_point<std::chrono::system_clock> time_point) {
  435. return localtime(std::chrono::system_clock::to_time_t(time_point));
  436. }
  437. /**
  438. Converts given time since epoch as ``std::time_t`` value into calendar time,
  439. expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
  440. function is thread-safe on most platforms.
  441. */
  442. inline std::tm gmtime(std::time_t time) {
  443. struct dispatcher {
  444. std::time_t time_;
  445. std::tm tm_;
  446. dispatcher(std::time_t t) : time_(t) {}
  447. bool run() {
  448. using namespace fmt::detail;
  449. return handle(gmtime_r(&time_, &tm_));
  450. }
  451. bool handle(std::tm* tm) { return tm != nullptr; }
  452. bool handle(detail::null<>) {
  453. using namespace fmt::detail;
  454. return fallback(gmtime_s(&tm_, &time_));
  455. }
  456. bool fallback(int res) { return res == 0; }
  457. #if !FMT_MSC_VERSION
  458. bool fallback(detail::null<>) {
  459. std::tm* tm = std::gmtime(&time_);
  460. if (tm) tm_ = *tm;
  461. return tm != nullptr;
  462. }
  463. #endif
  464. };
  465. dispatcher gt(time);
  466. // Too big time values may be unsupported.
  467. if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
  468. return gt.tm_;
  469. }
  470. inline std::tm gmtime(
  471. std::chrono::time_point<std::chrono::system_clock> time_point) {
  472. return gmtime(std::chrono::system_clock::to_time_t(time_point));
  473. }
  474. FMT_BEGIN_DETAIL_NAMESPACE
  475. // Writes two-digit numbers a, b and c separated by sep to buf.
  476. // The method by Pavel Novikov based on
  477. // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
  478. inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
  479. unsigned c, char sep) {
  480. unsigned long long digits =
  481. a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
  482. // Convert each value to BCD.
  483. // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
  484. // The difference is
  485. // y - x = a * 6
  486. // a can be found from x:
  487. // a = floor(x / 10)
  488. // then
  489. // y = x + a * 6 = x + floor(x / 10) * 6
  490. // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
  491. digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
  492. // Put low nibbles to high bytes and high nibbles to low bytes.
  493. digits = ((digits & 0x00f00000f00000f0) >> 4) |
  494. ((digits & 0x000f00000f00000f) << 8);
  495. auto usep = static_cast<unsigned long long>(sep);
  496. // Add ASCII '0' to each digit byte and insert separators.
  497. digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
  498. constexpr const size_t len = 8;
  499. if (const_check(is_big_endian())) {
  500. char tmp[len];
  501. std::memcpy(tmp, &digits, len);
  502. std::reverse_copy(tmp, tmp + len, buf);
  503. } else {
  504. std::memcpy(buf, &digits, len);
  505. }
  506. }
  507. template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
  508. if (std::is_same<Period, std::atto>::value) return "as";
  509. if (std::is_same<Period, std::femto>::value) return "fs";
  510. if (std::is_same<Period, std::pico>::value) return "ps";
  511. if (std::is_same<Period, std::nano>::value) return "ns";
  512. if (std::is_same<Period, std::micro>::value) return "µs";
  513. if (std::is_same<Period, std::milli>::value) return "ms";
  514. if (std::is_same<Period, std::centi>::value) return "cs";
  515. if (std::is_same<Period, std::deci>::value) return "ds";
  516. if (std::is_same<Period, std::ratio<1>>::value) return "s";
  517. if (std::is_same<Period, std::deca>::value) return "das";
  518. if (std::is_same<Period, std::hecto>::value) return "hs";
  519. if (std::is_same<Period, std::kilo>::value) return "ks";
  520. if (std::is_same<Period, std::mega>::value) return "Ms";
  521. if (std::is_same<Period, std::giga>::value) return "Gs";
  522. if (std::is_same<Period, std::tera>::value) return "Ts";
  523. if (std::is_same<Period, std::peta>::value) return "Ps";
  524. if (std::is_same<Period, std::exa>::value) return "Es";
  525. if (std::is_same<Period, std::ratio<60>>::value) return "m";
  526. if (std::is_same<Period, std::ratio<3600>>::value) return "h";
  527. return nullptr;
  528. }
  529. enum class numeric_system {
  530. standard,
  531. // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
  532. alternative
  533. };
  534. // Parses a put_time-like format string and invokes handler actions.
  535. template <typename Char, typename Handler>
  536. FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
  537. const Char* end,
  538. Handler&& handler) {
  539. auto ptr = begin;
  540. while (ptr != end) {
  541. auto c = *ptr;
  542. if (c == '}') break;
  543. if (c != '%') {
  544. ++ptr;
  545. continue;
  546. }
  547. if (begin != ptr) handler.on_text(begin, ptr);
  548. ++ptr; // consume '%'
  549. if (ptr == end) FMT_THROW(format_error("invalid format"));
  550. c = *ptr++;
  551. switch (c) {
  552. case '%':
  553. handler.on_text(ptr - 1, ptr);
  554. break;
  555. case 'n': {
  556. const Char newline[] = {'\n'};
  557. handler.on_text(newline, newline + 1);
  558. break;
  559. }
  560. case 't': {
  561. const Char tab[] = {'\t'};
  562. handler.on_text(tab, tab + 1);
  563. break;
  564. }
  565. // Year:
  566. case 'Y':
  567. handler.on_year(numeric_system::standard);
  568. break;
  569. case 'y':
  570. handler.on_short_year(numeric_system::standard);
  571. break;
  572. case 'C':
  573. handler.on_century(numeric_system::standard);
  574. break;
  575. case 'G':
  576. handler.on_iso_week_based_year();
  577. break;
  578. case 'g':
  579. handler.on_iso_week_based_short_year();
  580. break;
  581. // Day of the week:
  582. case 'a':
  583. handler.on_abbr_weekday();
  584. break;
  585. case 'A':
  586. handler.on_full_weekday();
  587. break;
  588. case 'w':
  589. handler.on_dec0_weekday(numeric_system::standard);
  590. break;
  591. case 'u':
  592. handler.on_dec1_weekday(numeric_system::standard);
  593. break;
  594. // Month:
  595. case 'b':
  596. case 'h':
  597. handler.on_abbr_month();
  598. break;
  599. case 'B':
  600. handler.on_full_month();
  601. break;
  602. case 'm':
  603. handler.on_dec_month(numeric_system::standard);
  604. break;
  605. // Day of the year/month:
  606. case 'U':
  607. handler.on_dec0_week_of_year(numeric_system::standard);
  608. break;
  609. case 'W':
  610. handler.on_dec1_week_of_year(numeric_system::standard);
  611. break;
  612. case 'V':
  613. handler.on_iso_week_of_year(numeric_system::standard);
  614. break;
  615. case 'j':
  616. handler.on_day_of_year();
  617. break;
  618. case 'd':
  619. handler.on_day_of_month(numeric_system::standard);
  620. break;
  621. case 'e':
  622. handler.on_day_of_month_space(numeric_system::standard);
  623. break;
  624. // Hour, minute, second:
  625. case 'H':
  626. handler.on_24_hour(numeric_system::standard);
  627. break;
  628. case 'I':
  629. handler.on_12_hour(numeric_system::standard);
  630. break;
  631. case 'M':
  632. handler.on_minute(numeric_system::standard);
  633. break;
  634. case 'S':
  635. handler.on_second(numeric_system::standard);
  636. break;
  637. // Other:
  638. case 'c':
  639. handler.on_datetime(numeric_system::standard);
  640. break;
  641. case 'x':
  642. handler.on_loc_date(numeric_system::standard);
  643. break;
  644. case 'X':
  645. handler.on_loc_time(numeric_system::standard);
  646. break;
  647. case 'D':
  648. handler.on_us_date();
  649. break;
  650. case 'F':
  651. handler.on_iso_date();
  652. break;
  653. case 'r':
  654. handler.on_12_hour_time();
  655. break;
  656. case 'R':
  657. handler.on_24_hour_time();
  658. break;
  659. case 'T':
  660. handler.on_iso_time();
  661. break;
  662. case 'p':
  663. handler.on_am_pm();
  664. break;
  665. case 'Q':
  666. handler.on_duration_value();
  667. break;
  668. case 'q':
  669. handler.on_duration_unit();
  670. break;
  671. case 'z':
  672. handler.on_utc_offset();
  673. break;
  674. case 'Z':
  675. handler.on_tz_name();
  676. break;
  677. // Alternative representation:
  678. case 'E': {
  679. if (ptr == end) FMT_THROW(format_error("invalid format"));
  680. c = *ptr++;
  681. switch (c) {
  682. case 'Y':
  683. handler.on_year(numeric_system::alternative);
  684. break;
  685. case 'y':
  686. handler.on_offset_year();
  687. break;
  688. case 'C':
  689. handler.on_century(numeric_system::alternative);
  690. break;
  691. case 'c':
  692. handler.on_datetime(numeric_system::alternative);
  693. break;
  694. case 'x':
  695. handler.on_loc_date(numeric_system::alternative);
  696. break;
  697. case 'X':
  698. handler.on_loc_time(numeric_system::alternative);
  699. break;
  700. default:
  701. FMT_THROW(format_error("invalid format"));
  702. }
  703. break;
  704. }
  705. case 'O':
  706. if (ptr == end) FMT_THROW(format_error("invalid format"));
  707. c = *ptr++;
  708. switch (c) {
  709. case 'y':
  710. handler.on_short_year(numeric_system::alternative);
  711. break;
  712. case 'm':
  713. handler.on_dec_month(numeric_system::alternative);
  714. break;
  715. case 'U':
  716. handler.on_dec0_week_of_year(numeric_system::alternative);
  717. break;
  718. case 'W':
  719. handler.on_dec1_week_of_year(numeric_system::alternative);
  720. break;
  721. case 'V':
  722. handler.on_iso_week_of_year(numeric_system::alternative);
  723. break;
  724. case 'd':
  725. handler.on_day_of_month(numeric_system::alternative);
  726. break;
  727. case 'e':
  728. handler.on_day_of_month_space(numeric_system::alternative);
  729. break;
  730. case 'w':
  731. handler.on_dec0_weekday(numeric_system::alternative);
  732. break;
  733. case 'u':
  734. handler.on_dec1_weekday(numeric_system::alternative);
  735. break;
  736. case 'H':
  737. handler.on_24_hour(numeric_system::alternative);
  738. break;
  739. case 'I':
  740. handler.on_12_hour(numeric_system::alternative);
  741. break;
  742. case 'M':
  743. handler.on_minute(numeric_system::alternative);
  744. break;
  745. case 'S':
  746. handler.on_second(numeric_system::alternative);
  747. break;
  748. default:
  749. FMT_THROW(format_error("invalid format"));
  750. }
  751. break;
  752. default:
  753. FMT_THROW(format_error("invalid format"));
  754. }
  755. begin = ptr;
  756. }
  757. if (begin != ptr) handler.on_text(begin, ptr);
  758. return ptr;
  759. }
  760. template <typename Derived> struct null_chrono_spec_handler {
  761. FMT_CONSTEXPR void unsupported() {
  762. static_cast<Derived*>(this)->unsupported();
  763. }
  764. FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
  765. FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
  766. FMT_CONSTEXPR void on_offset_year() { unsupported(); }
  767. FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
  768. FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
  769. FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
  770. FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
  771. FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
  772. FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
  773. FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
  774. FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
  775. FMT_CONSTEXPR void on_full_month() { unsupported(); }
  776. FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
  777. FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
  778. FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
  779. FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
  780. FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
  781. FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
  782. FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
  783. FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
  784. FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
  785. FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
  786. FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
  787. FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
  788. FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
  789. FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
  790. FMT_CONSTEXPR void on_us_date() { unsupported(); }
  791. FMT_CONSTEXPR void on_iso_date() { unsupported(); }
  792. FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
  793. FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
  794. FMT_CONSTEXPR void on_iso_time() { unsupported(); }
  795. FMT_CONSTEXPR void on_am_pm() { unsupported(); }
  796. FMT_CONSTEXPR void on_duration_value() { unsupported(); }
  797. FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
  798. FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
  799. FMT_CONSTEXPR void on_tz_name() { unsupported(); }
  800. };
  801. struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
  802. FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
  803. template <typename Char>
  804. FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
  805. FMT_CONSTEXPR void on_year(numeric_system) {}
  806. FMT_CONSTEXPR void on_short_year(numeric_system) {}
  807. FMT_CONSTEXPR void on_offset_year() {}
  808. FMT_CONSTEXPR void on_century(numeric_system) {}
  809. FMT_CONSTEXPR void on_iso_week_based_year() {}
  810. FMT_CONSTEXPR void on_iso_week_based_short_year() {}
  811. FMT_CONSTEXPR void on_abbr_weekday() {}
  812. FMT_CONSTEXPR void on_full_weekday() {}
  813. FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
  814. FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
  815. FMT_CONSTEXPR void on_abbr_month() {}
  816. FMT_CONSTEXPR void on_full_month() {}
  817. FMT_CONSTEXPR void on_dec_month(numeric_system) {}
  818. FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
  819. FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
  820. FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
  821. FMT_CONSTEXPR void on_day_of_year() {}
  822. FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
  823. FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
  824. FMT_CONSTEXPR void on_24_hour(numeric_system) {}
  825. FMT_CONSTEXPR void on_12_hour(numeric_system) {}
  826. FMT_CONSTEXPR void on_minute(numeric_system) {}
  827. FMT_CONSTEXPR void on_second(numeric_system) {}
  828. FMT_CONSTEXPR void on_datetime(numeric_system) {}
  829. FMT_CONSTEXPR void on_loc_date(numeric_system) {}
  830. FMT_CONSTEXPR void on_loc_time(numeric_system) {}
  831. FMT_CONSTEXPR void on_us_date() {}
  832. FMT_CONSTEXPR void on_iso_date() {}
  833. FMT_CONSTEXPR void on_12_hour_time() {}
  834. FMT_CONSTEXPR void on_24_hour_time() {}
  835. FMT_CONSTEXPR void on_iso_time() {}
  836. FMT_CONSTEXPR void on_am_pm() {}
  837. FMT_CONSTEXPR void on_utc_offset() {}
  838. FMT_CONSTEXPR void on_tz_name() {}
  839. };
  840. inline const char* tm_wday_full_name(int wday) {
  841. static constexpr const char* full_name_list[] = {
  842. "Sunday", "Monday", "Tuesday", "Wednesday",
  843. "Thursday", "Friday", "Saturday"};
  844. return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
  845. }
  846. inline const char* tm_wday_short_name(int wday) {
  847. static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
  848. "Thu", "Fri", "Sat"};
  849. return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
  850. }
  851. inline const char* tm_mon_full_name(int mon) {
  852. static constexpr const char* full_name_list[] = {
  853. "January", "February", "March", "April", "May", "June",
  854. "July", "August", "September", "October", "November", "December"};
  855. return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
  856. }
  857. inline const char* tm_mon_short_name(int mon) {
  858. static constexpr const char* short_name_list[] = {
  859. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  860. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  861. };
  862. return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
  863. }
  864. template <typename T, typename = void>
  865. struct has_member_data_tm_gmtoff : std::false_type {};
  866. template <typename T>
  867. struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
  868. : std::true_type {};
  869. template <typename T, typename = void>
  870. struct has_member_data_tm_zone : std::false_type {};
  871. template <typename T>
  872. struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
  873. : std::true_type {};
  874. #if FMT_USE_TZSET
  875. inline void tzset_once() {
  876. static bool init = []() -> bool {
  877. _tzset();
  878. return true;
  879. }();
  880. ignore_unused(init);
  881. }
  882. #endif
  883. template <typename OutputIt, typename Char> class tm_writer {
  884. private:
  885. static constexpr int days_per_week = 7;
  886. const std::locale& loc_;
  887. const bool is_classic_;
  888. OutputIt out_;
  889. const std::tm& tm_;
  890. auto tm_sec() const noexcept -> int {
  891. FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
  892. return tm_.tm_sec;
  893. }
  894. auto tm_min() const noexcept -> int {
  895. FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
  896. return tm_.tm_min;
  897. }
  898. auto tm_hour() const noexcept -> int {
  899. FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
  900. return tm_.tm_hour;
  901. }
  902. auto tm_mday() const noexcept -> int {
  903. FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
  904. return tm_.tm_mday;
  905. }
  906. auto tm_mon() const noexcept -> int {
  907. FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
  908. return tm_.tm_mon;
  909. }
  910. auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
  911. auto tm_wday() const noexcept -> int {
  912. FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
  913. return tm_.tm_wday;
  914. }
  915. auto tm_yday() const noexcept -> int {
  916. FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
  917. return tm_.tm_yday;
  918. }
  919. auto tm_hour12() const noexcept -> int {
  920. const auto h = tm_hour();
  921. const auto z = h < 12 ? h : h - 12;
  922. return z == 0 ? 12 : z;
  923. }
  924. // POSIX and the C Standard are unclear or inconsistent about what %C and %y
  925. // do if the year is negative or exceeds 9999. Use the convention that %C
  926. // concatenated with %y yields the same output as %Y, and that %Y contains at
  927. // least 4 characters, with more only if necessary.
  928. auto split_year_lower(long long year) const noexcept -> int {
  929. auto l = year % 100;
  930. if (l < 0) l = -l; // l in [0, 99]
  931. return static_cast<int>(l);
  932. }
  933. // Algorithm:
  934. // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
  935. auto iso_year_weeks(long long curr_year) const noexcept -> int {
  936. const auto prev_year = curr_year - 1;
  937. const auto curr_p =
  938. (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
  939. days_per_week;
  940. const auto prev_p =
  941. (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
  942. days_per_week;
  943. return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
  944. }
  945. auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
  946. return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
  947. days_per_week;
  948. }
  949. auto tm_iso_week_year() const noexcept -> long long {
  950. const auto year = tm_year();
  951. const auto w = iso_week_num(tm_yday(), tm_wday());
  952. if (w < 1) return year - 1;
  953. if (w > iso_year_weeks(year)) return year + 1;
  954. return year;
  955. }
  956. auto tm_iso_week_of_year() const noexcept -> int {
  957. const auto year = tm_year();
  958. const auto w = iso_week_num(tm_yday(), tm_wday());
  959. if (w < 1) return iso_year_weeks(year - 1);
  960. if (w > iso_year_weeks(year)) return 1;
  961. return w;
  962. }
  963. void write1(int value) {
  964. *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
  965. }
  966. void write2(int value) {
  967. const char* d = digits2(to_unsigned(value) % 100);
  968. *out_++ = *d++;
  969. *out_++ = *d;
  970. }
  971. void write_year_extended(long long year) {
  972. // At least 4 characters.
  973. int width = 4;
  974. if (year < 0) {
  975. *out_++ = '-';
  976. year = 0 - year;
  977. --width;
  978. }
  979. uint32_or_64_or_128_t<long long> n = to_unsigned(year);
  980. const int num_digits = count_digits(n);
  981. if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
  982. out_ = format_decimal<Char>(out_, n, num_digits).end;
  983. }
  984. void write_year(long long year) {
  985. if (year >= 0 && year < 10000) {
  986. write2(static_cast<int>(year / 100));
  987. write2(static_cast<int>(year % 100));
  988. } else {
  989. write_year_extended(year);
  990. }
  991. }
  992. void write_utc_offset(long offset) {
  993. if (offset < 0) {
  994. *out_++ = '-';
  995. offset = -offset;
  996. } else {
  997. *out_++ = '+';
  998. }
  999. offset /= 60;
  1000. write2(static_cast<int>(offset / 60));
  1001. write2(static_cast<int>(offset % 60));
  1002. }
  1003. template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
  1004. void format_utc_offset_impl(const T& tm) {
  1005. write_utc_offset(tm.tm_gmtoff);
  1006. }
  1007. template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
  1008. void format_utc_offset_impl(const T& tm) {
  1009. #if defined(_WIN32) && defined(_UCRT)
  1010. # if FMT_USE_TZSET
  1011. tzset_once();
  1012. # endif
  1013. long offset = 0;
  1014. _get_timezone(&offset);
  1015. if (tm.tm_isdst) {
  1016. long dstbias = 0;
  1017. _get_dstbias(&dstbias);
  1018. offset += dstbias;
  1019. }
  1020. write_utc_offset(-offset);
  1021. #else
  1022. ignore_unused(tm);
  1023. format_localized('z');
  1024. #endif
  1025. }
  1026. template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
  1027. void format_tz_name_impl(const T& tm) {
  1028. if (is_classic_)
  1029. out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
  1030. else
  1031. format_localized('Z');
  1032. }
  1033. template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
  1034. void format_tz_name_impl(const T&) {
  1035. format_localized('Z');
  1036. }
  1037. void format_localized(char format, char modifier = 0) {
  1038. out_ = write<Char>(out_, tm_, loc_, format, modifier);
  1039. }
  1040. public:
  1041. tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
  1042. : loc_(loc),
  1043. is_classic_(loc_ == get_classic_locale()),
  1044. out_(out),
  1045. tm_(tm) {}
  1046. OutputIt out() const { return out_; }
  1047. FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
  1048. out_ = copy_str<Char>(begin, end, out_);
  1049. }
  1050. void on_abbr_weekday() {
  1051. if (is_classic_)
  1052. out_ = write(out_, tm_wday_short_name(tm_wday()));
  1053. else
  1054. format_localized('a');
  1055. }
  1056. void on_full_weekday() {
  1057. if (is_classic_)
  1058. out_ = write(out_, tm_wday_full_name(tm_wday()));
  1059. else
  1060. format_localized('A');
  1061. }
  1062. void on_dec0_weekday(numeric_system ns) {
  1063. if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
  1064. format_localized('w', 'O');
  1065. }
  1066. void on_dec1_weekday(numeric_system ns) {
  1067. if (is_classic_ || ns == numeric_system::standard) {
  1068. auto wday = tm_wday();
  1069. write1(wday == 0 ? days_per_week : wday);
  1070. } else {
  1071. format_localized('u', 'O');
  1072. }
  1073. }
  1074. void on_abbr_month() {
  1075. if (is_classic_)
  1076. out_ = write(out_, tm_mon_short_name(tm_mon()));
  1077. else
  1078. format_localized('b');
  1079. }
  1080. void on_full_month() {
  1081. if (is_classic_)
  1082. out_ = write(out_, tm_mon_full_name(tm_mon()));
  1083. else
  1084. format_localized('B');
  1085. }
  1086. void on_datetime(numeric_system ns) {
  1087. if (is_classic_) {
  1088. on_abbr_weekday();
  1089. *out_++ = ' ';
  1090. on_abbr_month();
  1091. *out_++ = ' ';
  1092. on_day_of_month_space(numeric_system::standard);
  1093. *out_++ = ' ';
  1094. on_iso_time();
  1095. *out_++ = ' ';
  1096. on_year(numeric_system::standard);
  1097. } else {
  1098. format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
  1099. }
  1100. }
  1101. void on_loc_date(numeric_system ns) {
  1102. if (is_classic_)
  1103. on_us_date();
  1104. else
  1105. format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
  1106. }
  1107. void on_loc_time(numeric_system ns) {
  1108. if (is_classic_)
  1109. on_iso_time();
  1110. else
  1111. format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
  1112. }
  1113. void on_us_date() {
  1114. char buf[8];
  1115. write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
  1116. to_unsigned(tm_mday()),
  1117. to_unsigned(split_year_lower(tm_year())), '/');
  1118. out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
  1119. }
  1120. void on_iso_date() {
  1121. auto year = tm_year();
  1122. char buf[10];
  1123. size_t offset = 0;
  1124. if (year >= 0 && year < 10000) {
  1125. copy2(buf, digits2(static_cast<size_t>(year / 100)));
  1126. } else {
  1127. offset = 4;
  1128. write_year_extended(year);
  1129. year = 0;
  1130. }
  1131. write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
  1132. to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
  1133. '-');
  1134. out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
  1135. }
  1136. void on_utc_offset() { format_utc_offset_impl(tm_); }
  1137. void on_tz_name() { format_tz_name_impl(tm_); }
  1138. void on_year(numeric_system ns) {
  1139. if (is_classic_ || ns == numeric_system::standard)
  1140. return write_year(tm_year());
  1141. format_localized('Y', 'E');
  1142. }
  1143. void on_short_year(numeric_system ns) {
  1144. if (is_classic_ || ns == numeric_system::standard)
  1145. return write2(split_year_lower(tm_year()));
  1146. format_localized('y', 'O');
  1147. }
  1148. void on_offset_year() {
  1149. if (is_classic_) return write2(split_year_lower(tm_year()));
  1150. format_localized('y', 'E');
  1151. }
  1152. void on_century(numeric_system ns) {
  1153. if (is_classic_ || ns == numeric_system::standard) {
  1154. auto year = tm_year();
  1155. auto upper = year / 100;
  1156. if (year >= -99 && year < 0) {
  1157. // Zero upper on negative year.
  1158. *out_++ = '-';
  1159. *out_++ = '0';
  1160. } else if (upper >= 0 && upper < 100) {
  1161. write2(static_cast<int>(upper));
  1162. } else {
  1163. out_ = write<Char>(out_, upper);
  1164. }
  1165. } else {
  1166. format_localized('C', 'E');
  1167. }
  1168. }
  1169. void on_dec_month(numeric_system ns) {
  1170. if (is_classic_ || ns == numeric_system::standard)
  1171. return write2(tm_mon() + 1);
  1172. format_localized('m', 'O');
  1173. }
  1174. void on_dec0_week_of_year(numeric_system ns) {
  1175. if (is_classic_ || ns == numeric_system::standard)
  1176. return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
  1177. format_localized('U', 'O');
  1178. }
  1179. void on_dec1_week_of_year(numeric_system ns) {
  1180. if (is_classic_ || ns == numeric_system::standard) {
  1181. auto wday = tm_wday();
  1182. write2((tm_yday() + days_per_week -
  1183. (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
  1184. days_per_week);
  1185. } else {
  1186. format_localized('W', 'O');
  1187. }
  1188. }
  1189. void on_iso_week_of_year(numeric_system ns) {
  1190. if (is_classic_ || ns == numeric_system::standard)
  1191. return write2(tm_iso_week_of_year());
  1192. format_localized('V', 'O');
  1193. }
  1194. void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
  1195. void on_iso_week_based_short_year() {
  1196. write2(split_year_lower(tm_iso_week_year()));
  1197. }
  1198. void on_day_of_year() {
  1199. auto yday = tm_yday() + 1;
  1200. write1(yday / 100);
  1201. write2(yday % 100);
  1202. }
  1203. void on_day_of_month(numeric_system ns) {
  1204. if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
  1205. format_localized('d', 'O');
  1206. }
  1207. void on_day_of_month_space(numeric_system ns) {
  1208. if (is_classic_ || ns == numeric_system::standard) {
  1209. auto mday = to_unsigned(tm_mday()) % 100;
  1210. const char* d2 = digits2(mday);
  1211. *out_++ = mday < 10 ? ' ' : d2[0];
  1212. *out_++ = d2[1];
  1213. } else {
  1214. format_localized('e', 'O');
  1215. }
  1216. }
  1217. void on_24_hour(numeric_system ns) {
  1218. if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
  1219. format_localized('H', 'O');
  1220. }
  1221. void on_12_hour(numeric_system ns) {
  1222. if (is_classic_ || ns == numeric_system::standard)
  1223. return write2(tm_hour12());
  1224. format_localized('I', 'O');
  1225. }
  1226. void on_minute(numeric_system ns) {
  1227. if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
  1228. format_localized('M', 'O');
  1229. }
  1230. void on_second(numeric_system ns) {
  1231. if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
  1232. format_localized('S', 'O');
  1233. }
  1234. void on_12_hour_time() {
  1235. if (is_classic_) {
  1236. char buf[8];
  1237. write_digit2_separated(buf, to_unsigned(tm_hour12()),
  1238. to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
  1239. out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
  1240. *out_++ = ' ';
  1241. on_am_pm();
  1242. } else {
  1243. format_localized('r');
  1244. }
  1245. }
  1246. void on_24_hour_time() {
  1247. write2(tm_hour());
  1248. *out_++ = ':';
  1249. write2(tm_min());
  1250. }
  1251. void on_iso_time() {
  1252. char buf[8];
  1253. write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
  1254. to_unsigned(tm_sec()), ':');
  1255. out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
  1256. }
  1257. void on_am_pm() {
  1258. if (is_classic_) {
  1259. *out_++ = tm_hour() < 12 ? 'A' : 'P';
  1260. *out_++ = 'M';
  1261. } else {
  1262. format_localized('p');
  1263. }
  1264. }
  1265. // These apply to chrono durations but not tm.
  1266. void on_duration_value() {}
  1267. void on_duration_unit() {}
  1268. };
  1269. struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
  1270. FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
  1271. template <typename Char>
  1272. FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
  1273. FMT_CONSTEXPR void on_24_hour(numeric_system) {}
  1274. FMT_CONSTEXPR void on_12_hour(numeric_system) {}
  1275. FMT_CONSTEXPR void on_minute(numeric_system) {}
  1276. FMT_CONSTEXPR void on_second(numeric_system) {}
  1277. FMT_CONSTEXPR void on_12_hour_time() {}
  1278. FMT_CONSTEXPR void on_24_hour_time() {}
  1279. FMT_CONSTEXPR void on_iso_time() {}
  1280. FMT_CONSTEXPR void on_am_pm() {}
  1281. FMT_CONSTEXPR void on_duration_value() {}
  1282. FMT_CONSTEXPR void on_duration_unit() {}
  1283. };
  1284. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  1285. inline bool isfinite(T) {
  1286. return true;
  1287. }
  1288. // Converts value to Int and checks that it's in the range [0, upper).
  1289. template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
  1290. inline Int to_nonnegative_int(T value, Int upper) {
  1291. FMT_ASSERT(std::is_unsigned<Int>::value ||
  1292. (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
  1293. "invalid value");
  1294. (void)upper;
  1295. return static_cast<Int>(value);
  1296. }
  1297. template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
  1298. inline Int to_nonnegative_int(T value, Int upper) {
  1299. if (value < 0 || value > static_cast<T>(upper))
  1300. FMT_THROW(format_error("invalid value"));
  1301. return static_cast<Int>(value);
  1302. }
  1303. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  1304. inline T mod(T x, int y) {
  1305. return x % static_cast<T>(y);
  1306. }
  1307. template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
  1308. inline T mod(T x, int y) {
  1309. return std::fmod(x, static_cast<T>(y));
  1310. }
  1311. // If T is an integral type, maps T to its unsigned counterpart, otherwise
  1312. // leaves it unchanged (unlike std::make_unsigned).
  1313. template <typename T, bool INTEGRAL = std::is_integral<T>::value>
  1314. struct make_unsigned_or_unchanged {
  1315. using type = T;
  1316. };
  1317. template <typename T> struct make_unsigned_or_unchanged<T, true> {
  1318. using type = typename std::make_unsigned<T>::type;
  1319. };
  1320. #if FMT_SAFE_DURATION_CAST
  1321. // throwing version of safe_duration_cast
  1322. template <typename To, typename FromRep, typename FromPeriod>
  1323. To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
  1324. int ec;
  1325. To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
  1326. if (ec) FMT_THROW(format_error("cannot format duration"));
  1327. return to;
  1328. }
  1329. #endif
  1330. template <typename Rep, typename Period,
  1331. FMT_ENABLE_IF(std::is_integral<Rep>::value)>
  1332. inline std::chrono::duration<Rep, std::milli> get_milliseconds(
  1333. std::chrono::duration<Rep, Period> d) {
  1334. // this may overflow and/or the result may not fit in the
  1335. // target type.
  1336. #if FMT_SAFE_DURATION_CAST
  1337. using CommonSecondsType =
  1338. typename std::common_type<decltype(d), std::chrono::seconds>::type;
  1339. const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
  1340. const auto d_as_whole_seconds =
  1341. fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
  1342. // this conversion should be nonproblematic
  1343. const auto diff = d_as_common - d_as_whole_seconds;
  1344. const auto ms =
  1345. fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
  1346. return ms;
  1347. #else
  1348. auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
  1349. return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
  1350. #endif
  1351. }
  1352. // Counts the number of fractional digits in the range [0, 18] according to the
  1353. // C++20 spec. If more than 18 fractional digits are required then returns 6 for
  1354. // microseconds precision.
  1355. template <long long Num, long long Den, int N = 0,
  1356. bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
  1357. struct count_fractional_digits {
  1358. static constexpr int value =
  1359. Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
  1360. };
  1361. // Base case that doesn't instantiate any more templates
  1362. // in order to avoid overflow.
  1363. template <long long Num, long long Den, int N>
  1364. struct count_fractional_digits<Num, Den, N, false> {
  1365. static constexpr int value = (Num % Den == 0) ? N : 6;
  1366. };
  1367. constexpr long long pow10(std::uint32_t n) {
  1368. return n == 0 ? 1 : 10 * pow10(n - 1);
  1369. }
  1370. template <class Rep, class Period,
  1371. FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
  1372. constexpr std::chrono::duration<Rep, Period> abs(
  1373. std::chrono::duration<Rep, Period> d) {
  1374. // We need to compare the duration using the count() method directly
  1375. // due to a compiler bug in clang-11 regarding the spaceship operator,
  1376. // when -Wzero-as-null-pointer-constant is enabled.
  1377. // In clang-12 the bug has been fixed. See
  1378. // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
  1379. // https://www.godbolt.org/z/Knbb5joYx.
  1380. return d.count() >= d.zero().count() ? d : -d;
  1381. }
  1382. template <class Rep, class Period,
  1383. FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
  1384. constexpr std::chrono::duration<Rep, Period> abs(
  1385. std::chrono::duration<Rep, Period> d) {
  1386. return d;
  1387. }
  1388. template <typename Char, typename Rep, typename OutputIt,
  1389. FMT_ENABLE_IF(std::is_integral<Rep>::value)>
  1390. OutputIt format_duration_value(OutputIt out, Rep val, int) {
  1391. return write<Char>(out, val);
  1392. }
  1393. template <typename Char, typename Rep, typename OutputIt,
  1394. FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
  1395. OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
  1396. auto specs = basic_format_specs<Char>();
  1397. specs.precision = precision;
  1398. specs.type = precision >= 0 ? presentation_type::fixed_lower
  1399. : presentation_type::general_lower;
  1400. return write<Char>(out, val, specs);
  1401. }
  1402. template <typename Char, typename OutputIt>
  1403. OutputIt copy_unit(string_view unit, OutputIt out, Char) {
  1404. return std::copy(unit.begin(), unit.end(), out);
  1405. }
  1406. template <typename OutputIt>
  1407. OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
  1408. // This works when wchar_t is UTF-32 because units only contain characters
  1409. // that have the same representation in UTF-16 and UTF-32.
  1410. utf8_to_utf16 u(unit);
  1411. return std::copy(u.c_str(), u.c_str() + u.size(), out);
  1412. }
  1413. template <typename Char, typename Period, typename OutputIt>
  1414. OutputIt format_duration_unit(OutputIt out) {
  1415. if (const char* unit = get_units<Period>())
  1416. return copy_unit(string_view(unit), out, Char());
  1417. *out++ = '[';
  1418. out = write<Char>(out, Period::num);
  1419. if (const_check(Period::den != 1)) {
  1420. *out++ = '/';
  1421. out = write<Char>(out, Period::den);
  1422. }
  1423. *out++ = ']';
  1424. *out++ = 's';
  1425. return out;
  1426. }
  1427. class get_locale {
  1428. private:
  1429. union {
  1430. std::locale locale_;
  1431. };
  1432. bool has_locale_ = false;
  1433. public:
  1434. get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
  1435. if (localized)
  1436. ::new (&locale_) std::locale(loc.template get<std::locale>());
  1437. }
  1438. ~get_locale() {
  1439. if (has_locale_) locale_.~locale();
  1440. }
  1441. operator const std::locale&() const {
  1442. return has_locale_ ? locale_ : get_classic_locale();
  1443. }
  1444. };
  1445. template <typename FormatContext, typename OutputIt, typename Rep,
  1446. typename Period>
  1447. struct chrono_formatter {
  1448. FormatContext& context;
  1449. OutputIt out;
  1450. int precision;
  1451. bool localized = false;
  1452. // rep is unsigned to avoid overflow.
  1453. using rep =
  1454. conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
  1455. unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
  1456. rep val;
  1457. using seconds = std::chrono::duration<rep>;
  1458. seconds s;
  1459. using milliseconds = std::chrono::duration<rep, std::milli>;
  1460. bool negative;
  1461. using char_type = typename FormatContext::char_type;
  1462. using tm_writer_type = tm_writer<OutputIt, char_type>;
  1463. chrono_formatter(FormatContext& ctx, OutputIt o,
  1464. std::chrono::duration<Rep, Period> d)
  1465. : context(ctx),
  1466. out(o),
  1467. val(static_cast<rep>(d.count())),
  1468. negative(false) {
  1469. if (d.count() < 0) {
  1470. val = 0 - val;
  1471. negative = true;
  1472. }
  1473. // this may overflow and/or the result may not fit in the
  1474. // target type.
  1475. #if FMT_SAFE_DURATION_CAST
  1476. // might need checked conversion (rep!=Rep)
  1477. auto tmpval = std::chrono::duration<rep, Period>(val);
  1478. s = fmt_safe_duration_cast<seconds>(tmpval);
  1479. #else
  1480. s = std::chrono::duration_cast<seconds>(
  1481. std::chrono::duration<rep, Period>(val));
  1482. #endif
  1483. }
  1484. // returns true if nan or inf, writes to out.
  1485. bool handle_nan_inf() {
  1486. if (isfinite(val)) {
  1487. return false;
  1488. }
  1489. if (isnan(val)) {
  1490. write_nan();
  1491. return true;
  1492. }
  1493. // must be +-inf
  1494. if (val > 0) {
  1495. write_pinf();
  1496. } else {
  1497. write_ninf();
  1498. }
  1499. return true;
  1500. }
  1501. Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
  1502. Rep hour12() const {
  1503. Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
  1504. return hour <= 0 ? 12 : hour;
  1505. }
  1506. Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
  1507. Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
  1508. std::tm time() const {
  1509. auto time = std::tm();
  1510. time.tm_hour = to_nonnegative_int(hour(), 24);
  1511. time.tm_min = to_nonnegative_int(minute(), 60);
  1512. time.tm_sec = to_nonnegative_int(second(), 60);
  1513. return time;
  1514. }
  1515. void write_sign() {
  1516. if (negative) {
  1517. *out++ = '-';
  1518. negative = false;
  1519. }
  1520. }
  1521. void write(Rep value, int width) {
  1522. write_sign();
  1523. if (isnan(value)) return write_nan();
  1524. uint32_or_64_or_128_t<int> n =
  1525. to_unsigned(to_nonnegative_int(value, max_value<int>()));
  1526. int num_digits = detail::count_digits(n);
  1527. if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
  1528. out = format_decimal<char_type>(out, n, num_digits).end;
  1529. }
  1530. template <typename Duration> void write_fractional_seconds(Duration d) {
  1531. FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
  1532. constexpr auto num_fractional_digits =
  1533. count_fractional_digits<Duration::period::num,
  1534. Duration::period::den>::value;
  1535. using subsecond_precision = std::chrono::duration<
  1536. typename std::common_type<typename Duration::rep,
  1537. std::chrono::seconds::rep>::type,
  1538. std::ratio<1, detail::pow10(num_fractional_digits)>>;
  1539. if (std::ratio_less<typename subsecond_precision::period,
  1540. std::chrono::seconds::period>::value) {
  1541. *out++ = '.';
  1542. auto fractional =
  1543. detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
  1544. auto subseconds =
  1545. std::chrono::treat_as_floating_point<
  1546. typename subsecond_precision::rep>::value
  1547. ? fractional.count()
  1548. : std::chrono::duration_cast<subsecond_precision>(fractional)
  1549. .count();
  1550. uint32_or_64_or_128_t<long long> n =
  1551. to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
  1552. int num_digits = detail::count_digits(n);
  1553. if (num_fractional_digits > num_digits)
  1554. out = std::fill_n(out, num_fractional_digits - num_digits, '0');
  1555. out = format_decimal<char_type>(out, n, num_digits).end;
  1556. }
  1557. }
  1558. void write_nan() { std::copy_n("nan", 3, out); }
  1559. void write_pinf() { std::copy_n("inf", 3, out); }
  1560. void write_ninf() { std::copy_n("-inf", 4, out); }
  1561. template <typename Callback, typename... Args>
  1562. void format_tm(const tm& time, Callback cb, Args... args) {
  1563. if (isnan(val)) return write_nan();
  1564. get_locale loc(localized, context.locale());
  1565. auto w = tm_writer_type(loc, out, time);
  1566. (w.*cb)(args...);
  1567. out = w.out();
  1568. }
  1569. void on_text(const char_type* begin, const char_type* end) {
  1570. std::copy(begin, end, out);
  1571. }
  1572. // These are not implemented because durations don't have date information.
  1573. void on_abbr_weekday() {}
  1574. void on_full_weekday() {}
  1575. void on_dec0_weekday(numeric_system) {}
  1576. void on_dec1_weekday(numeric_system) {}
  1577. void on_abbr_month() {}
  1578. void on_full_month() {}
  1579. void on_datetime(numeric_system) {}
  1580. void on_loc_date(numeric_system) {}
  1581. void on_loc_time(numeric_system) {}
  1582. void on_us_date() {}
  1583. void on_iso_date() {}
  1584. void on_utc_offset() {}
  1585. void on_tz_name() {}
  1586. void on_year(numeric_system) {}
  1587. void on_short_year(numeric_system) {}
  1588. void on_offset_year() {}
  1589. void on_century(numeric_system) {}
  1590. void on_iso_week_based_year() {}
  1591. void on_iso_week_based_short_year() {}
  1592. void on_dec_month(numeric_system) {}
  1593. void on_dec0_week_of_year(numeric_system) {}
  1594. void on_dec1_week_of_year(numeric_system) {}
  1595. void on_iso_week_of_year(numeric_system) {}
  1596. void on_day_of_year() {}
  1597. void on_day_of_month(numeric_system) {}
  1598. void on_day_of_month_space(numeric_system) {}
  1599. void on_24_hour(numeric_system ns) {
  1600. if (handle_nan_inf()) return;
  1601. if (ns == numeric_system::standard) return write(hour(), 2);
  1602. auto time = tm();
  1603. time.tm_hour = to_nonnegative_int(hour(), 24);
  1604. format_tm(time, &tm_writer_type::on_24_hour, ns);
  1605. }
  1606. void on_12_hour(numeric_system ns) {
  1607. if (handle_nan_inf()) return;
  1608. if (ns == numeric_system::standard) return write(hour12(), 2);
  1609. auto time = tm();
  1610. time.tm_hour = to_nonnegative_int(hour12(), 12);
  1611. format_tm(time, &tm_writer_type::on_12_hour, ns);
  1612. }
  1613. void on_minute(numeric_system ns) {
  1614. if (handle_nan_inf()) return;
  1615. if (ns == numeric_system::standard) return write(minute(), 2);
  1616. auto time = tm();
  1617. time.tm_min = to_nonnegative_int(minute(), 60);
  1618. format_tm(time, &tm_writer_type::on_minute, ns);
  1619. }
  1620. void on_second(numeric_system ns) {
  1621. if (handle_nan_inf()) return;
  1622. if (ns == numeric_system::standard) {
  1623. if (std::is_floating_point<rep>::value) {
  1624. constexpr auto num_fractional_digits =
  1625. count_fractional_digits<Period::num, Period::den>::value;
  1626. auto buf = memory_buffer();
  1627. format_to(std::back_inserter(buf), runtime("{:.{}f}"),
  1628. std::fmod(val * static_cast<rep>(Period::num) /
  1629. static_cast<rep>(Period::den),
  1630. static_cast<rep>(60)),
  1631. num_fractional_digits);
  1632. if (negative) *out++ = '-';
  1633. if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
  1634. out = std::copy(buf.begin(), buf.end(), out);
  1635. } else {
  1636. write(second(), 2);
  1637. write_fractional_seconds(std::chrono::duration<rep, Period>(val));
  1638. }
  1639. return;
  1640. }
  1641. auto time = tm();
  1642. time.tm_sec = to_nonnegative_int(second(), 60);
  1643. format_tm(time, &tm_writer_type::on_second, ns);
  1644. }
  1645. void on_12_hour_time() {
  1646. if (handle_nan_inf()) return;
  1647. format_tm(time(), &tm_writer_type::on_12_hour_time);
  1648. }
  1649. void on_24_hour_time() {
  1650. if (handle_nan_inf()) {
  1651. *out++ = ':';
  1652. handle_nan_inf();
  1653. return;
  1654. }
  1655. write(hour(), 2);
  1656. *out++ = ':';
  1657. write(minute(), 2);
  1658. }
  1659. void on_iso_time() {
  1660. on_24_hour_time();
  1661. *out++ = ':';
  1662. if (handle_nan_inf()) return;
  1663. on_second(numeric_system::standard);
  1664. }
  1665. void on_am_pm() {
  1666. if (handle_nan_inf()) return;
  1667. format_tm(time(), &tm_writer_type::on_am_pm);
  1668. }
  1669. void on_duration_value() {
  1670. if (handle_nan_inf()) return;
  1671. write_sign();
  1672. out = format_duration_value<char_type>(out, val, precision);
  1673. }
  1674. void on_duration_unit() {
  1675. out = format_duration_unit<char_type, Period>(out);
  1676. }
  1677. };
  1678. FMT_END_DETAIL_NAMESPACE
  1679. #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
  1680. using weekday = std::chrono::weekday;
  1681. #else
  1682. // A fallback version of weekday.
  1683. class weekday {
  1684. private:
  1685. unsigned char value;
  1686. public:
  1687. weekday() = default;
  1688. explicit constexpr weekday(unsigned wd) noexcept
  1689. : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
  1690. constexpr unsigned c_encoding() const noexcept { return value; }
  1691. };
  1692. class year_month_day {};
  1693. #endif
  1694. // A rudimentary weekday formatter.
  1695. template <typename Char> struct formatter<weekday, Char> {
  1696. private:
  1697. bool localized = false;
  1698. public:
  1699. FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
  1700. -> decltype(ctx.begin()) {
  1701. auto begin = ctx.begin(), end = ctx.end();
  1702. if (begin != end && *begin == 'L') {
  1703. ++begin;
  1704. localized = true;
  1705. }
  1706. return begin;
  1707. }
  1708. template <typename FormatContext>
  1709. auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
  1710. auto time = std::tm();
  1711. time.tm_wday = static_cast<int>(wd.c_encoding());
  1712. detail::get_locale loc(localized, ctx.locale());
  1713. auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
  1714. w.on_abbr_weekday();
  1715. return w.out();
  1716. }
  1717. };
  1718. template <typename Rep, typename Period, typename Char>
  1719. struct formatter<std::chrono::duration<Rep, Period>, Char> {
  1720. private:
  1721. basic_format_specs<Char> specs;
  1722. int precision = -1;
  1723. using arg_ref_type = detail::arg_ref<Char>;
  1724. arg_ref_type width_ref;
  1725. arg_ref_type precision_ref;
  1726. bool localized = false;
  1727. basic_string_view<Char> format_str;
  1728. using duration = std::chrono::duration<Rep, Period>;
  1729. struct spec_handler {
  1730. formatter& f;
  1731. basic_format_parse_context<Char>& context;
  1732. basic_string_view<Char> format_str;
  1733. template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
  1734. context.check_arg_id(arg_id);
  1735. return arg_ref_type(arg_id);
  1736. }
  1737. FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
  1738. context.check_arg_id(arg_id);
  1739. return arg_ref_type(arg_id);
  1740. }
  1741. FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
  1742. return arg_ref_type(context.next_arg_id());
  1743. }
  1744. void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
  1745. FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
  1746. f.specs.fill = fill;
  1747. }
  1748. FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
  1749. FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
  1750. FMT_CONSTEXPR void on_precision(int _precision) {
  1751. f.precision = _precision;
  1752. }
  1753. FMT_CONSTEXPR void end_precision() {}
  1754. template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
  1755. f.width_ref = make_arg_ref(arg_id);
  1756. }
  1757. template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
  1758. f.precision_ref = make_arg_ref(arg_id);
  1759. }
  1760. };
  1761. using iterator = typename basic_format_parse_context<Char>::iterator;
  1762. struct parse_range {
  1763. iterator begin;
  1764. iterator end;
  1765. };
  1766. FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
  1767. auto begin = ctx.begin(), end = ctx.end();
  1768. if (begin == end || *begin == '}') return {begin, begin};
  1769. spec_handler handler{*this, ctx, format_str};
  1770. begin = detail::parse_align(begin, end, handler);
  1771. if (begin == end) return {begin, begin};
  1772. begin = detail::parse_width(begin, end, handler);
  1773. if (begin == end) return {begin, begin};
  1774. if (*begin == '.') {
  1775. if (std::is_floating_point<Rep>::value)
  1776. begin = detail::parse_precision(begin, end, handler);
  1777. else
  1778. handler.on_error("precision not allowed for this argument type");
  1779. }
  1780. if (begin != end && *begin == 'L') {
  1781. ++begin;
  1782. localized = true;
  1783. }
  1784. end = detail::parse_chrono_format(begin, end,
  1785. detail::chrono_format_checker());
  1786. return {begin, end};
  1787. }
  1788. public:
  1789. FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
  1790. -> decltype(ctx.begin()) {
  1791. auto range = do_parse(ctx);
  1792. format_str = basic_string_view<Char>(
  1793. &*range.begin, detail::to_unsigned(range.end - range.begin));
  1794. return range.end;
  1795. }
  1796. template <typename FormatContext>
  1797. auto format(const duration& d, FormatContext& ctx) const
  1798. -> decltype(ctx.out()) {
  1799. auto specs_copy = specs;
  1800. auto precision_copy = precision;
  1801. auto begin = format_str.begin(), end = format_str.end();
  1802. // As a possible future optimization, we could avoid extra copying if width
  1803. // is not specified.
  1804. basic_memory_buffer<Char> buf;
  1805. auto out = std::back_inserter(buf);
  1806. detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
  1807. width_ref, ctx);
  1808. detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
  1809. precision_ref, ctx);
  1810. if (begin == end || *begin == '}') {
  1811. out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
  1812. detail::format_duration_unit<Char, Period>(out);
  1813. } else {
  1814. detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
  1815. ctx, out, d);
  1816. f.precision = precision_copy;
  1817. f.localized = localized;
  1818. detail::parse_chrono_format(begin, end, f);
  1819. }
  1820. return detail::write(
  1821. ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
  1822. }
  1823. };
  1824. template <typename Char, typename Duration>
  1825. struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
  1826. Char> : formatter<std::tm, Char> {
  1827. FMT_CONSTEXPR formatter() {
  1828. basic_string_view<Char> default_specs =
  1829. detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
  1830. this->do_parse(default_specs.begin(), default_specs.end());
  1831. }
  1832. template <typename FormatContext>
  1833. auto format(std::chrono::time_point<std::chrono::system_clock> val,
  1834. FormatContext& ctx) const -> decltype(ctx.out()) {
  1835. return formatter<std::tm, Char>::format(localtime(val), ctx);
  1836. }
  1837. };
  1838. template <typename Char> struct formatter<std::tm, Char> {
  1839. private:
  1840. enum class spec {
  1841. unknown,
  1842. year_month_day,
  1843. hh_mm_ss,
  1844. };
  1845. spec spec_ = spec::unknown;
  1846. basic_string_view<Char> specs;
  1847. protected:
  1848. template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
  1849. if (begin != end && *begin == ':') ++begin;
  1850. end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
  1851. // Replace default spec only if the new spec is not empty.
  1852. if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
  1853. return end;
  1854. }
  1855. public:
  1856. FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
  1857. -> decltype(ctx.begin()) {
  1858. auto end = this->do_parse(ctx.begin(), ctx.end());
  1859. // basic_string_view<>::compare isn't constexpr before C++17.
  1860. if (specs.size() == 2 && specs[0] == Char('%')) {
  1861. if (specs[1] == Char('F'))
  1862. spec_ = spec::year_month_day;
  1863. else if (specs[1] == Char('T'))
  1864. spec_ = spec::hh_mm_ss;
  1865. }
  1866. return end;
  1867. }
  1868. template <typename FormatContext>
  1869. auto format(const std::tm& tm, FormatContext& ctx) const
  1870. -> decltype(ctx.out()) {
  1871. const auto loc_ref = ctx.locale();
  1872. detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
  1873. auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
  1874. if (spec_ == spec::year_month_day)
  1875. w.on_iso_date();
  1876. else if (spec_ == spec::hh_mm_ss)
  1877. w.on_iso_time();
  1878. else
  1879. detail::parse_chrono_format(specs.begin(), specs.end(), w);
  1880. return w.out();
  1881. }
  1882. };
  1883. FMT_MODULE_EXPORT_END
  1884. FMT_END_NAMESPACE
  1885. #endif // FMT_CHRONO_H_