Skip to content

Commit

Permalink
Fix handling of nan durations
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed May 5, 2019
1 parent c1d430e commit ca978b3
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 21 deletions.
47 changes: 31 additions & 16 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,20 @@ struct chrono_format_checker {
FMT_NORETURN void on_tz_name() { report_no_date(); }
};

template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
inline bool isnan(T) {
return false;
}
template <typename T, typename std::enable_if<std::is_floating_point<T>::value,
int>::type = 0>
inline bool isnan(T value) {
return std::isnan(value);
}

template <typename T> inline int to_int(T value) {
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
value <= (std::numeric_limits<int>::max)(),
FMT_ASSERT(isnan(value) || (value >= (std::numeric_limits<int>::min)() &&
value <= (std::numeric_limits<int>::max)()),
"invalid value");
return static_cast<int>(value);
}
Expand Down Expand Up @@ -439,33 +450,37 @@ struct chrono_formatter {
ms = std::chrono::duration_cast<milliseconds>(d - s);
}

int hour() const { return to_int(mod((s.count() / 3600), 24)); }
Rep hour() const { return mod((s.count() / 3600), 24); }

int hour12() const {
auto hour = to_int(mod((s.count() / 3600), 12));
return hour > 0 ? hour : 12;
Rep hour12() const {
Rep hour = mod((s.count() / 3600), 12);
return hour <= 0 ? 12 : hour;
}

int minute() const { return to_int(mod((s.count() / 60), 60)); }
int second() const { return to_int(mod(s.count(), 60)); }
Rep minute() const { return mod((s.count() / 60), 60); }
Rep second() const { return mod(s.count(), 60); }

std::tm time() const {
auto time = std::tm();
time.tm_hour = hour();
time.tm_min = minute();
time.tm_sec = second();
time.tm_hour = to_int(hour());
time.tm_min = to_int(minute());
time.tm_sec = to_int(second());
return time;
}

void write(int value, int width) {
void write(Rep value, int width) {
if (isnan(value)) return write_nan();
typedef typename int_traits<int>::main_type main_type;
main_type n = to_unsigned(value);
main_type n = to_unsigned(to_int(value));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
}

void write_nan() { std::copy_n("nan", 3, out); }

void format_localized(const tm& time, const char* format) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
Expand Down Expand Up @@ -497,14 +512,14 @@ struct chrono_formatter {
void on_24_hour(numeric_system ns) {
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = hour();
time.tm_hour = to_int(hour());
format_localized(time, "%OH");
}

void on_12_hour(numeric_system ns) {
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = hour();
time.tm_hour = hour12();
format_localized(time, "%OI");
}

Expand All @@ -520,7 +535,7 @@ struct chrono_formatter {
write(second(), 2);
if (ms != std::chrono::milliseconds(0)) {
*out++ = '.';
write(to_int(ms.count()), 3);
write(ms.count(), 3);
}
return;
}
Expand Down
12 changes: 7 additions & 5 deletions test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,14 @@ TEST(ChronoTest, InvalidColons) {
fmt::format_error);
}

TEST(ChronoTest, LargeDuration) {
TEST(ChronoTest, SpecialDurations) {
EXPECT_EQ("40", fmt::format("{:%S}", std::chrono::duration<double>(1e20)));
}

TEST(ChronoTest, NegativeDuration) {
EXPECT_EQ("-00:01", fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
EXPECT_EQ("-00:01",
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
"nan nan nan nan.nan nan:nan nan",
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
}

#endif // FMT_STATIC_THOUSANDS_SEPARATOR

0 comments on commit ca978b3

Please sign in to comment.