diff options
author | mkwst@chromium.org <mkwst@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-08 17:50:07 +0000 |
---|---|---|
committer | mkwst@chromium.org <mkwst@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-08 17:50:07 +0000 |
commit | a9e5f04481565af0a3c8dbe770b14899f24b68f7 (patch) | |
tree | 4565198c5cdff1435acd19f5efc903eb306ee061 | |
parent | 4a6d53c71fb914da3dd9e15cdc72b385b91701ec (diff) | |
download | chromium_src-a9e5f04481565af0a3c8dbe770b14899f24b68f7.zip chromium_src-a9e5f04481565af0a3c8dbe770b14899f24b68f7.tar.gz chromium_src-a9e5f04481565af0a3c8dbe770b14899f24b68f7.tar.bz2 |
Fixing Time::Max()'s behavior with Time::ToTimeT() and friends.
It looks like the math that's done to convert epoch times between platforms is
giving incorrect results when confronted with a "max" time. This CL adjusts the
various conversion methods to persist maxness in the same way they currently
persist zeroness.
BUG=146328
Review URL: https://chromiumcodereview.appspot.com/10916089
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155591 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/time.cc | 29 | ||||
-rw-r--r-- | base/time.h | 7 | ||||
-rw-r--r-- | base/time_mac.cc | 4 | ||||
-rw-r--r-- | base/time_posix.cc | 15 | ||||
-rw-r--r-- | base/time_unittest.cc | 62 | ||||
-rw-r--r-- | base/time_win.cc | 13 |
6 files changed, 124 insertions, 6 deletions
diff --git a/base/time.cc b/base/time.cc index 7055311..f2f3168 100644 --- a/base/time.cc +++ b/base/time.cc @@ -74,12 +74,23 @@ Time Time::Max() { Time Time::FromTimeT(time_t tt) { if (tt == 0) return Time(); // Preserve 0 so we can tell it doesn't exist. + if (tt == std::numeric_limits<time_t>::max()) + return Max(); return Time((tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset); } time_t Time::ToTimeT() const { - if (us_ == 0) + if (is_null()) return 0; // Preserve 0 so we can tell it doesn't exist. + if (is_max()) { + // Preserve max without offset to prevent overflow. + return std::numeric_limits<time_t>::max(); + } + if (std::numeric_limits<int64>::max() - kTimeTToMicrosecondsOffset <= us_) { + DLOG(WARNING) << "Overflow when converting base::Time with internal " << + "value " << us_ << " to time_t."; + return std::numeric_limits<time_t>::max(); + } return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; } @@ -87,14 +98,20 @@ time_t Time::ToTimeT() const { Time Time::FromDoubleT(double dt) { if (dt == 0 || isnan(dt)) return Time(); // Preserve 0 so we can tell it doesn't exist. + if (dt == std::numeric_limits<double>::max()) + return Max(); return Time(static_cast<int64>((dt * static_cast<double>(kMicrosecondsPerSecond)) + kTimeTToMicrosecondsOffset)); } double Time::ToDoubleT() const { - if (us_ == 0) + if (is_null()) return 0; // Preserve 0 so we can tell it doesn't exist. + if (is_max()) { + // Preserve max without offset to prevent overflow. + return std::numeric_limits<double>::max(); + } return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / static_cast<double>(kMicrosecondsPerSecond)); } @@ -103,15 +120,21 @@ double Time::ToDoubleT() const { Time Time::FromJsTime(double ms_since_epoch) { // The epoch is a valid time, so this constructor doesn't interpret // 0 as the null time. + if (ms_since_epoch == std::numeric_limits<double>::max()) + return Max(); return Time(static_cast<int64>(ms_since_epoch * kMicrosecondsPerMillisecond) + kTimeTToMicrosecondsOffset); } double Time::ToJsTime() const { - if (us_ == 0) { + if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform. return 0; } + if (is_max()) { + // Preserve max without offset to prevent overflow. + return std::numeric_limits<double>::max(); + } return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerMillisecond); } diff --git a/base/time.h b/base/time.h index 4d9bf35..ceed500 100644 --- a/base/time.h +++ b/base/time.h @@ -45,6 +45,8 @@ #include <windows.h> #endif +#include <limits> + namespace base { class Time; @@ -246,6 +248,11 @@ class BASE_EXPORT Time { return us_ == 0; } + // Returns true if the time object is the maximum time. + bool is_max() const { + return us_ == std::numeric_limits<int64>::max(); + } + // Returns the time for epoch in Unix-like system (Jan 1, 1970). static Time UnixEpoch(); diff --git a/base/time_mac.cc b/base/time_mac.cc index 98641bb..ca10bc8 100644 --- a/base/time_mac.cc +++ b/base/time_mac.cc @@ -54,6 +54,8 @@ Time Time::Now() { Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { if (t == 0) return Time(); // Consider 0 as a null Time. + if (t == std::numeric_limits<CFAbsoluteTime>::max()) + return Max(); return Time(static_cast<int64>( (t + kCFAbsoluteTimeIntervalSince1970) * kMicrosecondsPerSecond) + kWindowsEpochDeltaMicroseconds); @@ -62,6 +64,8 @@ Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { CFAbsoluteTime Time::ToCFAbsoluteTime() const { if (is_null()) return 0; // Consider 0 as a null Time. + if (is_max()) + return std::numeric_limits<CFAbsoluteTime>::max(); return (static_cast<CFAbsoluteTime>(us_ - kWindowsEpochDeltaMicroseconds) / kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970; } diff --git a/base/time_posix.cc b/base/time_posix.cc index c74cae5..c79781c 100644 --- a/base/time_posix.cc +++ b/base/time_posix.cc @@ -266,6 +266,11 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() { Time Time::FromTimeVal(struct timeval t) { DCHECK_LT(t.tv_usec, static_cast<int>(Time::kMicrosecondsPerSecond)); DCHECK_GE(t.tv_usec, 0); + if (t.tv_usec == 0 && t.tv_sec == 0) + return Time(); + if (t.tv_usec == static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1 && + t.tv_sec == std::numeric_limits<time_t>::max()) + return Max(); return Time( (static_cast<int64>(t.tv_sec) * Time::kMicrosecondsPerSecond) + t.tv_usec + @@ -274,6 +279,16 @@ Time Time::FromTimeVal(struct timeval t) { struct timeval Time::ToTimeVal() const { struct timeval result; + if (is_null()) { + result.tv_sec = 0; + result.tv_usec = 0; + return result; + } + if (is_max()) { + result.tv_sec = std::numeric_limits<time_t>::max(); + result.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1; + return result; + } int64 us = us_ - kTimeTToMicrosecondsOffset; result.tv_sec = us / Time::kMicrosecondsPerSecond; result.tv_usec = us % Time::kMicrosecondsPerSecond; diff --git a/base/time_unittest.cc b/base/time_unittest.cc index 5421ed1..dd6d26b 100644 --- a/base/time_unittest.cc +++ b/base/time_unittest.cc @@ -480,11 +480,67 @@ TEST_F(TimeTest, ExplodeBeforeUnixEpoch) { } TEST_F(TimeTest, Max) { - EXPECT_EQ(base::Time::Max(), base::Time::Max()); - EXPECT_GT(base::Time::Max(), base::Time::Now()); - EXPECT_GT(base::Time::Max(), base::Time()); + Time max = Time::Max(); + EXPECT_TRUE(max.is_max()); + EXPECT_EQ(max, Time::Max()); + EXPECT_GT(max, Time::Now()); + EXPECT_GT(max, Time()); } +TEST_F(TimeTest, MaxConversions) { + Time t = Time::Max(); + EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue()); + + t = Time::FromDoubleT(std::numeric_limits<double>::max()); + EXPECT_TRUE(t.is_max()); + EXPECT_EQ(std::numeric_limits<double>::max(), t.ToDoubleT()); + + t = Time::FromJsTime(std::numeric_limits<double>::max()); + EXPECT_TRUE(t.is_max()); + EXPECT_EQ(std::numeric_limits<double>::max(), t.ToJsTime()); + + t = Time::FromTimeT(std::numeric_limits<time_t>::max()); + EXPECT_TRUE(t.is_max()); + EXPECT_EQ(std::numeric_limits<time_t>::max(), t.ToTimeT()); + +#if defined(OS_POSIX) + struct timeval tval; + tval.tv_sec = std::numeric_limits<time_t>::max(); + tval.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1; + t = Time::FromTimeVal(tval); + EXPECT_TRUE(t.is_max()); + tval = t.ToTimeVal(); + EXPECT_EQ(std::numeric_limits<time_t>::max(), tval.tv_sec); + EXPECT_EQ(static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1, + tval.tv_usec); +#endif + +#if defined(OS_MACOSX) + t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::max()); + EXPECT_TRUE(t.is_max()); + EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::max(), t.ToCFAbsoluteTime()); +#endif + +#if defined(OS_WIN) + FILETIME ftime; + ftime.dwHighDateTime = std::numeric_limits<DWORD>::max(); + ftime.dwLowDateTime = std::numeric_limits<DWORD>::max(); + t = Time::FromFileTime(ftime); + EXPECT_TRUE(t.is_max()); + ftime = t.ToFileTime(); + EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwHighDateTime); + EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwLowDateTime); +#endif +} + +#if defined(OS_MACOSX) +TEST_F(TimeTest, TimeTOverflow) { + Time t = Time::FromInternalValue(std::numeric_limits<int64>::max() - 1); + EXPECT_FALSE(t.is_max()); + EXPECT_EQ(std::numeric_limits<time_t>::max(), t.ToTimeT()); +} +#endif + TEST(TimeTicks, Deltas) { for (int index = 0; index < 50; index++) { TimeTicks ticks_start = TimeTicks::Now(); diff --git a/base/time_win.cc b/base/time_win.cc index 191b7a7..6d8e432 100644 --- a/base/time_win.cc +++ b/base/time_win.cc @@ -141,10 +141,23 @@ Time Time::NowFromSystemTime() { // static Time Time::FromFileTime(FILETIME ft) { + if (bit_cast<int64, FILETIME>(ft) == 0) + return Time(); + if (ft.dwHighDateTime == std::numeric_limits<DWORD>::max() && + ft.dwLowDateTime == std::numeric_limits<DWORD>::max()) + return Max(); return Time(FileTimeToMicroseconds(ft)); } FILETIME Time::ToFileTime() const { + if (is_null()) + return bit_cast<FILETIME, int64>(0); + if (is_max()) { + FILETIME result; + result.dwHighDateTime = std::numeric_limits<DWORD>::max(); + result.dwLowDateTime = std::numeric_limits<DWORD>::max(); + return result; + } FILETIME utc_ft; MicrosecondsToFileTime(us_, &utc_ft); return utc_ft; |