|
|
|
@ -18,7 +18,7 @@
|
|
|
|
|
#include <ostream>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
|
|
#include "format.h"
|
|
|
|
|
#include "ostream.h" // formatbuf
|
|
|
|
|
|
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
|
@ -72,7 +72,8 @@ template <typename To, typename From,
|
|
|
|
|
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
|
|
|
|
std::numeric_limits<From>::is_signed ==
|
|
|
|
|
std::numeric_limits<To>::is_signed)>
|
|
|
|
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|
|
|
|
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
|
|
|
|
|
-> To {
|
|
|
|
|
ec = 0;
|
|
|
|
|
using F = std::numeric_limits<From>;
|
|
|
|
|
using T = std::numeric_limits<To>;
|
|
|
|
@ -101,7 +102,8 @@ template <typename To, typename From,
|
|
|
|
|
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
|
|
|
|
std::numeric_limits<From>::is_signed !=
|
|
|
|
|
std::numeric_limits<To>::is_signed)>
|
|
|
|
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|
|
|
|
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
|
|
|
|
|
-> To {
|
|
|
|
|
ec = 0;
|
|
|
|
|
using F = std::numeric_limits<From>;
|
|
|
|
|
using T = std::numeric_limits<To>;
|
|
|
|
@ -133,7 +135,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|
|
|
|
|
|
|
|
|
template <typename To, typename From,
|
|
|
|
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
|
|
|
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|
|
|
|
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
|
|
|
|
|
-> To {
|
|
|
|
|
ec = 0;
|
|
|
|
|
return from;
|
|
|
|
|
} // function
|
|
|
|
@ -154,7 +157,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|
|
|
|
// clang-format on
|
|
|
|
|
template <typename To, typename From,
|
|
|
|
|
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
|
|
|
|
|
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
|
|
|
|
FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
|
|
|
|
|
ec = 0;
|
|
|
|
|
using T = std::numeric_limits<To>;
|
|
|
|
|
static_assert(std::is_floating_point<From>::value, "From must be floating");
|
|
|
|
@ -176,7 +179,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
|
|
|
|
|
|
|
|
|
template <typename To, typename From,
|
|
|
|
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
|
|
|
|
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
|
|
|
|
FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
|
|
|
|
|
ec = 0;
|
|
|
|
|
static_assert(std::is_floating_point<From>::value, "From must be floating");
|
|
|
|
|
return from;
|
|
|
|
@ -188,8 +191,8 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
|
|
|
|
template <typename To, typename FromRep, typename FromPeriod,
|
|
|
|
|
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
|
|
|
|
|
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
|
|
|
|
|
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
int& ec) {
|
|
|
|
|
auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
int& ec) -> To {
|
|
|
|
|
using From = std::chrono::duration<FromRep, FromPeriod>;
|
|
|
|
|
ec = 0;
|
|
|
|
|
// the basic idea is that we need to convert from count() in the from type
|
|
|
|
@ -240,8 +243,8 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
template <typename To, typename FromRep, typename FromPeriod,
|
|
|
|
|
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
|
|
|
|
|
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
|
|
|
|
|
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
int& ec) {
|
|
|
|
|
auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
int& ec) -> To {
|
|
|
|
|
using From = std::chrono::duration<FromRep, FromPeriod>;
|
|
|
|
|
ec = 0;
|
|
|
|
|
if (std::isnan(from.count())) {
|
|
|
|
@ -321,12 +324,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
template <typename T = void> struct null {};
|
|
|
|
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
|
|
|
|
inline null<> localtime_s(...) { return null<>(); }
|
|
|
|
|
inline null<> gmtime_r(...) { return null<>(); }
|
|
|
|
|
inline null<> gmtime_s(...) { return 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<>(); }
|
|
|
|
|
|
|
|
|
|
inline const std::locale& get_classic_locale() {
|
|
|
|
|
inline auto get_classic_locale() -> const std::locale& {
|
|
|
|
|
static const auto& locale = std::locale::classic();
|
|
|
|
|
return locale;
|
|
|
|
|
}
|
|
|
|
@ -336,8 +339,6 @@ template <typename CodeUnit> struct codecvt_result {
|
|
|
|
|
CodeUnit buf[max_size];
|
|
|
|
|
CodeUnit* end;
|
|
|
|
|
};
|
|
|
|
|
template <typename CodeUnit>
|
|
|
|
|
constexpr const size_t codecvt_result<CodeUnit>::max_size;
|
|
|
|
|
|
|
|
|
|
template <typename CodeUnit>
|
|
|
|
|
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
|
|
|
|
@ -377,8 +378,8 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
|
|
|
|
|
unit_t unit;
|
|
|
|
|
write_codecvt(unit, in, loc);
|
|
|
|
|
// In UTF-8 is used one to four one-byte code units.
|
|
|
|
|
unicode_to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>
|
|
|
|
|
u;
|
|
|
|
|
auto u =
|
|
|
|
|
to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
|
|
|
|
|
if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
|
|
|
|
|
FMT_THROW(format_error("failed to format time"));
|
|
|
|
|
return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
|
|
|
|
@ -408,8 +409,7 @@ inline void do_write(buffer<Char>& buf, const std::tm& time,
|
|
|
|
|
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
|
|
|
|
auto&& os = std::basic_ostream<Char>(&format_buf);
|
|
|
|
|
os.imbue(loc);
|
|
|
|
|
using iterator = std::ostreambuf_iterator<Char>;
|
|
|
|
|
const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
|
|
|
|
|
const auto& facet = std::use_facet<std::time_put<Char>>(loc);
|
|
|
|
|
auto end = facet.put(os, os, Char(' '), &time, format, modifier);
|
|
|
|
|
if (end.failed()) FMT_THROW(format_error("failed to format time"));
|
|
|
|
|
}
|
|
|
|
@ -432,6 +432,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
|
|
|
|
|
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Rep1, typename Rep2>
|
|
|
|
|
struct is_same_arithmetic_type
|
|
|
|
|
: public std::integral_constant<bool,
|
|
|
|
|
(std::is_integral<Rep1>::value &&
|
|
|
|
|
std::is_integral<Rep2>::value) ||
|
|
|
|
|
(std::is_floating_point<Rep1>::value &&
|
|
|
|
|
std::is_floating_point<Rep2>::value)> {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename To, typename FromRep, typename FromPeriod,
|
|
|
|
|
FMT_ENABLE_IF(is_same_arithmetic_type<FromRep, typename To::rep>::value)>
|
|
|
|
|
auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> 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<To>(from, ec);
|
|
|
|
|
if (ec) FMT_THROW(format_error("cannot format duration"));
|
|
|
|
|
return to;
|
|
|
|
|
#else
|
|
|
|
|
// Standard duration cast, may overflow.
|
|
|
|
|
return std::chrono::duration_cast<To>(from);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename To, typename FromRep, typename FromPeriod,
|
|
|
|
|
FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
|
|
|
|
|
auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
|
|
|
|
// Mixed integer <-> float cast is not supported by safe_duration_cast.
|
|
|
|
|
return std::chrono::duration_cast<To>(from);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Duration>
|
|
|
|
|
auto to_time_t(
|
|
|
|
|
std::chrono::time_point<std::chrono::system_clock, Duration> 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<std::chrono::duration<std::time_t>>(
|
|
|
|
|
time_point.time_since_epoch())
|
|
|
|
|
.count();
|
|
|
|
|
}
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
FMT_BEGIN_EXPORT
|
|
|
|
@ -441,29 +486,29 @@ FMT_BEGIN_EXPORT
|
|
|
|
|
expressed in local time. Unlike ``std::localtime``, this function is
|
|
|
|
|
thread-safe on most platforms.
|
|
|
|
|
*/
|
|
|
|
|
inline std::tm localtime(std::time_t time) {
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
|
|
bool run() {
|
|
|
|
|
auto run() -> bool {
|
|
|
|
|
using namespace fmt::detail;
|
|
|
|
|
return handle(localtime_r(&time_, &tm_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
|
|
|
|
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
|
|
|
|
|
|
|
|
|
bool handle(detail::null<>) {
|
|
|
|
|
auto handle(detail::null<>) -> bool {
|
|
|
|
|
using namespace fmt::detail;
|
|
|
|
|
return fallback(localtime_s(&tm_, &time_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fallback(int res) { return res == 0; }
|
|
|
|
|
auto fallback(int res) -> bool { return res == 0; }
|
|
|
|
|
|
|
|
|
|
#if !FMT_MSC_VERSION
|
|
|
|
|
bool fallback(detail::null<>) {
|
|
|
|
|
auto fallback(detail::null<>) -> bool {
|
|
|
|
|
using namespace fmt::detail;
|
|
|
|
|
std::tm* tm = std::localtime(&time_);
|
|
|
|
|
if (tm) tm_ = *tm;
|
|
|
|
@ -480,8 +525,8 @@ inline std::tm localtime(std::time_t time) {
|
|
|
|
|
#if FMT_USE_LOCAL_TIME
|
|
|
|
|
template <typename Duration>
|
|
|
|
|
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
|
|
|
|
|
return localtime(std::chrono::system_clock::to_time_t(
|
|
|
|
|
std::chrono::current_zone()->to_sys(time)));
|
|
|
|
|
return localtime(
|
|
|
|
|
detail::to_time_t(std::chrono::current_zone()->to_sys(time)));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
@ -490,90 +535,49 @@ inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
|
|
|
|
|
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
|
|
|
|
|
function is thread-safe on most platforms.
|
|
|
|
|
*/
|
|
|
|
|
inline std::tm gmtime(std::time_t time) {
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
|
|
bool run() {
|
|
|
|
|
auto run() -> bool {
|
|
|
|
|
using namespace fmt::detail;
|
|
|
|
|
return handle(gmtime_r(&time_, &tm_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
|
|
|
|
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
|
|
|
|
|
|
|
|
|
bool handle(detail::null<>) {
|
|
|
|
|
auto handle(detail::null<>) -> bool {
|
|
|
|
|
using namespace fmt::detail;
|
|
|
|
|
return fallback(gmtime_s(&tm_, &time_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fallback(int res) { return res == 0; }
|
|
|
|
|
auto fallback(int res) -> bool { return res == 0; }
|
|
|
|
|
|
|
|
|
|
#if !FMT_MSC_VERSION
|
|
|
|
|
bool fallback(detail::null<>) {
|
|
|
|
|
auto fallback(detail::null<>) -> bool {
|
|
|
|
|
std::tm* tm = std::gmtime(&time_);
|
|
|
|
|
if (tm) tm_ = *tm;
|
|
|
|
|
return tm != nullptr;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
dispatcher gt(time);
|
|
|
|
|
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_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::tm gmtime(
|
|
|
|
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
|
|
|
|
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
|
|
|
|
template <typename Duration>
|
|
|
|
|
inline auto gmtime(
|
|
|
|
|
std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
|
|
|
|
|
-> std::tm {
|
|
|
|
|
return gmtime(detail::to_time_t(time_point));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FMT_BEGIN_DETAIL_NAMESPACE
|
|
|
|
|
|
|
|
|
|
// DEPRECATED!
|
|
|
|
|
template <typename Char>
|
|
|
|
|
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
|
|
|
|
|
format_specs<Char>& specs) -> const Char* {
|
|
|
|
|
FMT_ASSERT(begin != end, "");
|
|
|
|
|
auto align = align::none;
|
|
|
|
|
auto p = begin + code_point_length(begin);
|
|
|
|
|
if (end - p <= 0) p = begin;
|
|
|
|
|
for (;;) {
|
|
|
|
|
switch (to_ascii(*p)) {
|
|
|
|
|
case '<':
|
|
|
|
|
align = align::left;
|
|
|
|
|
break;
|
|
|
|
|
case '>':
|
|
|
|
|
align = align::right;
|
|
|
|
|
break;
|
|
|
|
|
case '^':
|
|
|
|
|
align = align::center;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (align != align::none) {
|
|
|
|
|
if (p != begin) {
|
|
|
|
|
auto c = *begin;
|
|
|
|
|
if (c == '}') return begin;
|
|
|
|
|
if (c == '{') {
|
|
|
|
|
throw_format_error("invalid fill character '{'");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
specs.fill = {begin, to_unsigned(p - begin)};
|
|
|
|
|
begin = p + 1;
|
|
|
|
|
} else {
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} else if (p == begin) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
p = begin;
|
|
|
|
|
}
|
|
|
|
|
specs.align = align;
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
// Writes two-digit numbers a, b and c separated by sep to buf.
|
|
|
|
|
// The method by Pavel Novikov based on
|
|
|
|
@ -609,7 +613,8 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
|
|
|
|
|
template <typename Period>
|
|
|
|
|
FMT_CONSTEXPR inline auto get_units() -> const char* {
|
|
|
|
|
if (std::is_same<Period, std::atto>::value) return "as";
|
|
|
|
|
if (std::is_same<Period, std::femto>::value) return "fs";
|
|
|
|
|
if (std::is_same<Period, std::pico>::value) return "ps";
|
|
|
|
@ -627,8 +632,9 @@ template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
|
|
|
|
|
if (std::is_same<Period, std::tera>::value) return "Ts";
|
|
|
|
|
if (std::is_same<Period, std::peta>::value) return "Ps";
|
|
|
|
|
if (std::is_same<Period, std::exa>::value) return "Es";
|
|
|
|
|
if (std::is_same<Period, std::ratio<60>>::value) return "m";
|
|
|
|
|
if (std::is_same<Period, std::ratio<60>>::value) return "min";
|
|
|
|
|
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
|
|
|
|
|
if (std::is_same<Period, std::ratio<86400>>::value) return "d";
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -664,9 +670,8 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
|
|
|
|
|
|
|
|
|
|
// Parses a put_time-like format string and invokes handler actions.
|
|
|
|
|
template <typename Char, typename Handler>
|
|
|
|
|
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|
|
|
|
const Char* end,
|
|
|
|
|
Handler&& handler) {
|
|
|
|
|
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;
|
|
|
|
@ -997,25 +1002,25 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
|
|
|
|
FMT_CONSTEXPR void on_tz_name() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline const char* tm_wday_full_name(int wday) {
|
|
|
|
|
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 const char* tm_wday_short_name(int 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 const char* tm_mon_full_name(int mon) {
|
|
|
|
|
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 const char* tm_mon_short_name(int 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",
|
|
|
|
@ -1047,21 +1052,21 @@ inline void tzset_once() {
|
|
|
|
|
|
|
|
|
|
// Converts value to Int and checks that it's in the range [0, upper).
|
|
|
|
|
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
|
|
|
inline Int to_nonnegative_int(T value, Int upper) {
|
|
|
|
|
FMT_ASSERT(std::is_unsigned<Int>::value ||
|
|
|
|
|
(value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
|
|
|
|
|
"invalid value");
|
|
|
|
|
(void)upper;
|
|
|
|
|
inline auto to_nonnegative_int(T value, Int upper) -> Int {
|
|
|
|
|
if (!std::is_unsigned<Int>::value &&
|
|
|
|
|
(value < 0 || to_unsigned(value) > to_unsigned(upper))) {
|
|
|
|
|
FMT_THROW(fmt::format_error("chrono value is out of range"));
|
|
|
|
|
}
|
|
|
|
|
return static_cast<Int>(value);
|
|
|
|
|
}
|
|
|
|
|
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
|
|
|
inline Int to_nonnegative_int(T value, Int upper) {
|
|
|
|
|
inline auto to_nonnegative_int(T value, Int upper) -> Int {
|
|
|
|
|
if (value < 0 || value > static_cast<T>(upper))
|
|
|
|
|
FMT_THROW(format_error("invalid value"));
|
|
|
|
|
return static_cast<Int>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr long long pow10(std::uint32_t n) {
|
|
|
|
|
constexpr auto pow10(std::uint32_t n) -> long long {
|
|
|
|
|
return n == 0 ? 1 : 10 * pow10(n - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1095,13 +1100,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
|
|
|
|
|
std::chrono::seconds::rep>::type,
|
|
|
|
|
std::ratio<1, detail::pow10(num_fractional_digits)>>;
|
|
|
|
|
|
|
|
|
|
const auto fractional =
|
|
|
|
|
d - std::chrono::duration_cast<std::chrono::seconds>(d);
|
|
|
|
|
const auto fractional = d - fmt_duration_cast<std::chrono::seconds>(d);
|
|
|
|
|
const auto subseconds =
|
|
|
|
|
std::chrono::treat_as_floating_point<
|
|
|
|
|
typename subsecond_precision::rep>::value
|
|
|
|
|
? fractional.count()
|
|
|
|
|
: std::chrono::duration_cast<subsecond_precision>(fractional).count();
|
|
|
|
|
: fmt_duration_cast<subsecond_precision>(fractional).count();
|
|
|
|
|
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
|
|
|
|
|
const int num_digits = detail::count_digits(n);
|
|
|
|
|
|
|
|
|
@ -1152,11 +1156,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration,
|
|
|
|
|
num_fractional_digits = 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
|
|
|
|
|
std::fmod(val * static_cast<rep>(Duration::period::num) /
|
|
|
|
|
static_cast<rep>(Duration::period::den),
|
|
|
|
|
static_cast<rep>(60)),
|
|
|
|
|
num_fractional_digits);
|
|
|
|
|
fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
|
|
|
|
|
std::fmod(val * static_cast<rep>(Duration::period::num) /
|
|
|
|
|
static_cast<rep>(Duration::period::den),
|
|
|
|
|
static_cast<rep>(60)),
|
|
|
|
|
num_fractional_digits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename OutputIt, typename Char,
|
|
|
|
@ -1217,8 +1221,7 @@ class tm_writer {
|
|
|
|
|
return static_cast<int>(l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Algorithm:
|
|
|
|
|
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
|
|
|
|
|
// 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 =
|
|
|
|
@ -1358,7 +1361,7 @@ class tm_writer {
|
|
|
|
|
subsecs_(subsecs),
|
|
|
|
|
tm_(tm) {}
|
|
|
|
|
|
|
|
|
|
OutputIt out() const { return out_; }
|
|
|
|
|
auto out() const -> OutputIt { return out_; }
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
|
|
|
out_ = copy_str<Char>(begin, end, out_);
|
|
|
|
@ -1622,6 +1625,7 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
|
|
|
|
|
|
|
|
|
template <typename Char>
|
|
|
|
|
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) {}
|
|
|
|
@ -1640,16 +1644,16 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
|
|
|
|
|
|
|
|
|
template <typename T,
|
|
|
|
|
FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
|
|
|
|
|
inline bool isfinite(T) {
|
|
|
|
|
inline auto isfinite(T) -> bool {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
|
|
|
inline T mod(T x, int y) {
|
|
|
|
|
inline auto mod(T x, int y) -> T {
|
|
|
|
|
return x % static_cast<T>(y);
|
|
|
|
|
}
|
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
|
|
|
inline T mod(T x, int y) {
|
|
|
|
|
inline auto mod(T x, int y) -> T {
|
|
|
|
|
return std::fmod(x, static_cast<T>(y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1664,49 +1668,38 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
|
|
|
|
|
using type = typename std::make_unsigned<T>::type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if FMT_SAFE_DURATION_CAST
|
|
|
|
|
// throwing version of safe_duration_cast
|
|
|
|
|
template <typename To, typename FromRep, typename FromPeriod>
|
|
|
|
|
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
|
|
|
|
|
int ec;
|
|
|
|
|
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
|
|
|
|
|
if (ec) FMT_THROW(format_error("cannot format duration"));
|
|
|
|
|
return to;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
template <typename Rep, typename Period,
|
|
|
|
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
|
|
|
|
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|
|
|
|
std::chrono::duration<Rep, Period> d) {
|
|
|
|
|
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
|
|
|
|
|
-> std::chrono::duration<Rep, std::milli> {
|
|
|
|
|
// 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<decltype(d), std::chrono::seconds>::type;
|
|
|
|
|
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
|
|
|
|
|
const auto d_as_common = fmt_duration_cast<CommonSecondsType>(d);
|
|
|
|
|
const auto d_as_whole_seconds =
|
|
|
|
|
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
|
|
|
|
|
fmt_duration_cast<std::chrono::seconds>(d_as_common);
|
|
|
|
|
// this conversion should be nonproblematic
|
|
|
|
|
const auto diff = d_as_common - d_as_whole_seconds;
|
|
|
|
|
const auto ms =
|
|
|
|
|
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
|
|
|
|
fmt_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
|
|
|
|
return ms;
|
|
|
|
|
#else
|
|
|
|
|
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
|
|
|
|
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
|
|
|
|
|
auto s = fmt_duration_cast<std::chrono::seconds>(d);
|
|
|
|
|
return fmt_duration_cast<std::chrono::milliseconds>(d - s);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char, typename Rep, typename OutputIt,
|
|
|
|
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
|
|
|
|
OutputIt format_duration_value(OutputIt out, Rep val, int) {
|
|
|
|
|
auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt {
|
|
|
|
|
return write<Char>(out, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char, typename Rep, typename OutputIt,
|
|
|
|
|
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
|
|
|
|
|
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
|
|
|
|
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
|
|
|
|
|
auto specs = format_specs<Char>();
|
|
|
|
|
specs.precision = precision;
|
|
|
|
|
specs.type = precision >= 0 ? presentation_type::fixed_lower
|
|
|
|
@ -1715,12 +1708,12 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char, typename OutputIt>
|
|
|
|
|
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
|
|
|
|
auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt {
|
|
|
|
|
return std::copy(unit.begin(), unit.end(), out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename OutputIt>
|
|
|
|
|
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
|
|
|
|
|
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);
|
|
|
|
@ -1728,7 +1721,7 @@ OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char, typename Period, typename OutputIt>
|
|
|
|
|
OutputIt format_duration_unit(OutputIt out) {
|
|
|
|
|
auto format_duration_unit(OutputIt out) -> OutputIt {
|
|
|
|
|
if (const char* unit = get_units<Period>())
|
|
|
|
|
return copy_unit(string_view(unit), out, Char());
|
|
|
|
|
*out++ = '[';
|
|
|
|
@ -1795,18 +1788,12 @@ struct chrono_formatter {
|
|
|
|
|
|
|
|
|
|
// this may overflow and/or the result may not fit in the
|
|
|
|
|
// target type.
|
|
|
|
|
#if FMT_SAFE_DURATION_CAST
|
|
|
|
|
// might need checked conversion (rep!=Rep)
|
|
|
|
|
auto tmpval = std::chrono::duration<rep, Period>(val);
|
|
|
|
|
s = fmt_safe_duration_cast<seconds>(tmpval);
|
|
|
|
|
#else
|
|
|
|
|
s = std::chrono::duration_cast<seconds>(
|
|
|
|
|
std::chrono::duration<rep, Period>(val));
|
|
|
|
|
#endif
|
|
|
|
|
s = fmt_duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns true if nan or inf, writes to out.
|
|
|
|
|
bool handle_nan_inf() {
|
|
|
|
|
auto handle_nan_inf() -> bool {
|
|
|
|
|
if (isfinite(val)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -1823,17 +1810,22 @@ struct chrono_formatter {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
|
|
|
|
|
auto days() const -> Rep { return static_cast<Rep>(s.count() / 86400); }
|
|
|
|
|
auto hour() const -> Rep {
|
|
|
|
|
return static_cast<Rep>(mod((s.count() / 3600), 24));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rep hour12() const {
|
|
|
|
|
auto hour12() const -> Rep {
|
|
|
|
|
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
|
|
|
|
|
return hour <= 0 ? 12 : hour;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
|
|
|
|
|
Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
|
|
|
|
|
auto minute() const -> Rep {
|
|
|
|
|
return static_cast<Rep>(mod((s.count() / 60), 60));
|
|
|
|
|
}
|
|
|
|
|
auto second() const -> Rep { return static_cast<Rep>(mod(s.count(), 60)); }
|
|
|
|
|
|
|
|
|
|
std::tm time() const {
|
|
|
|
|
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);
|
|
|
|
@ -1901,10 +1893,14 @@ struct chrono_formatter {
|
|
|
|
|
void on_dec0_week_of_year(numeric_system) {}
|
|
|
|
|
void on_dec1_week_of_year(numeric_system) {}
|
|
|
|
|
void on_iso_week_of_year(numeric_system) {}
|
|
|
|
|
void on_day_of_year() {}
|
|
|
|
|
void on_day_of_month(numeric_system) {}
|
|
|
|
|
void on_day_of_month_space(numeric_system) {}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
@ -1997,7 +1993,7 @@ struct chrono_formatter {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FMT_END_DETAIL_NAMESPACE
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
|
|
|
|
|
using weekday = std::chrono::weekday;
|
|
|
|
@ -2011,7 +2007,7 @@ class weekday {
|
|
|
|
|
weekday() = default;
|
|
|
|
|
explicit constexpr weekday(unsigned wd) noexcept
|
|
|
|
|
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
|
|
|
|
|
constexpr unsigned c_encoding() const noexcept { return value; }
|
|
|
|
|
constexpr auto c_encoding() const noexcept -> unsigned { return value; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class year_month_day {};
|
|
|
|
@ -2047,80 +2043,67 @@ template <typename Char> struct formatter<weekday, Char> {
|
|
|
|
|
template <typename Rep, typename Period, typename Char>
|
|
|
|
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|
|
|
|
private:
|
|
|
|
|
format_specs<Char> specs;
|
|
|
|
|
int precision = -1;
|
|
|
|
|
using arg_ref_type = detail::arg_ref<Char>;
|
|
|
|
|
arg_ref_type width_ref;
|
|
|
|
|
arg_ref_type precision_ref;
|
|
|
|
|
bool localized = false;
|
|
|
|
|
basic_string_view<Char> format_str;
|
|
|
|
|
using duration = std::chrono::duration<Rep, Period>;
|
|
|
|
|
|
|
|
|
|
using iterator = typename basic_format_parse_context<Char>::iterator;
|
|
|
|
|
struct parse_range {
|
|
|
|
|
iterator begin;
|
|
|
|
|
iterator end;
|
|
|
|
|
};
|
|
|
|
|
format_specs<Char> specs_;
|
|
|
|
|
detail::arg_ref<Char> width_ref_;
|
|
|
|
|
detail::arg_ref<Char> precision_ref_;
|
|
|
|
|
bool localized_ = false;
|
|
|
|
|
basic_string_view<Char> format_str_;
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
|
|
|
|
|
auto begin = ctx.begin(), end = ctx.end();
|
|
|
|
|
if (begin == end || *begin == '}') return {begin, begin};
|
|
|
|
|
public:
|
|
|
|
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
|
|
|
|
-> decltype(ctx.begin()) {
|
|
|
|
|
auto it = ctx.begin(), end = ctx.end();
|
|
|
|
|
if (it == end || *it == '}') return it;
|
|
|
|
|
|
|
|
|
|
begin = detail::parse_align(begin, end, specs);
|
|
|
|
|
if (begin == end) return {begin, begin};
|
|
|
|
|
it = detail::parse_align(it, end, specs_);
|
|
|
|
|
if (it == end) return it;
|
|
|
|
|
|
|
|
|
|
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
|
|
|
|
|
if (begin == end) return {begin, begin};
|
|
|
|
|
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
|
|
|
|
|
if (it == end) return it;
|
|
|
|
|
|
|
|
|
|
auto checker = detail::chrono_format_checker();
|
|
|
|
|
if (*begin == '.') {
|
|
|
|
|
if (*it == '.') {
|
|
|
|
|
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
|
|
|
|
|
begin =
|
|
|
|
|
detail::parse_precision(begin, end, precision, precision_ref, ctx);
|
|
|
|
|
it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
|
|
|
|
|
ctx);
|
|
|
|
|
}
|
|
|
|
|
if (begin != end && *begin == 'L') {
|
|
|
|
|
++begin;
|
|
|
|
|
localized = true;
|
|
|
|
|
if (it != end && *it == 'L') {
|
|
|
|
|
localized_ = true;
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
end = detail::parse_chrono_format(begin, end, checker);
|
|
|
|
|
return {begin, end};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
|
|
|
|
-> decltype(ctx.begin()) {
|
|
|
|
|
auto range = do_parse(ctx);
|
|
|
|
|
format_str = basic_string_view<Char>(
|
|
|
|
|
&*range.begin, detail::to_unsigned(range.end - range.begin));
|
|
|
|
|
return range.end;
|
|
|
|
|
end = detail::parse_chrono_format(it, end, checker);
|
|
|
|
|
format_str_ = {it, detail::to_unsigned(end - it)};
|
|
|
|
|
return end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename FormatContext>
|
|
|
|
|
auto format(const duration& d, FormatContext& ctx) const
|
|
|
|
|
auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
|
|
|
|
|
-> decltype(ctx.out()) {
|
|
|
|
|
auto specs_copy = specs;
|
|
|
|
|
auto precision_copy = precision;
|
|
|
|
|
auto begin = format_str.begin(), end = format_str.end();
|
|
|
|
|
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.
|
|
|
|
|
basic_memory_buffer<Char> buf;
|
|
|
|
|
auto buf = basic_memory_buffer<Char>();
|
|
|
|
|
auto out = std::back_inserter(buf);
|
|
|
|
|
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
|
|
|
|
|
width_ref, ctx);
|
|
|
|
|
detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
|
|
|
|
|
precision_ref, ctx);
|
|
|
|
|
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
|
|
|
|
|
ctx);
|
|
|
|
|
detail::handle_dynamic_spec<detail::precision_checker>(precision,
|
|
|
|
|
precision_ref_, ctx);
|
|
|
|
|
if (begin == end || *begin == '}') {
|
|
|
|
|
out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
|
|
|
|
|
out = detail::format_duration_value<Char>(out, d.count(), precision);
|
|
|
|
|
detail::format_duration_unit<Char, Period>(out);
|
|
|
|
|
} else {
|
|
|
|
|
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
|
|
|
|
ctx, out, d);
|
|
|
|
|
f.precision = precision_copy;
|
|
|
|
|
f.localized = localized;
|
|
|
|
|
using chrono_formatter =
|
|
|
|
|
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
|
|
|
|
|
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<Char>(buf.data(), buf.size()), specs_copy);
|
|
|
|
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -2128,34 +2111,33 @@ template <typename Char, typename Duration>
|
|
|
|
|
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
|
|
|
|
Char> : formatter<std::tm, Char> {
|
|
|
|
|
FMT_CONSTEXPR formatter() {
|
|
|
|
|
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
|
|
|
|
this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename FormatContext>
|
|
|
|
|
auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
|
|
|
|
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
|
|
|
|
using period = typename Duration::period;
|
|
|
|
|
if (period::num != 1 || period::den != 1 ||
|
|
|
|
|
std::is_floating_point<typename Duration::rep>::value) {
|
|
|
|
|
if (detail::const_check(
|
|
|
|
|
period::num != 1 || period::den != 1 ||
|
|
|
|
|
std::is_floating_point<typename Duration::rep>::value)) {
|
|
|
|
|
const auto epoch = val.time_since_epoch();
|
|
|
|
|
auto subsecs = std::chrono::duration_cast<Duration>(
|
|
|
|
|
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
|
|
|
|
|
auto subsecs = detail::fmt_duration_cast<Duration>(
|
|
|
|
|
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
|
|
|
|
|
|
|
|
|
|
if (subsecs.count() < 0) {
|
|
|
|
|
auto second = std::chrono::seconds(1);
|
|
|
|
|
auto second =
|
|
|
|
|
detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
|
|
|
|
|
if (epoch.count() < ((Duration::min)() + second).count())
|
|
|
|
|
FMT_THROW(format_error("duration is too small"));
|
|
|
|
|
subsecs += second;
|
|
|
|
|
val -= second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return formatter<std::tm, Char>::do_format(
|
|
|
|
|
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
|
|
|
|
|
&subsecs);
|
|
|
|
|
return formatter<std::tm, Char>::do_format(gmtime(val), ctx, &subsecs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return formatter<std::tm, Char>::format(
|
|
|
|
|
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
|
|
|
|
|
return formatter<std::tm, Char>::format(gmtime(val), ctx);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -2164,7 +2146,7 @@ template <typename Char, typename Duration>
|
|
|
|
|
struct formatter<std::chrono::local_time<Duration>, Char>
|
|
|
|
|
: formatter<std::tm, Char> {
|
|
|
|
|
FMT_CONSTEXPR formatter() {
|
|
|
|
|
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
|
|
|
|
this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename FormatContext>
|
|
|
|
@ -2174,17 +2156,13 @@ struct formatter<std::chrono::local_time<Duration>, Char>
|
|
|
|
|
if (period::num != 1 || period::den != 1 ||
|
|
|
|
|
std::is_floating_point<typename Duration::rep>::value) {
|
|
|
|
|
const auto epoch = val.time_since_epoch();
|
|
|
|
|
const auto subsecs = std::chrono::duration_cast<Duration>(
|
|
|
|
|
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
|
|
|
|
|
const auto subsecs = detail::fmt_duration_cast<Duration>(
|
|
|
|
|
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
|
|
|
|
|
|
|
|
|
|
return formatter<std::tm, Char>::do_format(
|
|
|
|
|
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
|
|
|
|
ctx, &subsecs);
|
|
|
|
|
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return formatter<std::tm, Char>::format(
|
|
|
|
|
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
|
|
|
|
ctx);
|
|
|
|
|
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
@ -2207,51 +2185,46 @@ struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
|
|
|
|
|
|
|
|
|
|
template <typename Char> struct formatter<std::tm, Char> {
|
|
|
|
|
private:
|
|
|
|
|
format_specs<Char> specs;
|
|
|
|
|
detail::arg_ref<Char> width_ref;
|
|
|
|
|
format_specs<Char> specs_;
|
|
|
|
|
detail::arg_ref<Char> width_ref_;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
basic_string_view<Char> format_str;
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
|
|
|
|
|
-> decltype(ctx.begin()) {
|
|
|
|
|
auto begin = ctx.begin(), end = ctx.end();
|
|
|
|
|
if (begin == end || *begin == '}') return begin;
|
|
|
|
|
|
|
|
|
|
begin = detail::parse_align(begin, end, specs);
|
|
|
|
|
if (begin == end) return end;
|
|
|
|
|
|
|
|
|
|
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
|
|
|
|
|
if (begin == end) return end;
|
|
|
|
|
|
|
|
|
|
end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
|
|
|
|
|
// Replace default format_str only if the new spec is not empty.
|
|
|
|
|
if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
|
|
|
|
|
return end;
|
|
|
|
|
}
|
|
|
|
|
basic_string_view<Char> format_str_;
|
|
|
|
|
|
|
|
|
|
template <typename FormatContext, typename Duration>
|
|
|
|
|
auto do_format(const std::tm& tm, FormatContext& ctx,
|
|
|
|
|
const Duration* subsecs) const -> decltype(ctx.out()) {
|
|
|
|
|
auto specs_copy = specs;
|
|
|
|
|
basic_memory_buffer<Char> buf;
|
|
|
|
|
auto specs = specs_;
|
|
|
|
|
auto buf = basic_memory_buffer<Char>();
|
|
|
|
|
auto out = std::back_inserter(buf);
|
|
|
|
|
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
|
|
|
|
|
width_ref, ctx);
|
|
|
|
|
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
|
|
|
|
|
ctx);
|
|
|
|
|
|
|
|
|
|
const auto loc_ref = ctx.locale();
|
|
|
|
|
auto loc_ref = ctx.locale();
|
|
|
|
|
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
|
|
|
|
auto w =
|
|
|
|
|
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
|
|
|
|
|
detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
|
|
|
|
|
detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
|
|
|
|
|
return detail::write(
|
|
|
|
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
|
|
|
|
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
|
|
|
|
-> decltype(ctx.begin()) {
|
|
|
|
|
return this->do_parse(ctx);
|
|
|
|
|
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 <typename FormatContext>
|
|
|
|
|