summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 00:22:35 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 00:22:35 +0000
commitcc6fe33b63fcd89a1ece028f2ab6cc5e1d8b8022 (patch)
tree34a71c51cfa353206ae6396d0595832c3889bc8b
parent21109574d203fea7e3f4490beadcb76eb62645b6 (diff)
downloadchromium_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.cc28
-rw-r--r--base/time_posix.cc28
-rw-r--r--base/time_unittest.cc129
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();