about summary refs log tree commit diff
path: root/absl/time
diff options
context:
space:
mode:
Diffstat (limited to 'absl/time')
-rw-r--r--absl/time/duration.cc8
-rw-r--r--absl/time/duration_test.cc34
-rw-r--r--absl/time/time.h5
3 files changed, 45 insertions, 2 deletions
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 2950c7cdc632..b77d5ec9006d 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -78,10 +78,16 @@ constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
 
 // Can't use std::isinfinite() because it doesn't exist on windows.
 inline bool IsFinite(double d) {
+  if (std::isnan(d)) return false;
   return d != std::numeric_limits<double>::infinity() &&
          d != -std::numeric_limits<double>::infinity();
 }
 
+inline bool IsValidDivisor(double d) {
+  if (std::isnan(d)) return false;
+  return d != 0.0;
+}
+
 // Can't use std::round() because it is only available in C++11.
 // Note that we ignore the possibility of floating-point over/underflow.
 template <typename Double>
@@ -455,7 +461,7 @@ Duration& Duration::operator/=(int64_t r) {
 }
 
 Duration& Duration::operator/=(double r) {
-  if (time_internal::IsInfiniteDuration(*this) || r == 0.0) {
+  if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) {
     const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
     return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
   }
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 775da91e6929..61f3c5c07fa1 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -803,6 +803,40 @@ TEST(Duration, DivisionByZero) {
   EXPECT_EQ(-dbl_inf, absl::FDivDuration(-any_dur, zero));
 }
 
+TEST(Duration, NaN) {
+  // Note that IEEE 754 does not define the behavior of a nan's sign when it is
+  // copied, so the code below allows for either + or - InfiniteDuration.
+#define TEST_NAN_HANDLING(NAME, NAN)           \
+  do {                                         \
+    const auto inf = absl::InfiniteDuration(); \
+    auto x = NAME(NAN);                        \
+    EXPECT_TRUE(x == inf || x == -inf);        \
+    auto y = NAME(42);                         \
+    y *= NAN;                                  \
+    EXPECT_TRUE(y == inf || y == -inf);        \
+    auto z = NAME(42);                         \
+    z /= NAN;                                  \
+    EXPECT_TRUE(z == inf || z == -inf);        \
+  } while (0)
+
+  const double nan = std::numeric_limits<double>::quiet_NaN();
+  TEST_NAN_HANDLING(absl::Nanoseconds, nan);
+  TEST_NAN_HANDLING(absl::Microseconds, nan);
+  TEST_NAN_HANDLING(absl::Milliseconds, nan);
+  TEST_NAN_HANDLING(absl::Seconds, nan);
+  TEST_NAN_HANDLING(absl::Minutes, nan);
+  TEST_NAN_HANDLING(absl::Hours, nan);
+
+  TEST_NAN_HANDLING(absl::Nanoseconds, -nan);
+  TEST_NAN_HANDLING(absl::Microseconds, -nan);
+  TEST_NAN_HANDLING(absl::Milliseconds, -nan);
+  TEST_NAN_HANDLING(absl::Seconds, -nan);
+  TEST_NAN_HANDLING(absl::Minutes, -nan);
+  TEST_NAN_HANDLING(absl::Hours, -nan);
+
+#undef TEST_NAN_HANDLING
+}
+
 TEST(Duration, Range) {
   const absl::Duration range = ApproxYears(100 * 1e9);
   const absl::Duration range_future = range;
diff --git a/absl/time/time.h b/absl/time/time.h
index 3fa9378fc849..b86abf27b045 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -69,6 +69,7 @@
 #include <winsock2.h>
 #endif
 #include <chrono>  // NOLINT(build/c++11)
+#include <cmath>
 #include <cstdint>
 #include <ctime>
 #include <ostream>
@@ -411,10 +412,12 @@ Duration Milliseconds(T n) {
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Seconds(T n) {
-  if (n >= 0) {
+  if (n >= 0) {  // Note: `NaN >= 0` is false.
     if (n >= (std::numeric_limits<int64_t>::max)()) return InfiniteDuration();
     return time_internal::MakePosDoubleDuration(n);
   } else {
+    if (std::isnan(n))
+      return std::signbit(n) ? -InfiniteDuration() : InfiniteDuration();
     if (n <= (std::numeric_limits<int64_t>::min)()) return -InfiniteDuration();
     return -time_internal::MakePosDoubleDuration(-n);
   }