mirror of
https://github.com/fmtlib/fmt.git
synced 2026-10-29 02:17:28 +08:00
Merge f501601c683957dfb3110f04958e913261841436 into f87a75e15262614ce8aa0e6a0664df9d0823fb92
This commit is contained in:
commit
ff5dcb3008
@ -510,14 +510,44 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
using is_float_to_integral = bool_constant<std::is_floating_point<T>::value &&
|
||||
std::is_integral<U>::value>;
|
||||
|
||||
template <typename To, typename FromRep, typename FromPeriod,
|
||||
FMT_ENABLE_IF(
|
||||
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
|
||||
!is_similar_arithmetic_type<FromRep, typename To::rep>::value &&
|
||||
!is_float_to_integral<FromRep, typename To::rep>::value)>
|
||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||
// Mixed integer <-> float cast is not supported by safe duration_cast.
|
||||
// Integer to float and custom representations are not supported by safe
|
||||
// duration_cast. An integer to float conversion is always defined.
|
||||
return std::chrono::duration_cast<To>(from);
|
||||
}
|
||||
|
||||
// Cast a floating-point duration to an integral one with a range check since
|
||||
// converting an out-of-range or NaN value to an integer is undefined behavior.
|
||||
template <typename To, typename FromRep, typename FromPeriod,
|
||||
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value&&
|
||||
std::is_integral<typename To::rep>::value)>
|
||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||
#if FMT_SAFE_DURATION_CAST
|
||||
using to_rep = typename To::rep;
|
||||
auto count = detail::duration_cast<
|
||||
std::chrono::duration<FromRep, typename To::period>>(from)
|
||||
.count();
|
||||
// The conversion truncates toward zero so check the truncated value.
|
||||
// max + 1 is a power of 2 so the cast to FromRep is exact even when max
|
||||
// itself is rounded up by the conversion; the comparison is false for NaN.
|
||||
auto upper = static_cast<FromRep>(max_value<to_rep>()) + 1;
|
||||
auto lower = static_cast<FromRep>((std::numeric_limits<to_rep>::min)());
|
||||
auto truncated = std::trunc(count);
|
||||
if (!(truncated >= lower && truncated < upper)) throw_duration_error();
|
||||
return To(static_cast<to_rep>(count));
|
||||
#else
|
||||
return std::chrono::duration_cast<To>(from);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Duration>
|
||||
auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
|
||||
// Cannot use std::chrono::system_clock::to_time_t since this would first
|
||||
|
||||
@ -1067,6 +1067,63 @@ TEST(chrono_test, out_of_range) {
|
||||
// A floating-point day count that doesn't fit in int.
|
||||
auto fd = std::chrono::duration<double>(1e300);
|
||||
EXPECT_THROW((void)fmt::format("{:%j}", fd), fmt::format_error);
|
||||
// A floating-point time point whose seconds don't fit in time_t.
|
||||
auto ftp = sys_time<std::chrono::duration<double>>(fd);
|
||||
EXPECT_THROW((void)fmt::format("{:%T}", ftp), fmt::format_error);
|
||||
auto fnan = sys_time<std::chrono::duration<double>>(
|
||||
std::chrono::duration<double>(std::numeric_limits<double>::quiet_NaN()));
|
||||
EXPECT_THROW((void)fmt::format("{:%T}", fnan), fmt::format_error);
|
||||
auto flocal = fmt::local_time<std::chrono::duration<double>>(
|
||||
std::chrono::duration<double>(-1e300));
|
||||
EXPECT_THROW((void)fmt::format("{:%F}", flocal), fmt::format_error);
|
||||
}
|
||||
|
||||
TEST(chrono_test, float_to_integral_duration_cast) {
|
||||
using fsec = std::chrono::duration<double>;
|
||||
// Values that truncate toward zero into range are valid.
|
||||
EXPECT_EQ(
|
||||
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-0.5))
|
||||
.count(),
|
||||
0u);
|
||||
auto min = (std::numeric_limits<int>::min)();
|
||||
EXPECT_EQ(
|
||||
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 0.5))
|
||||
.count(),
|
||||
min);
|
||||
auto max = (std::numeric_limits<int>::max)();
|
||||
EXPECT_EQ(
|
||||
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(max + 0.5))
|
||||
.count(),
|
||||
max);
|
||||
if (FMT_SAFE_DURATION_CAST) {
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-1)),
|
||||
fmt::format_error);
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 1.0)),
|
||||
fmt::format_error);
|
||||
}
|
||||
}
|
||||
|
||||
struct custom_rep {
|
||||
long long value = 0;
|
||||
custom_rep() = default;
|
||||
custom_rep(long long v) : value(v) {}
|
||||
operator long long() const { return value; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct common_type<custom_rep, long long> {
|
||||
using type = long long;
|
||||
};
|
||||
template <> struct common_type<long long, custom_rep> {
|
||||
using type = long long;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
TEST(chrono_test, custom_rep_duration_cast) {
|
||||
auto d = std::chrono::duration<custom_rep, std::milli>(custom_rep(2500));
|
||||
EXPECT_EQ(fmt::detail::duration_cast<std::chrono::seconds>(d).count(), 2);
|
||||
}
|
||||
|
||||
TEST(chrono_test, calendar_padding) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user