Support formatting of subseconds (#3115)
* Timestamp formatting shall print also subseconds, fixed a bug for fractional durations
This commit is contained in:
parent
cfb34a0607
commit
9254cfa6f0
@ -966,13 +966,131 @@ inline void tzset_once() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename OutputIt, typename Char> class tm_writer {
|
// 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;
|
||||||
|
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) {
|
||||||
|
if (value < 0 || value > static_cast<T>(upper))
|
||||||
|
FMT_THROW(format_error("invalid value"));
|
||||||
|
return static_cast<Int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period,
|
||||||
|
FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
|
||||||
|
constexpr std::chrono::duration<Rep, Period> abs(
|
||||||
|
std::chrono::duration<Rep, Period> d) {
|
||||||
|
// We need to compare the duration using the count() method directly
|
||||||
|
// due to a compiler bug in clang-11 regarding the spaceship operator,
|
||||||
|
// when -Wzero-as-null-pointer-constant is enabled.
|
||||||
|
// In clang-12 the bug has been fixed. See
|
||||||
|
// https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
|
||||||
|
// https://www.godbolt.org/z/Knbb5joYx.
|
||||||
|
return d.count() >= d.zero().count() ? d : -d;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period,
|
||||||
|
FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
|
||||||
|
constexpr std::chrono::duration<Rep, Period> abs(
|
||||||
|
std::chrono::duration<Rep, Period> d) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr long long pow10(std::uint32_t n) {
|
||||||
|
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 <long long Num, long long Den, int N = 0,
|
||||||
|
bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
|
||||||
|
struct count_fractional_digits {
|
||||||
|
static constexpr int value =
|
||||||
|
Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base case that doesn't instantiate any more templates
|
||||||
|
// in order to avoid overflow.
|
||||||
|
template <long long Num, long long Den, int N>
|
||||||
|
struct count_fractional_digits<Num, Den, N, false> {
|
||||||
|
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 <typename Char, typename OutputIt, typename Duration>
|
||||||
|
void write_fractional_seconds(OutputIt& out, Duration d) {
|
||||||
|
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
|
||||||
|
constexpr auto num_fractional_digits =
|
||||||
|
count_fractional_digits<Duration::period::num,
|
||||||
|
Duration::period::den>::value;
|
||||||
|
|
||||||
|
using subsecond_precision = std::chrono::duration<
|
||||||
|
typename std::common_type<typename Duration::rep,
|
||||||
|
std::chrono::seconds::rep>::type,
|
||||||
|
std::ratio<1, detail::pow10(num_fractional_digits)>>;
|
||||||
|
if (std::ratio_less<typename subsecond_precision::period,
|
||||||
|
std::chrono::seconds::period>::value) {
|
||||||
|
*out++ = '.';
|
||||||
|
auto fractional =
|
||||||
|
detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
auto subseconds =
|
||||||
|
std::chrono::treat_as_floating_point<
|
||||||
|
typename subsecond_precision::rep>::value
|
||||||
|
? fractional.count()
|
||||||
|
: std::chrono::duration_cast<subsecond_precision>(fractional)
|
||||||
|
.count();
|
||||||
|
uint32_or_64_or_128_t<long long> n =
|
||||||
|
to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
|
||||||
|
int num_digits = detail::count_digits(n);
|
||||||
|
if (num_fractional_digits > num_digits)
|
||||||
|
out = std::fill_n(out, num_fractional_digits - num_digits, '0');
|
||||||
|
out = format_decimal<Char>(out, n, num_digits).end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format subseconds which are given as a floating point type with an appropiate
|
||||||
|
// number of digits. We cannot pass the Duration here, as we explicitly need to
|
||||||
|
// pass the Rep value in the chrono_formatter.
|
||||||
|
template <typename Duration>
|
||||||
|
void write_floating_seconds(memory_buffer& buf, Duration duration) {
|
||||||
|
FMT_ASSERT(std::is_floating_point<typename Duration::rep>::value, "");
|
||||||
|
auto num_fractional_digits =
|
||||||
|
count_fractional_digits<Duration::period::num,
|
||||||
|
Duration::period::den>::value;
|
||||||
|
// For non-integer values, we ensure at least 6 digits to get microsecond
|
||||||
|
// precision.
|
||||||
|
auto val = duration.count();
|
||||||
|
if (num_fractional_digits < 6 &&
|
||||||
|
static_cast<typename Duration::rep>(std::round(val)) != val)
|
||||||
|
num_fractional_digits = 6;
|
||||||
|
|
||||||
|
format_to(
|
||||||
|
std::back_inserter(buf), runtime("{:.{}f}"),
|
||||||
|
std::fmod(val *
|
||||||
|
static_cast<typename Duration::rep>(Duration::period::num) /
|
||||||
|
static_cast<typename Duration::rep>(Duration::period::den),
|
||||||
|
static_cast<typename Duration::rep>(60)),
|
||||||
|
num_fractional_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char,
|
||||||
|
typename Duration = std::chrono::seconds>
|
||||||
|
class tm_writer {
|
||||||
private:
|
private:
|
||||||
static constexpr int days_per_week = 7;
|
static constexpr int days_per_week = 7;
|
||||||
|
|
||||||
const std::locale& loc_;
|
const std::locale& loc_;
|
||||||
const bool is_classic_;
|
const bool is_classic_;
|
||||||
OutputIt out_;
|
OutputIt out_;
|
||||||
|
const Duration* subsecs_;
|
||||||
const std::tm& tm_;
|
const std::tm& tm_;
|
||||||
|
|
||||||
auto tm_sec() const noexcept -> int {
|
auto tm_sec() const noexcept -> int {
|
||||||
@ -1135,10 +1253,12 @@ template <typename OutputIt, typename Char> class tm_writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
|
tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
|
||||||
|
const Duration* subsecs = nullptr)
|
||||||
: loc_(loc),
|
: loc_(loc),
|
||||||
is_classic_(loc_ == get_classic_locale()),
|
is_classic_(loc_ == get_classic_locale()),
|
||||||
out_(out),
|
out_(out),
|
||||||
|
subsecs_(subsecs),
|
||||||
tm_(tm) {}
|
tm_(tm) {}
|
||||||
|
|
||||||
OutputIt out() const { return out_; }
|
OutputIt out() const { return out_; }
|
||||||
@ -1337,9 +1457,26 @@ template <typename OutputIt, typename Char> class tm_writer {
|
|||||||
if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
|
if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
|
||||||
format_localized('M', 'O');
|
format_localized('M', 'O');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_second(numeric_system ns) {
|
void on_second(numeric_system ns) {
|
||||||
if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
|
if (is_classic_ || ns == numeric_system::standard) {
|
||||||
format_localized('S', 'O');
|
write2(tm_sec());
|
||||||
|
if (subsecs_) {
|
||||||
|
if (std::is_floating_point<typename Duration::rep>::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<Char>(out_, *subsecs_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Currently no formatting of subseconds when a locale is set.
|
||||||
|
format_localized('S', 'O');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_12_hour_time() {
|
void on_12_hour_time() {
|
||||||
@ -1402,22 +1539,6 @@ inline bool isfinite(T) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
|
||||||
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) {
|
|
||||||
if (value < 0 || value > static_cast<T>(upper))
|
|
||||||
FMT_THROW(format_error("invalid value"));
|
|
||||||
return static_cast<Int>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
inline T mod(T x, int y) {
|
inline T mod(T x, int y) {
|
||||||
return x % static_cast<T>(y);
|
return x % static_cast<T>(y);
|
||||||
@ -1472,47 +1593,6 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 <long long Num, long long Den, int N = 0,
|
|
||||||
bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
|
|
||||||
struct count_fractional_digits {
|
|
||||||
static constexpr int value =
|
|
||||||
Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base case that doesn't instantiate any more templates
|
|
||||||
// in order to avoid overflow.
|
|
||||||
template <long long Num, long long Den, int N>
|
|
||||||
struct count_fractional_digits<Num, Den, N, false> {
|
|
||||||
static constexpr int value = (Num % Den == 0) ? N : 6;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr long long pow10(std::uint32_t n) {
|
|
||||||
return n == 0 ? 1 : 10 * pow10(n - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Rep, class Period,
|
|
||||||
FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
|
|
||||||
constexpr std::chrono::duration<Rep, Period> abs(
|
|
||||||
std::chrono::duration<Rep, Period> d) {
|
|
||||||
// We need to compare the duration using the count() method directly
|
|
||||||
// due to a compiler bug in clang-11 regarding the spaceship operator,
|
|
||||||
// when -Wzero-as-null-pointer-constant is enabled.
|
|
||||||
// In clang-12 the bug has been fixed. See
|
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
|
|
||||||
// https://www.godbolt.org/z/Knbb5joYx.
|
|
||||||
return d.count() >= d.zero().count() ? d : -d;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Rep, class Period,
|
|
||||||
FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
|
|
||||||
constexpr std::chrono::duration<Rep, Period> abs(
|
|
||||||
std::chrono::duration<Rep, Period> d) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename Rep, typename OutputIt,
|
template <typename Char, typename Rep, typename OutputIt,
|
||||||
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||||
OutputIt format_duration_value(OutputIt out, Rep val, int) {
|
OutputIt format_duration_value(OutputIt out, Rep val, int) {
|
||||||
@ -1673,36 +1753,6 @@ struct chrono_formatter {
|
|||||||
out = format_decimal<char_type>(out, n, num_digits).end;
|
out = format_decimal<char_type>(out, n, num_digits).end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Duration> void write_fractional_seconds(Duration d) {
|
|
||||||
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
|
|
||||||
constexpr auto num_fractional_digits =
|
|
||||||
count_fractional_digits<Duration::period::num,
|
|
||||||
Duration::period::den>::value;
|
|
||||||
|
|
||||||
using subsecond_precision = std::chrono::duration<
|
|
||||||
typename std::common_type<typename Duration::rep,
|
|
||||||
std::chrono::seconds::rep>::type,
|
|
||||||
std::ratio<1, detail::pow10(num_fractional_digits)>>;
|
|
||||||
if (std::ratio_less<typename subsecond_precision::period,
|
|
||||||
std::chrono::seconds::period>::value) {
|
|
||||||
*out++ = '.';
|
|
||||||
auto fractional =
|
|
||||||
detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
|
|
||||||
auto subseconds =
|
|
||||||
std::chrono::treat_as_floating_point<
|
|
||||||
typename subsecond_precision::rep>::value
|
|
||||||
? fractional.count()
|
|
||||||
: std::chrono::duration_cast<subsecond_precision>(fractional)
|
|
||||||
.count();
|
|
||||||
uint32_or_64_or_128_t<long long> n =
|
|
||||||
to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
|
|
||||||
int num_digits = detail::count_digits(n);
|
|
||||||
if (num_fractional_digits > num_digits)
|
|
||||||
out = std::fill_n(out, num_fractional_digits - num_digits, '0');
|
|
||||||
out = format_decimal<char_type>(out, n, num_digits).end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_nan() { std::copy_n("nan", 3, out); }
|
void write_nan() { std::copy_n("nan", 3, out); }
|
||||||
void write_pinf() { std::copy_n("inf", 3, out); }
|
void write_pinf() { std::copy_n("inf", 3, out); }
|
||||||
void write_ninf() { std::copy_n("-inf", 4, out); }
|
void write_ninf() { std::copy_n("-inf", 4, out); }
|
||||||
@ -1780,20 +1830,15 @@ struct chrono_formatter {
|
|||||||
|
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
if (std::is_floating_point<rep>::value) {
|
if (std::is_floating_point<rep>::value) {
|
||||||
constexpr auto num_fractional_digits =
|
|
||||||
count_fractional_digits<Period::num, Period::den>::value;
|
|
||||||
auto buf = memory_buffer();
|
auto buf = memory_buffer();
|
||||||
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val));
|
||||||
std::fmod(val * static_cast<rep>(Period::num) /
|
|
||||||
static_cast<rep>(Period::den),
|
|
||||||
static_cast<rep>(60)),
|
|
||||||
num_fractional_digits);
|
|
||||||
if (negative) *out++ = '-';
|
if (negative) *out++ = '-';
|
||||||
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
|
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
|
||||||
out = std::copy(buf.begin(), buf.end(), out);
|
out = std::copy(buf.begin(), buf.end(), out);
|
||||||
} else {
|
} else {
|
||||||
write(second(), 2);
|
write(second(), 2);
|
||||||
write_fractional_seconds(std::chrono::duration<rep, Period>(val));
|
write_fractional_seconds<char_type>(
|
||||||
|
out, std::chrono::duration<Rep, Period>(val));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2016,28 +2061,41 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
|||||||
this->do_parse(default_specs.begin(), default_specs.end());
|
this->do_parse(default_specs.begin(), default_specs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext, typename Rep, typename Period>
|
||||||
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
auto format(std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::duration<Rep, Period>>
|
||||||
|
val,
|
||||||
FormatContext& ctx) const -> decltype(ctx.out()) {
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
if (Period::num != 1 || Period::den != 1 ||
|
||||||
|
std::is_floating_point<Rep>::value) {
|
||||||
|
const auto epoch = val.time_since_epoch();
|
||||||
|
const auto subsecs =
|
||||||
|
std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
|
||||||
|
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
|
||||||
|
|
||||||
|
return formatter<std::tm, Char>::format(
|
||||||
|
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
||||||
|
ctx, subsecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatter<std::tm, Char>::format(
|
||||||
|
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
||||||
|
ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_USE_UTC_TIME
|
#if FMT_USE_UTC_TIME
|
||||||
template <typename Char, typename Duration>
|
template <typename Char, typename Duration>
|
||||||
struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
|
struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
|
||||||
Char> : formatter<std::tm, Char> {
|
Char>
|
||||||
FMT_CONSTEXPR formatter() {
|
: formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
basic_string_view<Char> default_specs =
|
Char> {
|
||||||
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
template <typename FormatContext, typename Duration>
|
||||||
this->do_parse(default_specs.begin(), default_specs.end());
|
auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(std::chrono::time_point<std::chrono::utc_clock> val,
|
|
||||||
FormatContext& ctx) const -> decltype(ctx.out()) {
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
return formatter<std::tm, Char>::format(
|
return formatter<
|
||||||
localtime(std::chrono::utc_clock::to_sys(val)), ctx);
|
std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
|
Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -2089,6 +2147,22 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
detail::parse_chrono_format(specs.begin(), specs.end(), w);
|
detail::parse_chrono_format(specs.begin(), specs.end(), w);
|
||||||
return w.out();
|
return w.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename Duration>
|
||||||
|
auto format(const std::tm& tm, FormatContext& ctx,
|
||||||
|
const Duration& subsecs) const -> decltype(ctx.out()) {
|
||||||
|
const auto loc_ref = ctx.locale();
|
||||||
|
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
||||||
|
auto w = detail::tm_writer<decltype(ctx.out()), Char, Duration>(
|
||||||
|
loc, ctx.out(), tm, &subsecs);
|
||||||
|
if (spec_ == spec::year_month_day)
|
||||||
|
w.on_iso_date();
|
||||||
|
else if (spec_ == spec::hh_mm_ss)
|
||||||
|
w.on_iso_time();
|
||||||
|
else
|
||||||
|
detail::parse_chrono_format(specs.begin(), specs.end(), w);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_MODULE_EXPORT_END
|
FMT_MODULE_EXPORT_END
|
||||||
|
@ -253,7 +253,8 @@ template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(chrono_test, time_point) {
|
TEST(chrono_test, time_point) {
|
||||||
auto t1 = std::chrono::system_clock::now();
|
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::system_clock::now());
|
||||||
EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
|
EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
|
||||||
EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1));
|
EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1));
|
||||||
using time_point =
|
using time_point =
|
||||||
@ -634,10 +635,18 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
|||||||
// fixed precision, and print zeros even if there is no fractional part.
|
// fixed precision, and print zeros even if there is no fractional part.
|
||||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
|
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
|
||||||
"07.000000");
|
"07.000000");
|
||||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
|
EXPECT_EQ(fmt::format("{:%S}",
|
||||||
|
std::chrono::duration<long long, std::ratio<1, 3>>(1)),
|
||||||
"00.333333");
|
"00.333333");
|
||||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
|
EXPECT_EQ(fmt::format("{:%S}",
|
||||||
|
std::chrono::duration<long long, std::ratio<1, 7>>(1)),
|
||||||
"00.142857");
|
"00.142857");
|
||||||
|
|
||||||
|
// Check that floating point seconds with ratio<1,1> are printed.
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<double>{1.5}),
|
||||||
|
"01.500000");
|
||||||
|
EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration<double>{-61.25}),
|
||||||
|
"-01:01.250000");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
@ -652,3 +661,66 @@ TEST(chrono_test, utc_clock) {
|
|||||||
fmt::format("{:%Y-%m-%d %H:%M:%S}", t1_utc));
|
fmt::format("{:%Y-%m-%d %H:%M:%S}", t1_utc));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(chrono_test, timestamps_sub_seconds) {
|
||||||
|
std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::duration<long long, std::ratio<1, 3>>>
|
||||||
|
t1(std::chrono::duration<long long, std::ratio<1, 3>>(4));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t1), "01.333333");
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::duration<double, std::ratio<1, 3>>>
|
||||||
|
t2(std::chrono::duration<double, std::ratio<1, 3>>(4));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t2), "01.333333");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
|
||||||
|
t3(std::chrono::seconds(2));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t3), "02");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::duration<double>>
|
||||||
|
t4(std::chrono::duration<double, std::ratio<1, 1>>(9.5));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t4), "09.500000");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::duration<double>>
|
||||||
|
t5(std::chrono::duration<double, std::ratio<1, 1>>(9));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t5), "09");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::milliseconds>
|
||||||
|
t6(std::chrono::seconds(1) + std::chrono::milliseconds(120));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t6), "01.120");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::microseconds>
|
||||||
|
t7(std::chrono::microseconds(1234567));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t7), "01.234567");
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::nanoseconds>
|
||||||
|
t8(std::chrono::nanoseconds(123456789));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t8), "00.123456789");
|
||||||
|
|
||||||
|
const auto t9 = std::chrono::time_point_cast<std::chrono::nanoseconds>(
|
||||||
|
std::chrono::system_clock::now());
|
||||||
|
const auto t9_sec = std::chrono::time_point_cast<std::chrono::seconds>(t9);
|
||||||
|
auto t9_sub_sec_part = fmt::format("{0:09}", (t9 - t9_sec).count());
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{}.{}", strftime_full(t9_sec), t9_sub_sec_part),
|
||||||
|
fmt::format("{:%Y-%m-%d %H:%M:%S}", t9));
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock,
|
||||||
|
std::chrono::milliseconds>
|
||||||
|
t10(std::chrono::milliseconds(2000));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", t10), "02.000");
|
||||||
|
}
|
||||||
|
@ -285,7 +285,8 @@ std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(chrono_test_wchar, time_point) {
|
TEST(chrono_test_wchar, time_point) {
|
||||||
auto t1 = std::chrono::system_clock::now();
|
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::system_clock::now());
|
||||||
|
|
||||||
std::vector<std::wstring> spec_list = {
|
std::vector<std::wstring> spec_list = {
|
||||||
L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C",
|
L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C",
|
||||||
|
Loading…
Reference in New Issue
Block a user