summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/time.h9
-rw-r--r--base/time_mac.cc34
-rw-r--r--base/time_posix.cc76
-rw-r--r--base/time_unittest.cc30
-rw-r--r--chrome/browser/bookmarks/bookmark_codec.cc18
-rw-r--r--chrome/browser/history/history_backend.cc13
-rw-r--r--chrome/browser/history/history_database.cc51
-rw-r--r--chrome/browser/history/history_database.h21
-rw-r--r--chrome/browser/password_manager/password_store_mac_unittest.cc4
-rw-r--r--chrome/test/data/profiles/typical_history/Default/Historybin13770752 -> 13783040 bytes
-rw-r--r--net/base/cookie_monster_unittest.cc11
11 files changed, 205 insertions, 62 deletions
diff --git a/base/time.h b/base/time.h
index beeca27..a57c04f 100644
--- a/base/time.h
+++ b/base/time.h
@@ -178,6 +178,15 @@ class Time {
static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond *
kMicrosecondsPerSecond;
+#if !defined(OS_WIN)
+ // On Mac & Linux, this value is the delta from the Windows epoch of 1601 to
+ // the Posix delta of 1970. This is used for migrating between the old
+ // 1970-based epochs to the new 1601-based ones. It should be removed from
+ // this global header and put in the platform-specific ones when we remove the
+ // migration code.
+ static const int64 kWindowsEpochDeltaMicroseconds;
+#endif
+
// Represents an exploded time that can be formatted nicely. This is kind of
// like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few
// additions and changes to prevent errors.
diff --git a/base/time_mac.cc b/base/time_mac.cc
index 3e5e14a..6b46b95 100644
--- a/base/time_mac.cc
+++ b/base/time_mac.cc
@@ -24,20 +24,33 @@ namespace base {
// Time -----------------------------------------------------------------------
-// The internal representation of Time uses a 64-bit microsecond count
-// from 1970-01-01 00:00:00 UTC. Core Foundation uses a double second count
-// since 2001-01-01 00:00:00 UTC.
+// Core Foundation uses a double second count since 2001-01-01 00:00:00 UTC.
+// The UNIX epoch is 1970-01-01 00:00:00 UTC.
+// Windows uses a Gregorian epoch of 1601. We need to match this internally
+// so that our time representations match across all platforms. See bug 14734.
+// irb(main):010:0> Time.at(0).getutc()
+// => Thu Jan 01 00:00:00 UTC 1970
+// irb(main):011:0> Time.at(-11644473600).getutc()
+// => Mon Jan 01 00:00:00 UTC 1601
+static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
+static const int64 kWindowsEpochDeltaMilliseconds =
+ kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
-// Some functions in time.cc use time_t directly, so we provide a zero offset
-// for them. The epoch is 1970-01-01 00:00:00 UTC.
// static
-const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0);
+const int64 Time::kWindowsEpochDeltaMicroseconds =
+ kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond;
+
+// Some functions in time.cc use time_t directly, so we provide an offset
+// to convert from time_t (Unix epoch) and internal (Windows epoch).
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds;
// static
Time Time::Now() {
CFAbsoluteTime now =
CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
- return Time(static_cast<int64>(now * kMicrosecondsPerSecond));
+ return Time(static_cast<int64>(now * kMicrosecondsPerSecond) +
+ kWindowsEpochDeltaMicroseconds);
}
// static
@@ -61,13 +74,14 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) {
time_zone(is_local ? CFTimeZoneCopySystem() : NULL);
CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) +
kCFAbsoluteTimeIntervalSince1970;
- return Time(static_cast<int64>(seconds * kMicrosecondsPerSecond));
+ return Time(static_cast<int64>(seconds * kMicrosecondsPerSecond) +
+ kWindowsEpochDeltaMicroseconds);
}
void Time::Explode(bool is_local, Exploded* exploded) const {
CFAbsoluteTime seconds =
- (static_cast<double>(us_) / kMicrosecondsPerSecond) -
- kCFAbsoluteTimeIntervalSince1970;
+ (static_cast<double>((us_ - kWindowsEpochDeltaMicroseconds) /
+ kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970);
scoped_cftyperef<CFTimeZoneRef>
time_zone(is_local ? CFTimeZoneCopySystem() : NULL);
diff --git a/base/time_posix.cc b/base/time_posix.cc
index 8b04be9..66f41d3 100644
--- a/base/time_posix.cc
+++ b/base/time_posix.cc
@@ -4,9 +4,6 @@
#include "base/time.h"
-#ifdef OS_MACOSX
-#include <mach/mach_time.h>
-#endif
#include <sys/time.h>
#include <time.h>
@@ -23,10 +20,24 @@ namespace base {
// Time -----------------------------------------------------------------------
-// Some functions in time.cc use time_t directly, so we provide a zero offset
-// for them. The epoch is 1970-01-01 00:00:00 UTC.
+// Windows uses a Gregorian epoch of 1601. We need to match this internally
+// so that our time representations match across all platforms. See bug 14734.
+// irb(main):010:0> Time.at(0).getutc()
+// => Thu Jan 01 00:00:00 UTC 1970
+// irb(main):011:0> Time.at(-11644473600).getutc()
+// => Mon Jan 01 00:00:00 UTC 1601
+static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
+static const int64 kWindowsEpochDeltaMilliseconds =
+ kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
+
+// static
+const int64 Time::kWindowsEpochDeltaMicroseconds =
+ kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond;
+
+// Some functions in time.cc use time_t directly, so we provide an offset
+// to convert from time_t (Unix epoch) and internal (Windows epoch).
// static
-const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0);
+const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds;
// static
Time Time::Now() {
@@ -36,8 +47,10 @@ Time Time::Now() {
DCHECK(0) << "Could not determine time of day";
}
// Combine seconds and microseconds in a 64-bit field containing microseconds
- // since the epoch. That's enough for nearly 600 centuries.
- return tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec;
+ // since the epoch. That's enough for nearly 600 centuries. Adjust from
+ // Unix (1970) to Windows (1601) epoch.
+ return Time((tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec) +
+ kWindowsEpochDeltaMicroseconds);
}
// static
@@ -100,13 +113,17 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) {
milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond;
}
- return Time(milliseconds * kMicrosecondsPerMillisecond);
+ // Adjust from Unix (1970) to Windows (1601) epoch.
+ return Time((milliseconds * kMicrosecondsPerMillisecond) +
+ kWindowsEpochDeltaMicroseconds);
}
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.
- int64 milliseconds = us_ / kMicrosecondsPerMillisecond;
+ // 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;
struct tm timestruct;
@@ -127,38 +144,13 @@ void Time::Explode(bool is_local, Exploded* exploded) const {
// TimeTicks ------------------------------------------------------------------
+#if defined(OS_POSIX) && \
+ defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
+
// static
TimeTicks TimeTicks::Now() {
uint64_t absolute_micro;
-#if defined(OS_MACOSX)
- static mach_timebase_info_data_t timebase_info;
- if (timebase_info.denom == 0) {
- // Zero-initialization of statics guarantees that denom will be 0 before
- // calling mach_timebase_info. mach_timebase_info will never set denom to
- // 0 as that would be invalid, so the zero-check can be used to determine
- // whether mach_timebase_info has already been called. This is
- // recommended by Apple's QA1398.
- kern_return_t kr = mach_timebase_info(&timebase_info);
- DCHECK(kr == KERN_SUCCESS);
- }
-
- // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
- // with less precision (such as TickCount) just call through to
- // mach_absolute_time.
-
- // timebase_info converts absolute time tick units into nanoseconds. Convert
- // to microseconds up front to stave off overflows.
- absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond *
- timebase_info.numer / timebase_info.denom;
-
- // Don't bother with the rollover handling that the Windows version does.
- // With numer and denom = 1 (the expected case), the 64-bit absolute time
- // reported in nanoseconds is enough to last nearly 585 years.
-
-#elif defined(OS_POSIX) && \
- defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
-
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed.";
@@ -169,13 +161,13 @@ TimeTicks TimeTicks::Now() {
(static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) +
(static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond);
+ return TimeTicks(absolute_micro);
+}
+
#else // _POSIX_MONOTONIC_CLOCK
#error No usable tick clock function on this platform.
#endif // _POSIX_MONOTONIC_CLOCK
- return TimeTicks(absolute_micro);
-}
-
// static
TimeTicks TimeTicks::HighResNow() {
return Now();
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
index ebe69eb..f8a62cb 100644
--- a/base/time_unittest.cc
+++ b/base/time_unittest.cc
@@ -66,9 +66,10 @@ TEST(Time, LocalExplode) {
Time b = Time::FromLocalExploded(exploded);
- // The exploded structure doesn't have microseconds, so the result will be
- // rounded to the nearest millisecond.
- EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+ // The exploded structure doesn't have microseconds, and on Mac & Linux, the
+ // internal OS conversion uses seconds, which will cause truncation. So we
+ // can only make sure that the delta is within one second.
+ EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1));
}
TEST(Time, UTCExplode) {
@@ -77,7 +78,7 @@ TEST(Time, UTCExplode) {
a.UTCExplode(&exploded);
Time b = Time::FromUTCExploded(exploded);
- EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+ EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1));
}
TEST(Time, LocalMidnight) {
@@ -140,3 +141,24 @@ TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds());
}
+
+// Our internal time format is serialized in things like databases, so it's
+// important that it's consistent across all our platforms. We use the 1601
+// Windows epoch as the internal format across all platforms.
+TEST(TimeDelta, WindowsEpoch) {
+ Time::Exploded exploded;
+ exploded.year = 1970;
+ exploded.month = 1;
+ exploded.day_of_week = 0; // Should be unusued.
+ exploded.day_of_month = 1;
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ Time t = Time::FromUTCExploded(exploded);
+ // Unix 1970 epoch.
+ EXPECT_EQ(GG_INT64_C(11644473600000000), t.ToInternalValue());
+
+ // We can't test 1601 epoch, since the system time functions on Linux
+ // only compute years starting from 1900.
+}
diff --git a/chrome/browser/bookmarks/bookmark_codec.cc b/chrome/browser/bookmarks/bookmark_codec.cc
index cae5f22..ebf48db 100644
--- a/chrome/browser/bookmarks/bookmark_codec.cc
+++ b/chrome/browser/bookmarks/bookmark_codec.cc
@@ -200,6 +200,21 @@ bool BookmarkCodec::DecodeNode(const DictionaryValue& value,
std::wstring date_added_string;
if (!value.GetString(kDateAddedKey, &date_added_string))
date_added_string = Int64ToWString(Time::Now().ToInternalValue());
+ base::Time date_added = base::Time::FromInternalValue(
+ StringToInt64(WideToUTF16Hack(date_added_string)));
+#if !defined(OS_WIN)
+ // We changed the epoch for dates on Mac & Linux from 1970 to the Windows
+ // one of 1601. We assume any number we encounter from before 1970 is using
+ // the old format, so we need to add the delta to it.
+ //
+ // This code should be removed at some point:
+ // http://code.google.com/p/chromium/issues/detail?id=20264
+ if (date_added.ToInternalValue() <
+ base::Time::kWindowsEpochDeltaMicroseconds) {
+ date_added = base::Time::FromInternalValue(date_added.ToInternalValue() +
+ base::Time::kWindowsEpochDeltaMicroseconds);
+ }
+#endif
std::wstring type_string;
if (!value.GetString(kTypeKey, &type_string))
@@ -255,8 +270,7 @@ bool BookmarkCodec::DecodeNode(const DictionaryValue& value,
}
node->SetTitle(title);
- node->set_date_added(Time::FromInternalValue(
- StringToInt64(WideToUTF16Hack(date_added_string))));
+ node->set_date_added(date_added);
return true;
}
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index 1ccd7cf..0d01496 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -529,6 +529,13 @@ void HistoryBackend::InitImpl() {
LOG(WARNING) << "Text database initialization failed, running without it.";
text_database_.reset();
}
+ if (db_->needs_version_17_migration()) {
+ // See needs_version_17_migration() decl for more. In this case, we want
+ // to erase all the text database files. This must be done after the text
+ // database manager has been initialized, since it knows about all the
+ // files it manages.
+ text_database_->DeleteAll();
+ }
// Thumbnail database.
thumbnail_db_.reset(new ThumbnailDatabase());
@@ -544,6 +551,12 @@ void HistoryBackend::InitImpl() {
}
// Archived database.
+ if (db_->needs_version_17_migration()) {
+ // See needs_version_17_migration() decl for more. In this case, we want
+ // to delete the archived database and need to do so before we try to
+ // open the file. We can ignore any error (maybe the file doesn't exist).
+ file_util::Delete(archived_name, false);
+ }
archived_db_.reset(new ArchivedDatabase());
if (!archived_db_->Init(archived_name)) {
LOG(WARNING) << "Could not initialize the archived database.";
diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc
index 5a929b1..198c6a4 100644
--- a/chrome/browser/history/history_database.cc
+++ b/chrome/browser/history/history_database.cc
@@ -16,8 +16,10 @@ namespace history {
namespace {
-// Current version number.
-static const int kCurrentVersionNumber = 16;
+// Current version number. We write databases at the "current" version number,
+// but any previous version that can read the "compatible" one can make do with
+// or database without *too* many bad effects.
+static const int kCurrentVersionNumber = 17;
static const int kCompatibleVersionNumber = 16;
static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
@@ -25,7 +27,8 @@ static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
HistoryDatabase::HistoryDatabase()
: transaction_nesting_(0),
- db_(NULL) {
+ db_(NULL),
+ needs_version_17_migration_(false) {
}
HistoryDatabase::~HistoryDatabase() {
@@ -235,6 +238,20 @@ InitStatus HistoryDatabase::EnsureCurrentVersion(
std::min(cur_version, kCompatibleVersionNumber));
}
+ if (cur_version == 16) {
+#if !defined(OS_WIN)
+ // In this version we bring the time format on Mac & Linux in sync with the
+ // Windows version so that profiles can be moved between computers.
+ MigrateTimeEpoch();
+#endif
+ // On all platforms we bump the version number, so on Windows this
+ // migration is a NOP. We keep the compatible version at 16 since things
+ // will basically still work, just history will be in the future if an
+ // old version reads it.
+ ++cur_version;
+ meta_table_.SetVersionNumber(cur_version);
+ }
+
// When the version is too old, we just try to continue anyway, there should
// not be a released product that makes a database too old for us to handle.
LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
@@ -243,4 +260,32 @@ InitStatus HistoryDatabase::EnsureCurrentVersion(
return INIT_OK;
}
+#if !defined(OS_WIN)
+void HistoryDatabase::MigrateTimeEpoch() {
+ // Update all the times in the URLs and visits table in the main database.
+ // For visits, clear the indexed flag since we'll delete the FTS databases in
+ // the next step.
+ sqlite3_exec(GetDB(),
+ "UPDATE urls "
+ "SET last_visit_time = last_visit_time + 11644473600000000 "
+ "WHERE id IN (SELECT id FROM urls WHERE last_visit_time > 0);",
+ NULL, NULL, NULL);
+ sqlite3_exec(GetDB(),
+ "UPDATE visits "
+ "SET visit_time = visit_time + 11644473600000000, is_indexed = 0 "
+ "WHERE id IN (SELECT id FROM visits WHERE visit_time > 0);",
+ NULL, NULL, NULL);
+ sqlite3_exec(GetDB(),
+ "UPDATE segment_usage "
+ "SET time_slot = time_slot + 11644473600000000 "
+ "WHERE id IN (SELECT id FROM segment_usage WHERE time_slot > 0);",
+ NULL, NULL, NULL);
+
+ // Erase all the full text index files. These will take a while to update and
+ // are less important, so we just blow them away. Same with the archived
+ // database.
+ needs_version_17_migration_ = true;
+}
+#endif
+
} // namespace history
diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h
index 35d8595..9351c26 100644
--- a/chrome/browser/history/history_database.h
+++ b/chrome/browser/history/history_database.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_HISTORY_HISTORY_DATABASE_H_
#define CHROME_BROWSER_HISTORY_HISTORY_DATABASE_H_
+#include "build/build_config.h"
#include "chrome/browser/history/download_database.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/history/starred_url_database.h"
@@ -110,6 +111,17 @@ class HistoryDatabase : public DownloadDatabase,
// unused space in the file. It can be VERY SLOW.
void Vacuum();
+ // Returns true if the history backend should erase the full text search
+ // and archived history files as part of version 16 -> 17 migration. The
+ // time format changed in this revision, and these files would be much slower
+ // to migrate. Since the data is less important, they should be deleted.
+ //
+ // This flag will be valid after Init() is called. It will always be false
+ // when running on Windows.
+ bool needs_version_17_migration() const {
+ return needs_version_17_migration_;
+ }
+
// Visit table functions ----------------------------------------------------
// Update the segment id of a visit. Return true on success.
@@ -144,6 +156,12 @@ class HistoryDatabase : public DownloadDatabase,
// may commit the transaction and start a new one if migration requires it.
InitStatus EnsureCurrentVersion(const FilePath& tmp_bookmarks_path);
+#if !defined(OS_WIN)
+ // Converts the time epoch in the database from being 1970-based to being
+ // 1601-based which corresponds to the change in Time.internal_value_.
+ void MigrateTimeEpoch();
+#endif
+
// ---------------------------------------------------------------------------
// How many nested transactions are pending? When this gets to 0, we commit.
@@ -162,6 +180,9 @@ class HistoryDatabase : public DownloadDatabase,
MetaTableHelper meta_table_;
base::Time cached_early_expiration_threshold_;
+ // See the getter above.
+ bool needs_version_17_migration_;
+
DISALLOW_COPY_AND_ASSIGN(HistoryDatabase);
};
diff --git a/chrome/browser/password_manager/password_store_mac_unittest.cc b/chrome/browser/password_manager/password_store_mac_unittest.cc
index c96a1a4..6eeeac7 100644
--- a/chrome/browser/password_manager/password_store_mac_unittest.cc
+++ b/chrome/browser/password_manager/password_store_mac_unittest.cc
@@ -232,9 +232,11 @@ TEST_F(PasswordStoreMacTest, TestKeychainToFormTranslation) {
{ PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
"https://some.domain.com/", L"digest_auth_user", L"digest", true,
1998, 3, 30, 10, 0, 0 },
+ // This one gives us an invalid date, which we will treat as a "NULL" date
+ // which is 1601.
{ PasswordForm::SCHEME_OTHER, "http://a.server.com/",
"http://a.server.com/", L"abc", L"123", false,
- 1970, 1, 1, 0, 0, 0 },
+ 1601, 1, 1, 0, 0, 0 },
};
for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
diff --git a/chrome/test/data/profiles/typical_history/Default/History b/chrome/test/data/profiles/typical_history/Default/History
index b3d68c7..3251af2 100644
--- a/chrome/test/data/profiles/typical_history/Default/History
+++ b/chrome/test/data/profiles/typical_history/Default/History
Binary files differ
diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc
index bb36585..1f0b45d 100644
--- a/net/base/cookie_monster_unittest.cc
+++ b/net/base/cookie_monster_unittest.cc
@@ -688,6 +688,17 @@ TEST(CookieMonsterTest, TestCookieDeletion) {
std::string(kValidCookieLine) +
"; expires=Mon, 18-Apr-1977 22:50:13 GMT"));
EXPECT_EQ("", cm.GetCookies(url_google));
+
+ // Create a persistent cookie.
+ EXPECT_TRUE(cm.SetCookie(url_google,
+ std::string(kValidCookieLine) +
+ "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+ EXPECT_EQ("A=B", cm.GetCookies(url_google));
+ // Delete it via Expires, with a unix epoch of 0.
+ EXPECT_TRUE(cm.SetCookie(url_google,
+ std::string(kValidCookieLine) +
+ "; expires=Thu, 1-Jan-1970 00:00:00 GMT"));
+ EXPECT_EQ("", cm.GetCookies(url_google));
}
TEST(CookieMonsterTest, TestCookieDeleteAll) {