Fix handling of chrono durations with minimal signed rep

This commit is contained in:
Victor Zverovich 2019-06-01 09:07:49 -07:00
parent 87e4ea2906
commit 78daa50ffc
2 changed files with 29 additions and 9 deletions

View File

@ -401,6 +401,17 @@ inline T mod(T x, int y) {
return std::fmod(x, y); return std::fmod(x, y);
} }
// If T is an integral type, maps T to its unsigned counterpart, otherwise
// leaves it unchanged (unlike std::make_unsigned).
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
struct make_unsigned_or_unchanged {
using type = T;
};
template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
template <typename Rep, typename Period, template <typename Rep, typename Period,
typename std::enable_if<std::is_integral<Rep>::value, int>::type = 0> typename std::enable_if<std::is_integral<Rep>::value, int>::type = 0>
inline std::chrono::duration<Rep, std::milli> get_milliseconds( inline std::chrono::duration<Rep, std::milli> get_milliseconds(
@ -438,21 +449,27 @@ struct chrono_formatter {
FormatContext& context; FormatContext& context;
OutputIt out; OutputIt out;
int precision; int precision;
typedef typename std::conditional<std::is_integral<Rep>::value && // rep is unsigned to avoid overflow.
sizeof(Rep) < sizeof(int), using rep = typename std::conditional<
int, Rep>::type rep; std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int), unsigned,
typename make_unsigned_or_unchanged<Rep>::type>::type;
rep val; rep val;
typedef std::chrono::duration<rep> seconds; typedef std::chrono::duration<rep> seconds;
seconds s; seconds s;
typedef std::chrono::duration<rep, std::milli> milliseconds; typedef std::chrono::duration<rep, std::milli> milliseconds;
bool negative;
typedef typename FormatContext::char_type char_type; typedef typename FormatContext::char_type char_type;
explicit chrono_formatter(FormatContext& ctx, OutputIt o, explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d) std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()) { : context(ctx), out(o), val(d.count()), negative(false) {
if (d.count() < 0) d = -d; if (d.count() < 0) {
s = std::chrono::duration_cast<seconds>(d); val = -val;
negative = true;
}
s = std::chrono::duration_cast<seconds>(
std::chrono::duration<rep, Period>(val));
} }
Rep hour() const { return mod((s.count() / 3600), 24); } Rep hour() const { return mod((s.count() / 3600), 24); }
@ -474,9 +491,9 @@ struct chrono_formatter {
} }
void write_sign() { void write_sign() {
if (val < 0) { if (negative) {
*out++ = '-'; *out++ = '-';
val = -val; negative = false;
} }
} }

View File

@ -306,7 +306,7 @@ TEST(ChronoTest, InvalidColons) {
fmt::format_error); fmt::format_error);
} }
TEST(ChronoTest, NegativeDuration) { TEST(ChronoTest, NegativeDurations) {
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-03:25:45", EXPECT_EQ("-03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
@ -316,6 +316,9 @@ TEST(ChronoTest, NegativeDuration) {
EXPECT_EQ("-00.127", EXPECT_EQ("-00.127",
fmt::format("{:%S}", fmt::format("{:%S}",
std::chrono::duration<signed char, std::milli>{-127})); std::chrono::duration<signed char, std::milli>{-127}));
auto min = std::numeric_limits<int>::min();
EXPECT_EQ(fmt::format("{}", min),
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
} }
TEST(ChronoTest, SpecialDurations) { TEST(ChronoTest, SpecialDurations) {