diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-10 00:22:35 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-10 00:22:35 +0000 |
commit | cc6fe33b63fcd89a1ece028f2ab6cc5e1d8b8022 (patch) | |
tree | 34a71c51cfa353206ae6396d0595832c3889bc8b | |
parent | 21109574d203fea7e3f4490beadcb76eb62645b6 (diff) | |
download | chromium_src-cc6fe33b63fcd89a1ece028f2ab6cc5e1d8b8022.zip chromium_src-cc6fe33b63fcd89a1ece028f2ab6cc5e1d8b8022.tar.gz chromium_src-cc6fe33b63fcd89a1ece028f2ab6cc5e1d8b8022.tar.bz2 |
Fix POSIX and Mac Time::Explode().
Also add a test for the case that failed.
BUG=109437,109465
Review URL: http://codereview.chromium.org/9124016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116969 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/time_mac.cc | 28 | ||||
-rw-r--r-- | base/time_posix.cc | 28 | ||||
-rw-r--r-- | base/time_unittest.cc | 129 |
3 files changed, 171 insertions, 14 deletions
diff --git a/base/time_mac.cc b/base/time_mac.cc index 25cf037..da17e28 100644 --- a/base/time_mac.cc +++ b/base/time_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -79,23 +79,35 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { } void Time::Explode(bool is_local, Exploded* exploded) const { - CFAbsoluteTime seconds = - ((static_cast<double>(us_) - kWindowsEpochDeltaMicroseconds) / - kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970; + // Avoid rounding issues, by only putting the integral number of seconds + // (rounded towards -infinity) into a |CFAbsoluteTime| (which is a |double|). + int64 microsecond = us_ % kMicrosecondsPerSecond; + if (microsecond < 0) + microsecond += kMicrosecondsPerSecond; + CFAbsoluteTime seconds = ((us_ - microsecond) / kMicrosecondsPerSecond) - + kWindowsEpochDeltaSeconds - + kCFAbsoluteTimeIntervalSince1970; base::mac::ScopedCFTypeRef<CFTimeZoneRef> time_zone(is_local ? CFTimeZoneCopySystem() : NULL); CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone); + // 1 = Monday, ..., 7 = Sunday. + int cf_day_of_week = CFAbsoluteTimeGetDayOfWeek(seconds, time_zone); exploded->year = date.year; exploded->month = date.month; + exploded->day_of_week = (cf_day_of_week == 7) ? 0 : cf_day_of_week - 1; exploded->day_of_month = date.day; exploded->hour = date.hour; exploded->minute = date.minute; - exploded->second = date.second; - exploded->millisecond = - static_cast<int>(date.second * kMillisecondsPerSecond) % - kMillisecondsPerSecond; + // Make sure seconds are rounded down towards -infinity. + exploded->second = floor(date.second); + // Calculate milliseconds ourselves, since we rounded the |seconds|, making + // sure to round towards -infinity. + exploded->millisecond = + (microsecond >= 0) ? microsecond / kMicrosecondsPerMillisecond : + (microsecond - kMicrosecondsPerMillisecond + 1) / + kMicrosecondsPerMillisecond; } // TimeTicks ------------------------------------------------------------------ diff --git a/base/time_posix.cc b/base/time_posix.cc index 79a0b94..be99971 100644 --- a/base/time_posix.cc +++ b/base/time_posix.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -86,9 +86,27 @@ void Time::Explode(bool is_local, Exploded* exploded) const { // Time stores times with microsecond resolution, but Exploded only carries // millisecond resolution, so begin by being lossy. Adjust from Windows // epoch (1601) to Unix epoch (1970); - int64 milliseconds = (us_ - kWindowsEpochDeltaMicroseconds) / - kMicrosecondsPerMillisecond; - time_t seconds = milliseconds / kMillisecondsPerSecond; + int64 microseconds = us_ - kWindowsEpochDeltaMicroseconds; + // The following values are all rounded towards -infinity. + int64 milliseconds; // Milliseconds since epoch. + time_t seconds; // Seconds since epoch. + int millisecond; // Exploded millisecond value (0-999). + if (microseconds >= 0) { + // Rounding towards -infinity <=> rounding towards 0, in this case. + milliseconds = microseconds / kMicrosecondsPerMillisecond; + seconds = milliseconds / kMillisecondsPerSecond; + millisecond = milliseconds % kMillisecondsPerSecond; + } else { + // Round these *down* (towards -infinity). + milliseconds = (microseconds - kMicrosecondsPerMillisecond + 1) / + kMicrosecondsPerMillisecond; + seconds = (milliseconds - kMillisecondsPerSecond + 1) / + kMillisecondsPerSecond; + // Make this nonnegative (and between 0 and 999 inclusive). + millisecond = milliseconds % kMillisecondsPerSecond; + if (millisecond < 0) + millisecond += kMillisecondsPerSecond; + } struct tm timestruct; if (is_local) @@ -103,7 +121,7 @@ void Time::Explode(bool is_local, Exploded* exploded) const { exploded->hour = timestruct.tm_hour; exploded->minute = timestruct.tm_min; exploded->second = timestruct.tm_sec; - exploded->millisecond = milliseconds % kMillisecondsPerSecond; + exploded->millisecond = millisecond; } // static diff --git a/base/time_unittest.cc b/base/time_unittest.cc index 261b831..b81ef44 100644 --- a/base/time_unittest.cc +++ b/base/time_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -308,6 +308,133 @@ TEST_F(TimeTest, ParseTimeTestInvalidString) { EXPECT_FALSE(Time::FromString("Monday morning 2000", &parsed_time)); } +TEST_F(TimeTest, ExplodeBeforeUnixEpoch) { + static const int kUnixEpochYear = 1970; // In case this changes (ha!). + Time t; + Time::Exploded exploded; + + t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1969-12-31 23:59:59 999 milliseconds (and 999 microseconds). + EXPECT_EQ(kUnixEpochYear - 1, exploded.year); + EXPECT_EQ(12, exploded.month); + EXPECT_EQ(31, exploded.day_of_month); + EXPECT_EQ(23, exploded.hour); + EXPECT_EQ(59, exploded.minute); + EXPECT_EQ(59, exploded.second); + EXPECT_EQ(999, exploded.millisecond); + + t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1000); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1969-12-31 23:59:59 999 milliseconds. + EXPECT_EQ(kUnixEpochYear - 1, exploded.year); + EXPECT_EQ(12, exploded.month); + EXPECT_EQ(31, exploded.day_of_month); + EXPECT_EQ(23, exploded.hour); + EXPECT_EQ(59, exploded.minute); + EXPECT_EQ(59, exploded.second); + EXPECT_EQ(999, exploded.millisecond); + + t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1001); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1969-12-31 23:59:59 998 milliseconds (and 999 microseconds). + EXPECT_EQ(kUnixEpochYear - 1, exploded.year); + EXPECT_EQ(12, exploded.month); + EXPECT_EQ(31, exploded.day_of_month); + EXPECT_EQ(23, exploded.hour); + EXPECT_EQ(59, exploded.minute); + EXPECT_EQ(59, exploded.second); + EXPECT_EQ(998, exploded.millisecond); + + t = Time::UnixEpoch() - TimeDelta::FromMilliseconds(1000); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1969-12-31 23:59:59. + EXPECT_EQ(kUnixEpochYear - 1, exploded.year); + EXPECT_EQ(12, exploded.month); + EXPECT_EQ(31, exploded.day_of_month); + EXPECT_EQ(23, exploded.hour); + EXPECT_EQ(59, exploded.minute); + EXPECT_EQ(59, exploded.second); + EXPECT_EQ(0, exploded.millisecond); + + t = Time::UnixEpoch() - TimeDelta::FromMilliseconds(1001); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1969-12-31 23:59:58 999 milliseconds. + EXPECT_EQ(kUnixEpochYear - 1, exploded.year); + EXPECT_EQ(12, exploded.month); + EXPECT_EQ(31, exploded.day_of_month); + EXPECT_EQ(23, exploded.hour); + EXPECT_EQ(59, exploded.minute); + EXPECT_EQ(58, exploded.second); + EXPECT_EQ(999, exploded.millisecond); + + // Make sure we still handle at/after Unix epoch correctly. + t = Time::UnixEpoch(); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1970-12-31 00:00:00 0 milliseconds. + EXPECT_EQ(kUnixEpochYear, exploded.year); + EXPECT_EQ(1, exploded.month); + EXPECT_EQ(1, exploded.day_of_month); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(0, exploded.second); + EXPECT_EQ(0, exploded.millisecond); + + t = Time::UnixEpoch() + TimeDelta::FromMicroseconds(1); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1970-01-01 00:00:00 0 milliseconds (and 1 microsecond). + EXPECT_EQ(kUnixEpochYear, exploded.year); + EXPECT_EQ(1, exploded.month); + EXPECT_EQ(1, exploded.day_of_month); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(0, exploded.second); + EXPECT_EQ(0, exploded.millisecond); + + t = Time::UnixEpoch() + TimeDelta::FromMicroseconds(1000); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1970-01-01 00:00:00 1 millisecond. + EXPECT_EQ(kUnixEpochYear, exploded.year); + EXPECT_EQ(1, exploded.month); + EXPECT_EQ(1, exploded.day_of_month); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(0, exploded.second); + EXPECT_EQ(1, exploded.millisecond); + + t = Time::UnixEpoch() + TimeDelta::FromMilliseconds(1000); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1970-01-01 00:00:01. + EXPECT_EQ(kUnixEpochYear, exploded.year); + EXPECT_EQ(1, exploded.month); + EXPECT_EQ(1, exploded.day_of_month); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(1, exploded.second); + EXPECT_EQ(0, exploded.millisecond); + + t = Time::UnixEpoch() + TimeDelta::FromMilliseconds(1001); + t.UTCExplode(&exploded); + EXPECT_TRUE(exploded.HasValidValues()); + // Should be 1970-01-01 00:00:01 1 millisecond. + EXPECT_EQ(kUnixEpochYear, exploded.year); + EXPECT_EQ(1, exploded.month); + EXPECT_EQ(1, exploded.day_of_month); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(1, exploded.second); + EXPECT_EQ(1, exploded.millisecond); +} + TEST(TimeTicks, Deltas) { for (int index = 0; index < 50; index++) { TimeTicks ticks_start = TimeTicks::Now(); |