Merge f501601c683957dfb3110f04958e913261841436 into f87a75e15262614ce8aa0e6a0664df9d0823fb92

This commit is contained in:
aizu-m 2026-10-26 04:52:58 +00:00 committed by GitHub
commit ff5dcb3008
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 89 additions and 2 deletions

View File

@ -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

View File

@ -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) {