From 61e5bcef2629e2d68b805a956a96fff264d4f74d Mon Sep 17 00:00:00 2001 From: untodesu Date: Sat, 28 Jun 2025 01:59:49 +0500 Subject: Restructure dependencies and update to C++20 - Nuked static_assert from almost everywhere in the project - Nuked binary dependency support. Might add one later though - Separated dependency headers into a separate include subdirectory - Grafted a thirdpartylegalnotices.txt generator from RITEG - Pushed development snapshot version to 2126 (26th week of 2025) --- deps/include/spdlog/fmt/bundled/chrono.h | 2432 ------------------------------ 1 file changed, 2432 deletions(-) delete mode 100644 deps/include/spdlog/fmt/bundled/chrono.h (limited to 'deps/include/spdlog/fmt/bundled/chrono.h') diff --git a/deps/include/spdlog/fmt/bundled/chrono.h b/deps/include/spdlog/fmt/bundled/chrono.h deleted file mode 100644 index ce08402..0000000 --- a/deps/include/spdlog/fmt/bundled/chrono.h +++ /dev/null @@ -1,2432 +0,0 @@ -// Formatting library for C++ - chrono support -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_CHRONO_H_ -#define FMT_CHRONO_H_ - -#ifndef FMT_MODULE -# include -# include -# include // std::isfinite -# include // std::memcpy -# include -# include -# include -# include -# include -#endif - -#include "format.h" - -FMT_BEGIN_NAMESPACE - -// Check if std::chrono::local_t is available. -#ifndef FMT_USE_LOCAL_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_LOCAL_TIME 0 -# endif -#endif - -// Check if std::chrono::utc_timestamp is available. -#ifndef FMT_USE_UTC_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_UTC_TIME 0 -# endif -#endif - -// Enable tzset. -#ifndef FMT_USE_TZSET -// UWP doesn't provide _tzset. -# if FMT_HAS_INCLUDE("winapifamily.h") -# include -# endif -# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ - (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# define FMT_USE_TZSET 1 -# else -# define FMT_USE_TZSET 0 -# endif -#endif - -// Enable safe chrono durations, unless explicitly disabled. -#ifndef FMT_SAFE_DURATION_CAST -# define FMT_SAFE_DURATION_CAST 1 -#endif -#if FMT_SAFE_DURATION_CAST - -// For conversion between std::chrono::durations without undefined -// behaviour or erroneous results. -// This is a stripped down version of duration_cast, for inclusion in fmt. -// See https://github.com/pauldreik/safe_duration_cast -// -// Copyright Paul Dreik 2019 -namespace safe_duration_cast { - -template ::value && - std::numeric_limits::is_signed == - std::numeric_limits::is_signed)> -FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) - -> To { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; - static_assert(F::is_integer, "From must be integral"); - static_assert(T::is_integer, "To must be integral"); - - // A and B are both signed, or both unsigned. - if (detail::const_check(F::digits <= T::digits)) { - // From fits in To without any problem. - } else { - // From does not always fit in To, resort to a dynamic check. - if (from < (T::min)() || from > (T::max)()) { - // outside range. - ec = 1; - return {}; - } - } - return static_cast(from); -} - -/// Converts From to To, without loss. If the dynamic value of from -/// can't be converted to To without loss, ec is set. -template ::value && - std::numeric_limits::is_signed != - std::numeric_limits::is_signed)> -FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) - -> To { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; - static_assert(F::is_integer, "From must be integral"); - static_assert(T::is_integer, "To must be integral"); - - if (detail::const_check(F::is_signed && !T::is_signed)) { - // From may be negative, not allowed! - if (fmt::detail::is_negative(from)) { - ec = 1; - return {}; - } - // From is positive. Can it always fit in To? - if (detail::const_check(F::digits > T::digits) && - from > static_cast(detail::max_value())) { - ec = 1; - return {}; - } - } - - if (detail::const_check(!F::is_signed && T::is_signed && - F::digits >= T::digits) && - from > static_cast(detail::max_value())) { - ec = 1; - return {}; - } - return static_cast(from); // Lossless conversion. -} - -template ::value)> -FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) - -> To { - ec = 0; - return from; -} // function - -// clang-format off -/** - * converts From to To if possible, otherwise ec is set. - * - * input | output - * ---------------------------------|--------------- - * NaN | NaN - * Inf | Inf - * normal, fits in output | converted (possibly lossy) - * normal, does not fit in output | ec is set - * subnormal | best effort - * -Inf | -Inf - */ -// clang-format on -template ::value)> -FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { - ec = 0; - using T = std::numeric_limits; - static_assert(std::is_floating_point::value, "From must be floating"); - static_assert(std::is_floating_point::value, "To must be floating"); - - // catch the only happy case - if (std::isfinite(from)) { - if (from >= T::lowest() && from <= (T::max)()) { - return static_cast(from); - } - // not within range. - ec = 1; - return {}; - } - - // nan and inf will be preserved - return static_cast(from); -} // function - -template ::value)> -FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { - ec = 0; - static_assert(std::is_floating_point::value, "From must be floating"); - return from; -} - -/// Safe duration cast between integral durations -template ::value), - FMT_ENABLE_IF(std::is_integral::value)> -auto safe_duration_cast(std::chrono::duration from, - int& ec) -> To { - using From = std::chrono::duration; - ec = 0; - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // safe conversion to IntermediateRep - IntermediateRep count = - lossless_integral_conversion(from.count(), ec); - if (ec) return {}; - // multiply with Factor::num without overflow or underflow - if (detail::const_check(Factor::num != 1)) { - const auto max1 = detail::max_value() / Factor::num; - if (count > max1) { - ec = 1; - return {}; - } - const auto min1 = - (std::numeric_limits::min)() / Factor::num; - if (detail::const_check(!std::is_unsigned::value) && - count < min1) { - ec = 1; - return {}; - } - count *= Factor::num; - } - - if (detail::const_check(Factor::den != 1)) count /= Factor::den; - auto tocount = lossless_integral_conversion(count, ec); - return ec ? To() : To(tocount); -} - -/// Safe duration_cast between floating point durations -template ::value), - FMT_ENABLE_IF(std::is_floating_point::value)> -auto safe_duration_cast(std::chrono::duration from, - int& ec) -> To { - using From = std::chrono::duration; - ec = 0; - if (std::isnan(from.count())) { - // nan in, gives nan out. easy. - return To{std::numeric_limits::quiet_NaN()}; - } - // maybe we should also check if from is denormal, and decide what to do about - // it. - - // +-inf should be preserved. - if (std::isinf(from.count())) { - return To{from.count()}; - } - - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // force conversion of From::rep -> IntermediateRep to be safe, - // even if it will never happen be narrowing in this context. - IntermediateRep count = - safe_float_conversion(from.count(), ec); - if (ec) { - return {}; - } - - // multiply with Factor::num without overflow or underflow - if (detail::const_check(Factor::num != 1)) { - constexpr auto max1 = detail::max_value() / - static_cast(Factor::num); - if (count > max1) { - ec = 1; - return {}; - } - constexpr auto min1 = std::numeric_limits::lowest() / - static_cast(Factor::num); - if (count < min1) { - ec = 1; - return {}; - } - count *= static_cast(Factor::num); - } - - // this can't go wrong, right? den>0 is checked earlier. - if (detail::const_check(Factor::den != 1)) { - using common_t = typename std::common_type::type; - count /= static_cast(Factor::den); - } - - // convert to the to type, safely - using ToRep = typename To::rep; - - const ToRep tocount = safe_float_conversion(count, ec); - if (ec) { - return {}; - } - return To{tocount}; -} -} // namespace safe_duration_cast -#endif - -// Prevents expansion of a preceding token as a function-style macro. -// Usage: f FMT_NOMACRO() -#define FMT_NOMACRO - -namespace detail { -template struct null {}; -inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } -inline auto localtime_s(...) -> null<> { return null<>(); } -inline auto gmtime_r(...) -> null<> { return null<>(); } -inline auto gmtime_s(...) -> null<> { return null<>(); } - -// It is defined here and not in ostream.h because the latter has expensive -// includes. -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const char_type* s, streamsize count) -> streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - -inline auto get_classic_locale() -> const std::locale& { - static const auto& locale = std::locale::classic(); - return locale; -} - -template struct codecvt_result { - static constexpr const size_t max_size = 32; - CodeUnit buf[max_size]; - CodeUnit* end; -}; - -template -void write_codecvt(codecvt_result& out, string_view in_buf, - const std::locale& loc) { -#if FMT_CLANG_VERSION -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated" - auto& f = std::use_facet>(loc); -# pragma clang diagnostic pop -#else - auto& f = std::use_facet>(loc); -#endif - auto mb = std::mbstate_t(); - const char* from_next = nullptr; - auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, - std::begin(out.buf), std::end(out.buf), out.end); - if (result != std::codecvt_base::ok) - FMT_THROW(format_error("failed to format time")); -} - -template -auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) - -> OutputIt { - if (detail::use_utf8() && loc != get_classic_locale()) { - // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and - // gcc-4. -#if FMT_MSC_VERSION != 0 || \ - (defined(__GLIBCXX__) && \ - (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) - // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 - // and newer. - using code_unit = wchar_t; -#else - using code_unit = char32_t; -#endif - - using unit_t = codecvt_result; - unit_t unit; - write_codecvt(unit, in, loc); - // In UTF-8 is used one to four one-byte code units. - auto u = - to_utf8>(); - if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) - FMT_THROW(format_error("failed to format time")); - return copy(u.c_str(), u.c_str() + u.size(), out); - } - return copy(in.data(), in.data() + in.size(), out); -} - -template ::value)> -auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) - -> OutputIt { - codecvt_result unit; - write_codecvt(unit, sv, loc); - return copy(unit.buf, unit.end, out); -} - -template ::value)> -auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) - -> OutputIt { - return write_encoded_tm_str(out, sv, loc); -} - -template -inline void do_write(buffer& buf, const std::tm& time, - const std::locale& loc, char format, char modifier) { - auto&& format_buf = formatbuf>(buf); - auto&& os = std::basic_ostream(&format_buf); - os.imbue(loc); - const auto& facet = std::use_facet>(loc); - auto end = facet.put(os, os, Char(' '), &time, format, modifier); - if (end.failed()) FMT_THROW(format_error("failed to format time")); -} - -template ::value)> -auto write(OutputIt out, const std::tm& time, const std::locale& loc, - char format, char modifier = 0) -> OutputIt { - auto&& buf = get_buffer(out); - do_write(buf, time, loc, format, modifier); - return get_iterator(buf, out); -} - -template ::value)> -auto write(OutputIt out, const std::tm& time, const std::locale& loc, - char format, char modifier = 0) -> OutputIt { - auto&& buf = basic_memory_buffer(); - do_write(buf, time, loc, format, modifier); - return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); -} - -template -struct is_same_arithmetic_type - : public std::integral_constant::value && - std::is_integral::value) || - (std::is_floating_point::value && - std::is_floating_point::value)> { -}; - -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { -#if FMT_SAFE_DURATION_CAST - // Throwing version of safe_duration_cast is only available for - // integer to integer or float to float casts. - int ec; - To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); - return to; -#else - // Standard duration cast, may overflow. - return std::chrono::duration_cast(from); -#endif -} - -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(!is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { - // Mixed integer <-> float cast is not supported by safe_duration_cast. - return std::chrono::duration_cast(from); -} - -template -auto to_time_t( - std::chrono::time_point time_point) - -> std::time_t { - // Cannot use std::chrono::system_clock::to_time_t since this would first - // require a cast to std::chrono::system_clock::time_point, which could - // overflow. - return fmt_duration_cast>( - time_point.time_since_epoch()) - .count(); -} -} // namespace detail - -FMT_BEGIN_EXPORT - -/** - * Converts given time since epoch as `std::time_t` value into calendar time, - * expressed in local time. Unlike `std::localtime`, this function is - * thread-safe on most platforms. - */ -inline auto localtime(std::time_t time) -> std::tm { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - dispatcher(std::time_t t) : time_(t) {} - - auto run() -> bool { - using namespace fmt::detail; - return handle(localtime_r(&time_, &tm_)); - } - - auto handle(std::tm* tm) -> bool { return tm != nullptr; } - - auto handle(detail::null<>) -> bool { - using namespace fmt::detail; - return fallback(localtime_s(&tm_, &time_)); - } - - auto fallback(int res) -> bool { return res == 0; } - -#if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { - using namespace fmt::detail; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - dispatcher lt(time); - // Too big time values may be unsupported. - if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); - return lt.tm_; -} - -#if FMT_USE_LOCAL_TIME -template -inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime( - detail::to_time_t(std::chrono::current_zone()->to_sys(time))); -} -#endif - -/** - * Converts given time since epoch as `std::time_t` value into calendar time, - * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this - * function is thread-safe on most platforms. - */ -inline auto gmtime(std::time_t time) -> std::tm { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - dispatcher(std::time_t t) : time_(t) {} - - auto run() -> bool { - using namespace fmt::detail; - return handle(gmtime_r(&time_, &tm_)); - } - - auto handle(std::tm* tm) -> bool { return tm != nullptr; } - - auto handle(detail::null<>) -> bool { - using namespace fmt::detail; - return fallback(gmtime_s(&tm_, &time_)); - } - - auto fallback(int res) -> bool { return res == 0; } - -#if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { - std::tm* tm = std::gmtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - auto gt = dispatcher(time); - // Too big time values may be unsupported. - if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); - return gt.tm_; -} - -template -inline auto gmtime( - std::chrono::time_point time_point) - -> std::tm { - return gmtime(detail::to_time_t(time_point)); -} - -namespace detail { - -// Writes two-digit numbers a, b and c separated by sep to buf. -// The method by Pavel Novikov based on -// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. -inline void write_digit2_separated(char* buf, unsigned a, unsigned b, - unsigned c, char sep) { - unsigned long long digits = - a | (b << 24) | (static_cast(c) << 48); - // Convert each value to BCD. - // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. - // The difference is - // y - x = a * 6 - // a can be found from x: - // a = floor(x / 10) - // then - // y = x + a * 6 = x + floor(x / 10) * 6 - // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). - digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; - // Put low nibbles to high bytes and high nibbles to low bytes. - digits = ((digits & 0x00f00000f00000f0) >> 4) | - ((digits & 0x000f00000f00000f) << 8); - auto usep = static_cast(sep); - // Add ASCII '0' to each digit byte and insert separators. - digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); - - constexpr const size_t len = 8; - if (const_check(is_big_endian())) { - char tmp[len]; - std::memcpy(tmp, &digits, len); - std::reverse_copy(tmp, tmp + len, buf); - } else { - std::memcpy(buf, &digits, len); - } -} - -template -FMT_CONSTEXPR inline auto get_units() -> const char* { - if (std::is_same::value) return "as"; - if (std::is_same::value) return "fs"; - if (std::is_same::value) return "ps"; - if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; - if (std::is_same::value) return "ms"; - if (std::is_same::value) return "cs"; - if (std::is_same::value) return "ds"; - if (std::is_same>::value) return "s"; - if (std::is_same::value) return "das"; - if (std::is_same::value) return "hs"; - if (std::is_same::value) return "ks"; - if (std::is_same::value) return "Ms"; - if (std::is_same::value) return "Gs"; - if (std::is_same::value) return "Ts"; - if (std::is_same::value) return "Ps"; - if (std::is_same::value) return "Es"; - if (std::is_same>::value) return "min"; - if (std::is_same>::value) return "h"; - if (std::is_same>::value) return "d"; - return nullptr; -} - -enum class numeric_system { - standard, - // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. - alternative -}; - -// Glibc extensions for formatting numeric values. -enum class pad_type { - // Pad a numeric result string with zeros (the default). - zero, - // Do not pad a numeric result string. - none, - // Pad a numeric result string with spaces. - space, -}; - -template -auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { - if (pad == pad_type::none) return out; - return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); -} - -template -auto write_padding(OutputIt out, pad_type pad) -> OutputIt { - if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; - return out; -} - -// Parses a put_time-like format string and invokes handler actions. -template -FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - if (begin == end || *begin == '}') return begin; - if (*begin != '%') FMT_THROW(format_error("invalid format")); - auto ptr = begin; - while (ptr != end) { - pad_type pad = pad_type::zero; - auto c = *ptr; - if (c == '}') break; - if (c != '%') { - ++ptr; - continue; - } - if (begin != ptr) handler.on_text(begin, ptr); - ++ptr; // consume '%' - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr; - switch (c) { - case '_': - pad = pad_type::space; - ++ptr; - break; - case '-': - pad = pad_type::none; - ++ptr; - break; - } - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case '%': - handler.on_text(ptr - 1, ptr); - break; - case 'n': { - const Char newline[] = {'\n'}; - handler.on_text(newline, newline + 1); - break; - } - case 't': { - const Char tab[] = {'\t'}; - handler.on_text(tab, tab + 1); - break; - } - // Year: - case 'Y': - handler.on_year(numeric_system::standard); - break; - case 'y': - handler.on_short_year(numeric_system::standard); - break; - case 'C': - handler.on_century(numeric_system::standard); - break; - case 'G': - handler.on_iso_week_based_year(); - break; - case 'g': - handler.on_iso_week_based_short_year(); - break; - // Day of the week: - case 'a': - handler.on_abbr_weekday(); - break; - case 'A': - handler.on_full_weekday(); - break; - case 'w': - handler.on_dec0_weekday(numeric_system::standard); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::standard); - break; - // Month: - case 'b': - case 'h': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; - case 'm': - handler.on_dec_month(numeric_system::standard); - break; - // Day of the year/month: - case 'U': - handler.on_dec0_week_of_year(numeric_system::standard, pad); - break; - case 'W': - handler.on_dec1_week_of_year(numeric_system::standard, pad); - break; - case 'V': - handler.on_iso_week_of_year(numeric_system::standard, pad); - break; - case 'j': - handler.on_day_of_year(); - break; - case 'd': - handler.on_day_of_month(numeric_system::standard, pad); - break; - case 'e': - handler.on_day_of_month(numeric_system::standard, pad_type::space); - break; - // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::standard, pad); - break; - case 'M': - handler.on_minute(numeric_system::standard, pad); - break; - case 'S': - handler.on_second(numeric_system::standard, pad); - break; - // Other: - case 'c': - handler.on_datetime(numeric_system::standard); - break; - case 'x': - handler.on_loc_date(numeric_system::standard); - break; - case 'X': - handler.on_loc_time(numeric_system::standard); - break; - case 'D': - handler.on_us_date(); - break; - case 'F': - handler.on_iso_date(); - break; - case 'r': - handler.on_12_hour_time(); - break; - case 'R': - handler.on_24_hour_time(); - break; - case 'T': - handler.on_iso_time(); - break; - case 'p': - handler.on_am_pm(); - break; - case 'Q': - handler.on_duration_value(); - break; - case 'q': - handler.on_duration_unit(); - break; - case 'z': - handler.on_utc_offset(numeric_system::standard); - break; - case 'Z': - handler.on_tz_name(); - break; - // Alternative representation: - case 'E': { - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case 'Y': - handler.on_year(numeric_system::alternative); - break; - case 'y': - handler.on_offset_year(); - break; - case 'C': - handler.on_century(numeric_system::alternative); - break; - case 'c': - handler.on_datetime(numeric_system::alternative); - break; - case 'x': - handler.on_loc_date(numeric_system::alternative); - break; - case 'X': - handler.on_loc_time(numeric_system::alternative); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); - } - break; - } - case 'O': - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case 'y': - handler.on_short_year(numeric_system::alternative); - break; - case 'm': - handler.on_dec_month(numeric_system::alternative); - break; - case 'U': - handler.on_dec0_week_of_year(numeric_system::alternative, pad); - break; - case 'W': - handler.on_dec1_week_of_year(numeric_system::alternative, pad); - break; - case 'V': - handler.on_iso_week_of_year(numeric_system::alternative, pad); - break; - case 'd': - handler.on_day_of_month(numeric_system::alternative, pad); - break; - case 'e': - handler.on_day_of_month(numeric_system::alternative, pad_type::space); - break; - case 'w': - handler.on_dec0_weekday(numeric_system::alternative); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::alternative); - break; - case 'H': - handler.on_24_hour(numeric_system::alternative, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::alternative, pad); - break; - case 'M': - handler.on_minute(numeric_system::alternative, pad); - break; - case 'S': - handler.on_second(numeric_system::alternative, pad); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); - } - break; - default: - FMT_THROW(format_error("invalid format")); - } - begin = ptr; - } - if (begin != ptr) handler.on_text(begin, ptr); - return ptr; -} - -template struct null_chrono_spec_handler { - FMT_CONSTEXPR void unsupported() { - static_cast(this)->unsupported(); - } - FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_offset_year() { unsupported(); } - FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } - FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } - FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } - FMT_CONSTEXPR void on_full_weekday() { unsupported(); } - FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_abbr_month() { unsupported(); } - FMT_CONSTEXPR void on_full_month() { unsupported(); } - FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { - unsupported(); - } - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { - unsupported(); - } - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { - unsupported(); - } - FMT_CONSTEXPR void on_day_of_year() { unsupported(); } - FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { - unsupported(); - } - FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_us_date() { unsupported(); } - FMT_CONSTEXPR void on_iso_date() { unsupported(); } - FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } - FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } - FMT_CONSTEXPR void on_iso_time() { unsupported(); } - FMT_CONSTEXPR void on_am_pm() { unsupported(); } - FMT_CONSTEXPR void on_duration_value() { unsupported(); } - FMT_CONSTEXPR void on_duration_unit() { unsupported(); } - FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_tz_name() { unsupported(); } -}; - -struct tm_format_checker : null_chrono_spec_handler { - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } - - template - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_year(numeric_system) {} - FMT_CONSTEXPR void on_short_year(numeric_system) {} - FMT_CONSTEXPR void on_offset_year() {} - FMT_CONSTEXPR void on_century(numeric_system) {} - FMT_CONSTEXPR void on_iso_week_based_year() {} - FMT_CONSTEXPR void on_iso_week_based_short_year() {} - FMT_CONSTEXPR void on_abbr_weekday() {} - FMT_CONSTEXPR void on_full_weekday() {} - FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} - FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} - FMT_CONSTEXPR void on_abbr_month() {} - FMT_CONSTEXPR void on_full_month() {} - FMT_CONSTEXPR void on_dec_month(numeric_system) {} - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_datetime(numeric_system) {} - FMT_CONSTEXPR void on_loc_date(numeric_system) {} - FMT_CONSTEXPR void on_loc_time(numeric_system) {} - FMT_CONSTEXPR void on_us_date() {} - FMT_CONSTEXPR void on_iso_date() {} - FMT_CONSTEXPR void on_12_hour_time() {} - FMT_CONSTEXPR void on_24_hour_time() {} - FMT_CONSTEXPR void on_iso_time() {} - FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset(numeric_system) {} - FMT_CONSTEXPR void on_tz_name() {} -}; - -inline auto tm_wday_full_name(int wday) -> const char* { - static constexpr const char* full_name_list[] = { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday"}; - return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; -} -inline auto tm_wday_short_name(int wday) -> const char* { - static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; -} - -inline auto tm_mon_full_name(int mon) -> const char* { - static constexpr const char* full_name_list[] = { - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"}; - return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; -} -inline auto tm_mon_short_name(int mon) -> const char* { - static constexpr const char* short_name_list[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - }; - return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; -} - -template -struct has_member_data_tm_gmtoff : std::false_type {}; -template -struct has_member_data_tm_gmtoff> - : std::true_type {}; - -template -struct has_member_data_tm_zone : std::false_type {}; -template -struct has_member_data_tm_zone> - : std::true_type {}; - -#if FMT_USE_TZSET -inline void tzset_once() { - static bool init = []() -> bool { - _tzset(); - return true; - }(); - ignore_unused(init); -} -#endif - -// Converts value to Int and checks that it's in the range [0, upper). -template ::value)> -inline auto to_nonnegative_int(T value, Int upper) -> Int { - if (!std::is_unsigned::value && - (value < 0 || to_unsigned(value) > to_unsigned(upper))) { - FMT_THROW(fmt::format_error("chrono value is out of range")); - } - return static_cast(value); -} -template ::value)> -inline auto to_nonnegative_int(T value, Int upper) -> Int { - auto int_value = static_cast(value); - if (int_value < 0 || value > static_cast(upper)) - FMT_THROW(format_error("invalid value")); - return int_value; -} - -constexpr auto pow10(std::uint32_t n) -> long long { - return n == 0 ? 1 : 10 * pow10(n - 1); -} - -// Counts the number of fractional digits in the range [0, 18] according to the -// C++20 spec. If more than 18 fractional digits are required then returns 6 for -// microseconds precision. -template () / 10)> -struct count_fractional_digits { - static constexpr int value = - Num % Den == 0 ? N : count_fractional_digits::value; -}; - -// Base case that doesn't instantiate any more templates -// in order to avoid overflow. -template -struct count_fractional_digits { - static constexpr int value = (Num % Den == 0) ? N : 6; -}; - -// Format subseconds which are given as an integer type with an appropriate -// number of digits. -template -void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { - constexpr auto num_fractional_digits = - count_fractional_digits::value; - - using subsecond_precision = std::chrono::duration< - typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; - - const auto fractional = d - fmt_duration_cast(d); - const auto subseconds = - std::chrono::treat_as_floating_point< - typename subsecond_precision::rep>::value - ? fractional.count() - : fmt_duration_cast(fractional).count(); - auto n = static_cast>(subseconds); - const int num_digits = detail::count_digits(n); - - int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); - if (precision < 0) { - FMT_ASSERT(!std::is_floating_point::value, ""); - if (std::ratio_less::value) { - *out++ = '.'; - out = detail::fill_n(out, leading_zeroes, '0'); - out = format_decimal(out, n, num_digits).end; - } - } else if (precision > 0) { - *out++ = '.'; - leading_zeroes = (std::min)(leading_zeroes, precision); - int remaining = precision - leading_zeroes; - out = detail::fill_n(out, leading_zeroes, '0'); - if (remaining < num_digits) { - int num_truncated_digits = num_digits - remaining; - n /= to_unsigned(detail::pow10(to_unsigned(num_truncated_digits))); - if (n) { - out = format_decimal(out, n, remaining).end; - } - return; - } - if (n) { - out = format_decimal(out, n, num_digits).end; - remaining -= num_digits; - } - out = detail::fill_n(out, remaining, '0'); - } -} - -// Format subseconds which are given as a floating point type with an -// appropriate number of digits. We cannot pass the Duration here, as we -// explicitly need to pass the Rep value in the chrono_formatter. -template -void write_floating_seconds(memory_buffer& buf, Duration duration, - int num_fractional_digits = -1) { - using rep = typename Duration::rep; - FMT_ASSERT(std::is_floating_point::value, ""); - - auto val = duration.count(); - - if (num_fractional_digits < 0) { - // For `std::round` with fallback to `round`: - // On some toolchains `std::round` is not available (e.g. GCC 6). - using namespace std; - num_fractional_digits = - count_fractional_digits::value; - if (num_fractional_digits < 6 && static_cast(round(val)) != val) - num_fractional_digits = 6; - } - - fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), - std::fmod(val * static_cast(Duration::period::num) / - static_cast(Duration::period::den), - static_cast(60)), - num_fractional_digits); -} - -template -class tm_writer { - private: - static constexpr int days_per_week = 7; - - const std::locale& loc_; - const bool is_classic_; - OutputIt out_; - const Duration* subsecs_; - const std::tm& tm_; - - auto tm_sec() const noexcept -> int { - FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); - return tm_.tm_sec; - } - auto tm_min() const noexcept -> int { - FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); - return tm_.tm_min; - } - auto tm_hour() const noexcept -> int { - FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); - return tm_.tm_hour; - } - auto tm_mday() const noexcept -> int { - FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); - return tm_.tm_mday; - } - auto tm_mon() const noexcept -> int { - FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); - return tm_.tm_mon; - } - auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } - auto tm_wday() const noexcept -> int { - FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); - return tm_.tm_wday; - } - auto tm_yday() const noexcept -> int { - FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); - return tm_.tm_yday; - } - - auto tm_hour12() const noexcept -> int { - const auto h = tm_hour(); - const auto z = h < 12 ? h : h - 12; - return z == 0 ? 12 : z; - } - - // POSIX and the C Standard are unclear or inconsistent about what %C and %y - // do if the year is negative or exceeds 9999. Use the convention that %C - // concatenated with %y yields the same output as %Y, and that %Y contains at - // least 4 characters, with more only if necessary. - auto split_year_lower(long long year) const noexcept -> int { - auto l = year % 100; - if (l < 0) l = -l; // l in [0, 99] - return static_cast(l); - } - - // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. - auto iso_year_weeks(long long curr_year) const noexcept -> int { - const auto prev_year = curr_year - 1; - const auto curr_p = - (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % - days_per_week; - const auto prev_p = - (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % - days_per_week; - return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); - } - auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { - return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / - days_per_week; - } - auto tm_iso_week_year() const noexcept -> long long { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); - if (w < 1) return year - 1; - if (w > iso_year_weeks(year)) return year + 1; - return year; - } - auto tm_iso_week_of_year() const noexcept -> int { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); - if (w < 1) return iso_year_weeks(year - 1); - if (w > iso_year_weeks(year)) return 1; - return w; - } - - void write1(int value) { - *out_++ = static_cast('0' + to_unsigned(value) % 10); - } - void write2(int value) { - const char* d = digits2(to_unsigned(value) % 100); - *out_++ = *d++; - *out_++ = *d; - } - void write2(int value, pad_type pad) { - unsigned int v = to_unsigned(value) % 100; - if (v >= 10) { - const char* d = digits2(v); - *out_++ = *d++; - *out_++ = *d; - } else { - out_ = detail::write_padding(out_, pad); - *out_++ = static_cast('0' + v); - } - } - - void write_year_extended(long long year) { - // At least 4 characters. - int width = 4; - if (year < 0) { - *out_++ = '-'; - year = 0 - year; - --width; - } - uint32_or_64_or_128_t n = to_unsigned(year); - const int num_digits = count_digits(n); - if (width > num_digits) - out_ = detail::fill_n(out_, width - num_digits, '0'); - out_ = format_decimal(out_, n, num_digits).end; - } - void write_year(long long year) { - if (year >= 0 && year < 10000) { - write2(static_cast(year / 100)); - write2(static_cast(year % 100)); - } else { - write_year_extended(year); - } - } - - void write_utc_offset(long offset, numeric_system ns) { - if (offset < 0) { - *out_++ = '-'; - offset = -offset; - } else { - *out_++ = '+'; - } - offset /= 60; - write2(static_cast(offset / 60)); - if (ns != numeric_system::standard) *out_++ = ':'; - write2(static_cast(offset % 60)); - } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { - write_utc_offset(tm.tm_gmtoff, ns); - } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { -#if defined(_WIN32) && defined(_UCRT) -# if FMT_USE_TZSET - tzset_once(); -# endif - long offset = 0; - _get_timezone(&offset); - if (tm.tm_isdst) { - long dstbias = 0; - _get_dstbias(&dstbias); - offset += dstbias; - } - write_utc_offset(-offset, ns); -#else - if (ns == numeric_system::standard) return format_localized('z'); - - // Extract timezone offset from timezone conversion functions. - std::tm gtm = tm; - std::time_t gt = std::mktime(>m); - std::tm ltm = gmtime(gt); - std::time_t lt = std::mktime(<m); - long offset = gt - lt; - write_utc_offset(offset, ns); -#endif - } - - template ::value)> - void format_tz_name_impl(const T& tm) { - if (is_classic_) - out_ = write_tm_str(out_, tm.tm_zone, loc_); - else - format_localized('Z'); - } - template ::value)> - void format_tz_name_impl(const T&) { - format_localized('Z'); - } - - void format_localized(char format, char modifier = 0) { - out_ = write(out_, tm_, loc_, format, modifier); - } - - public: - tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, - const Duration* subsecs = nullptr) - : loc_(loc), - is_classic_(loc_ == get_classic_locale()), - out_(out), - subsecs_(subsecs), - tm_(tm) {} - - auto out() const -> OutputIt { return out_; } - - FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - out_ = copy(begin, end, out_); - } - - void on_abbr_weekday() { - if (is_classic_) - out_ = write(out_, tm_wday_short_name(tm_wday())); - else - format_localized('a'); - } - void on_full_weekday() { - if (is_classic_) - out_ = write(out_, tm_wday_full_name(tm_wday())); - else - format_localized('A'); - } - void on_dec0_weekday(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); - format_localized('w', 'O'); - } - void on_dec1_weekday(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto wday = tm_wday(); - write1(wday == 0 ? days_per_week : wday); - } else { - format_localized('u', 'O'); - } - } - - void on_abbr_month() { - if (is_classic_) - out_ = write(out_, tm_mon_short_name(tm_mon())); - else - format_localized('b'); - } - void on_full_month() { - if (is_classic_) - out_ = write(out_, tm_mon_full_name(tm_mon())); - else - format_localized('B'); - } - - void on_datetime(numeric_system ns) { - if (is_classic_) { - on_abbr_weekday(); - *out_++ = ' '; - on_abbr_month(); - *out_++ = ' '; - on_day_of_month(numeric_system::standard, pad_type::space); - *out_++ = ' '; - on_iso_time(); - *out_++ = ' '; - on_year(numeric_system::standard); - } else { - format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); - } - } - void on_loc_date(numeric_system ns) { - if (is_classic_) - on_us_date(); - else - format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); - } - void on_loc_time(numeric_system ns) { - if (is_classic_) - on_iso_time(); - else - format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); - } - void on_us_date() { - char buf[8]; - write_digit2_separated(buf, to_unsigned(tm_mon() + 1), - to_unsigned(tm_mday()), - to_unsigned(split_year_lower(tm_year())), '/'); - out_ = copy(std::begin(buf), std::end(buf), out_); - } - void on_iso_date() { - auto year = tm_year(); - char buf[10]; - size_t offset = 0; - if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); - } else { - offset = 4; - write_year_extended(year); - year = 0; - } - write_digit2_separated(buf + 2, static_cast(year % 100), - to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), - '-'); - out_ = copy(std::begin(buf) + offset, std::end(buf), out_); - } - - void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } - void on_tz_name() { format_tz_name_impl(tm_); } - - void on_year(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) - return write_year(tm_year()); - format_localized('Y', 'E'); - } - void on_short_year(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) - return write2(split_year_lower(tm_year())); - format_localized('y', 'O'); - } - void on_offset_year() { - if (is_classic_) return write2(split_year_lower(tm_year())); - format_localized('y', 'E'); - } - - void on_century(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto year = tm_year(); - auto upper = year / 100; - if (year >= -99 && year < 0) { - // Zero upper on negative year. - *out_++ = '-'; - *out_++ = '0'; - } else if (upper >= 0 && upper < 100) { - write2(static_cast(upper)); - } else { - out_ = write(out_, upper); - } - } else { - format_localized('C', 'E'); - } - } - - void on_dec_month(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_mon() + 1); - format_localized('m', 'O'); - } - - void on_dec0_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, - pad); - format_localized('U', 'O'); - } - void on_dec1_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) { - auto wday = tm_wday(); - write2((tm_yday() + days_per_week - - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week, - pad); - } else { - format_localized('W', 'O'); - } - } - void on_iso_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_iso_week_of_year(), pad); - format_localized('V', 'O'); - } - - void on_iso_week_based_year() { write_year(tm_iso_week_year()); } - void on_iso_week_based_short_year() { - write2(split_year_lower(tm_iso_week_year())); - } - - void on_day_of_year() { - auto yday = tm_yday() + 1; - write1(yday / 100); - write2(yday % 100); - } - void on_day_of_month(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_mday(), pad); - format_localized('d', 'O'); - } - - void on_24_hour(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_hour(), pad); - format_localized('H', 'O'); - } - void on_12_hour(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_hour12(), pad); - format_localized('I', 'O'); - } - void on_minute(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) - return write2(tm_min(), pad); - format_localized('M', 'O'); - } - - void on_second(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) { - write2(tm_sec(), pad); - if (subsecs_) { - if (std::is_floating_point::value) { - auto buf = memory_buffer(); - write_floating_seconds(buf, *subsecs_); - if (buf.size() > 1) { - // Remove the leading "0", write something like ".123". - out_ = std::copy(buf.begin() + 1, buf.end(), out_); - } - } else { - write_fractional_seconds(out_, *subsecs_); - } - } - } else { - // Currently no formatting of subseconds when a locale is set. - format_localized('S', 'O'); - } - } - - void on_12_hour_time() { - if (is_classic_) { - char buf[8]; - write_digit2_separated(buf, to_unsigned(tm_hour12()), - to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); - out_ = copy(std::begin(buf), std::end(buf), out_); - *out_++ = ' '; - on_am_pm(); - } else { - format_localized('r'); - } - } - void on_24_hour_time() { - write2(tm_hour()); - *out_++ = ':'; - write2(tm_min()); - } - void on_iso_time() { - on_24_hour_time(); - *out_++ = ':'; - on_second(numeric_system::standard, pad_type::zero); - } - - void on_am_pm() { - if (is_classic_) { - *out_++ = tm_hour() < 12 ? 'A' : 'P'; - *out_++ = 'M'; - } else { - format_localized('p'); - } - } - - // These apply to chrono durations but not tm. - void on_duration_value() {} - void on_duration_unit() {} -}; - -struct chrono_format_checker : null_chrono_spec_handler { - bool has_precision_integral = false; - - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } - - template - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_12_hour_time() {} - FMT_CONSTEXPR void on_24_hour_time() {} - FMT_CONSTEXPR void on_iso_time() {} - FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_duration_value() const { - if (has_precision_integral) { - FMT_THROW(format_error("precision not allowed for this argument type")); - } - } - FMT_CONSTEXPR void on_duration_unit() {} -}; - -template ::value&& has_isfinite::value)> -inline auto isfinite(T) -> bool { - return true; -} - -template ::value)> -inline auto mod(T x, int y) -> T { - return x % static_cast(y); -} -template ::value)> -inline auto mod(T x, int y) -> T { - return std::fmod(x, static_cast(y)); -} - -// If T is an integral type, maps T to its unsigned counterpart, otherwise -// leaves it unchanged (unlike std::make_unsigned). -template ::value> -struct make_unsigned_or_unchanged { - using type = T; -}; - -template struct make_unsigned_or_unchanged { - using type = typename std::make_unsigned::type; -}; - -template ::value)> -inline auto get_milliseconds(std::chrono::duration d) - -> std::chrono::duration { - // this may overflow and/or the result may not fit in the - // target type. -#if FMT_SAFE_DURATION_CAST - using CommonSecondsType = - typename std::common_type::type; - const auto d_as_common = fmt_duration_cast(d); - const auto d_as_whole_seconds = - fmt_duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = - fmt_duration_cast>(diff); - return ms; -#else - auto s = fmt_duration_cast(d); - return fmt_duration_cast(d - s); -#endif -} - -template ::value)> -auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { - return write(out, val); -} - -template ::value)> -auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { - auto specs = format_specs(); - specs.precision = precision; - specs.type = - precision >= 0 ? presentation_type::fixed : presentation_type::general; - return write(out, val, specs); -} - -template -auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { - return std::copy(unit.begin(), unit.end(), out); -} - -template -auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { - // This works when wchar_t is UTF-32 because units only contain characters - // that have the same representation in UTF-16 and UTF-32. - utf8_to_utf16 u(unit); - return std::copy(u.c_str(), u.c_str() + u.size(), out); -} - -template -auto format_duration_unit(OutputIt out) -> OutputIt { - if (const char* unit = get_units()) - return copy_unit(string_view(unit), out, Char()); - *out++ = '['; - out = write(out, Period::num); - if (const_check(Period::den != 1)) { - *out++ = '/'; - out = write(out, Period::den); - } - *out++ = ']'; - *out++ = 's'; - return out; -} - -class get_locale { - private: - union { - std::locale locale_; - }; - bool has_locale_ = false; - - public: - get_locale(bool localized, locale_ref loc) : has_locale_(localized) { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR - if (localized) - ::new (&locale_) std::locale(loc.template get()); -#endif - } - ~get_locale() { - if (has_locale_) locale_.~locale(); - } - operator const std::locale&() const { - return has_locale_ ? locale_ : get_classic_locale(); - } -}; - -template -struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; - bool localized = false; - // rep is unsigned to avoid overflow. - using rep = - conditional_t::value && sizeof(Rep) < sizeof(int), - unsigned, typename make_unsigned_or_unchanged::type>; - rep val; - using seconds = std::chrono::duration; - seconds s; - using milliseconds = std::chrono::duration; - bool negative; - - using char_type = typename FormatContext::char_type; - using tm_writer_type = tm_writer; - - chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), - negative(false) { - if (d.count() < 0) { - val = 0 - val; - negative = true; - } - - // this may overflow and/or the result may not fit in the - // target type. - // might need checked conversion (rep!=Rep) - s = fmt_duration_cast(std::chrono::duration(val)); - } - - // returns true if nan or inf, writes to out. - auto handle_nan_inf() -> bool { - if (isfinite(val)) { - return false; - } - if (isnan(val)) { - write_nan(); - return true; - } - // must be +-inf - if (val > 0) { - write_pinf(); - } else { - write_ninf(); - } - return true; - } - - auto days() const -> Rep { return static_cast(s.count() / 86400); } - auto hour() const -> Rep { - return static_cast(mod((s.count() / 3600), 24)); - } - - auto hour12() const -> Rep { - Rep hour = static_cast(mod((s.count() / 3600), 12)); - return hour <= 0 ? 12 : hour; - } - - auto minute() const -> Rep { - return static_cast(mod((s.count() / 60), 60)); - } - auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } - - auto time() const -> std::tm { - auto time = std::tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); - time.tm_min = to_nonnegative_int(minute(), 60); - time.tm_sec = to_nonnegative_int(second(), 60); - return time; - } - - void write_sign() { - if (negative) { - *out++ = '-'; - negative = false; - } - } - - void write(Rep value, int width, pad_type pad = pad_type::zero) { - write_sign(); - if (isnan(value)) return write_nan(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(value, max_value())); - int num_digits = detail::count_digits(n); - if (width > num_digits) { - out = detail::write_padding(out, pad, width - num_digits); - } - out = format_decimal(out, n, num_digits).end; - } - - void write_nan() { std::copy_n("nan", 3, out); } - void write_pinf() { std::copy_n("inf", 3, out); } - void write_ninf() { std::copy_n("-inf", 4, out); } - - template - void format_tm(const tm& time, Callback cb, Args... args) { - if (isnan(val)) return write_nan(); - get_locale loc(localized, context.locale()); - auto w = tm_writer_type(loc, out, time); - (w.*cb)(args...); - out = w.out(); - } - - void on_text(const char_type* begin, const char_type* end) { - std::copy(begin, end, out); - } - - // These are not implemented because durations don't have date information. - void on_abbr_weekday() {} - void on_full_weekday() {} - void on_dec0_weekday(numeric_system) {} - void on_dec1_weekday(numeric_system) {} - void on_abbr_month() {} - void on_full_month() {} - void on_datetime(numeric_system) {} - void on_loc_date(numeric_system) {} - void on_loc_time(numeric_system) {} - void on_us_date() {} - void on_iso_date() {} - void on_utc_offset(numeric_system) {} - void on_tz_name() {} - void on_year(numeric_system) {} - void on_short_year(numeric_system) {} - void on_offset_year() {} - void on_century(numeric_system) {} - void on_iso_week_based_year() {} - void on_iso_week_based_short_year() {} - void on_dec_month(numeric_system) {} - void on_dec0_week_of_year(numeric_system, pad_type) {} - void on_dec1_week_of_year(numeric_system, pad_type) {} - void on_iso_week_of_year(numeric_system, pad_type) {} - void on_day_of_month(numeric_system, pad_type) {} - - void on_day_of_year() { - if (handle_nan_inf()) return; - write(days(), 0); - } - - void on_24_hour(numeric_system ns, pad_type pad) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(hour(), 2, pad); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); - format_tm(time, &tm_writer_type::on_24_hour, ns, pad); - } - - void on_12_hour(numeric_system ns, pad_type pad) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(hour12(), 2, pad); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour12(), 12); - format_tm(time, &tm_writer_type::on_12_hour, ns, pad); - } - - void on_minute(numeric_system ns, pad_type pad) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(minute(), 2, pad); - auto time = tm(); - time.tm_min = to_nonnegative_int(minute(), 60); - format_tm(time, &tm_writer_type::on_minute, ns, pad); - } - - void on_second(numeric_system ns, pad_type pad) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) { - if (std::is_floating_point::value) { - auto buf = memory_buffer(); - write_floating_seconds(buf, std::chrono::duration(val), - precision); - if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') { - out = detail::write_padding(out, pad); - } - out = std::copy(buf.begin(), buf.end(), out); - } else { - write(second(), 2, pad); - write_fractional_seconds( - out, std::chrono::duration(val), precision); - } - return; - } - auto time = tm(); - time.tm_sec = to_nonnegative_int(second(), 60); - format_tm(time, &tm_writer_type::on_second, ns, pad); - } - - void on_12_hour_time() { - if (handle_nan_inf()) return; - format_tm(time(), &tm_writer_type::on_12_hour_time); - } - - void on_24_hour_time() { - if (handle_nan_inf()) { - *out++ = ':'; - handle_nan_inf(); - return; - } - - write(hour(), 2); - *out++ = ':'; - write(minute(), 2); - } - - void on_iso_time() { - on_24_hour_time(); - *out++ = ':'; - if (handle_nan_inf()) return; - on_second(numeric_system::standard, pad_type::zero); - } - - void on_am_pm() { - if (handle_nan_inf()) return; - format_tm(time(), &tm_writer_type::on_am_pm); - } - - void on_duration_value() { - if (handle_nan_inf()) return; - write_sign(); - out = format_duration_value(out, val, precision); - } - - void on_duration_unit() { - out = format_duration_unit(out); - } -}; - -} // namespace detail - -#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 -using weekday = std::chrono::weekday; -using day = std::chrono::day; -using month = std::chrono::month; -using year = std::chrono::year; -using year_month_day = std::chrono::year_month_day; -#else -// A fallback version of weekday. -class weekday { - private: - unsigned char value_; - - public: - weekday() = default; - constexpr explicit weekday(unsigned wd) noexcept - : value_(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned { return value_; } -}; - -class day { - private: - unsigned char value_; - - public: - day() = default; - constexpr explicit day(unsigned d) noexcept - : value_(static_cast(d)) {} - constexpr explicit operator unsigned() const noexcept { return value_; } -}; - -class month { - private: - unsigned char value_; - - public: - month() = default; - constexpr explicit month(unsigned m) noexcept - : value_(static_cast(m)) {} - constexpr explicit operator unsigned() const noexcept { return value_; } -}; - -class year { - private: - int value_; - - public: - year() = default; - constexpr explicit year(int y) noexcept : value_(y) {} - constexpr explicit operator int() const noexcept { return value_; } -}; - -class year_month_day { - private: - fmt::year year_; - fmt::month month_; - fmt::day day_; - - public: - year_month_day() = default; - constexpr year_month_day(const year& y, const month& m, const day& d) noexcept - : year_(y), month_(m), day_(d) {} - constexpr auto year() const noexcept -> fmt::year { return year_; } - constexpr auto month() const noexcept -> fmt::month { return month_; } - constexpr auto day() const noexcept -> fmt::day { return day_; } -}; -#endif - -template -struct formatter : private formatter { - private: - bool localized_ = false; - bool use_tm_formatter_ = false; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end && *it == 'L') { - ++it; - localized_ = true; - return it; - } - use_tm_formatter_ = it != end && *it != '}'; - return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_wday = static_cast(wd.c_encoding()); - if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(localized_, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_abbr_weekday(); - return w.out(); - } -}; - -template -struct formatter : private formatter { - private: - bool use_tm_formatter_ = false; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - use_tm_formatter_ = it != end && *it != '}'; - return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_mday = static_cast(static_cast(d)); - if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(false, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); - return w.out(); - } -}; - -template -struct formatter : private formatter { - private: - bool localized_ = false; - bool use_tm_formatter_ = false; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end && *it == 'L') { - ++it; - localized_ = true; - return it; - } - use_tm_formatter_ = it != end && *it != '}'; - return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_mon = static_cast(static_cast(m)) - 1; - if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(localized_, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_abbr_month(); - return w.out(); - } -}; - -template -struct formatter : private formatter { - private: - bool use_tm_formatter_ = false; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - use_tm_formatter_ = it != end && *it != '}'; - return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_year = static_cast(y) - 1900; - if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(false, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_year(detail::numeric_system::standard); - return w.out(); - } -}; - -template -struct formatter : private formatter { - private: - bool use_tm_formatter_ = false; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - use_tm_formatter_ = it != end && *it != '}'; - return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(year_month_day val, FormatContext& ctx) const - -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_year = static_cast(val.year()) - 1900; - time.tm_mon = static_cast(static_cast(val.month())) - 1; - time.tm_mday = static_cast(static_cast(val.day())); - if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(true, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_iso_date(); - return w.out(); - } -}; - -template -struct formatter, Char> { - private: - format_specs specs_; - detail::arg_ref width_ref_; - detail::arg_ref precision_ref_; - bool localized_ = false; - basic_string_view format_str_; - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it == end || *it == '}') return it; - - it = detail::parse_align(it, end, specs_); - if (it == end) return it; - - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; - - auto checker = detail::chrono_format_checker(); - if (*it == '.') { - checker.has_precision_integral = !std::is_floating_point::value; - it = detail::parse_precision(it, end, specs_.precision, precision_ref_, - ctx); - } - if (it != end && *it == 'L') { - localized_ = true; - ++it; - } - end = detail::parse_chrono_format(it, end, checker); - format_str_ = {it, detail::to_unsigned(end - it)}; - return end; - } - - template - auto format(std::chrono::duration d, FormatContext& ctx) const - -> decltype(ctx.out()) { - auto specs = specs_; - auto precision = specs.precision; - specs.precision = -1; - auto begin = format_str_.begin(), end = format_str_.end(); - // As a possible future optimization, we could avoid extra copying if width - // is not specified. - auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - detail::handle_dynamic_spec(precision, - precision_ref_, ctx); - if (begin == end || *begin == '}') { - out = detail::format_duration_value(out, d.count(), precision); - detail::format_duration_unit(out); - } else { - using chrono_formatter = - detail::chrono_formatter; - auto f = chrono_formatter(ctx, out, d); - f.precision = precision; - f.localized = localized_; - detail::parse_chrono_format(begin, end, f); - } - return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } -}; - -template -struct formatter, - Char> : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - std::tm tm = gmtime(val); - using period = typename Duration::period; - if (detail::const_check( - period::num == 1 && period::den == 1 && - !std::is_floating_point::value)) { - return formatter::format(tm, ctx); - } - Duration epoch = val.time_since_epoch(); - Duration subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); - if (subsecs.count() < 0) { - auto second = - detail::fmt_duration_cast(std::chrono::seconds(1)); - if (tm.tm_sec != 0) - --tm.tm_sec; - else - tm = gmtime(val - second); - subsecs += detail::fmt_duration_cast(std::chrono::seconds(1)); - } - return formatter::do_format(tm, ctx, &subsecs); - } -}; - -#if FMT_USE_LOCAL_TIME -template -struct formatter, Char> - : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::local_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { - using period = typename Duration::period; - if (period::num != 1 || period::den != 1 || - std::is_floating_point::value) { - const auto epoch = val.time_since_epoch(); - const auto subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); - - return formatter::do_format(localtime(val), ctx, &subsecs); - } - - return formatter::format(localtime(val), ctx); - } -}; -#endif - -#if FMT_USE_UTC_TIME -template -struct formatter, - Char> - : formatter, - Char> { - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter< - std::chrono::time_point, - Char>::format(std::chrono::utc_clock::to_sys(val), ctx); - } -}; -#endif - -template struct formatter { - private: - format_specs specs_; - detail::arg_ref width_ref_; - - protected: - basic_string_view format_str_; - - template - auto do_format(const std::tm& tm, FormatContext& ctx, - const Duration* subsecs) const -> decltype(ctx.out()) { - auto specs = specs_; - auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - - auto loc_ref = ctx.locale(); - detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = - detail::tm_writer(loc, out, tm, subsecs); - detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w); - return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it == end || *it == '}') return it; - - it = detail::parse_align(it, end, specs_); - if (it == end) return it; - - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; - - end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); - // Replace the default format_str only if the new spec is not empty. - if (end != it) format_str_ = {it, detail::to_unsigned(end - it)}; - return end; - } - - template - auto format(const std::tm& tm, FormatContext& ctx) const - -> decltype(ctx.out()) { - return do_format(tm, ctx, nullptr); - } -}; - -FMT_END_EXPORT -FMT_END_NAMESPACE - -#endif // FMT_CHRONO_H_ -- cgit