diff options
author | digit@chromium.org <digit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 15:38:50 +0000 |
---|---|---|
committer | digit@chromium.org <digit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 15:38:50 +0000 |
commit | 2a278516943eee02e0206506a4b907fc0b55f27b (patch) | |
tree | bf80d179b0b66b02de34b884bf55634cb427b6a1 /base/time | |
parent | f5385e64e92cbd7f4afa670a4bb3abff3647ddde (diff) | |
download | chromium_src-2a278516943eee02e0206506a4b907fc0b55f27b.zip chromium_src-2a278516943eee02e0206506a4b907fc0b55f27b.tar.gz chromium_src-2a278516943eee02e0206506a4b907fc0b55f27b.tar.bz2 |
android: fix base::Time::FromLocalExploded() crash.
This patch does the following:
- Provide a work-around for an Android platform bug that
happens on older Android releases (e.g. 4.1.2), but fixed
on later ones (e.g. 4.3), where mktime() / mktime64()
would return -1 even when passed proper time values.
- Improve the code to properly deal with the fact that
SysTime is actually int64 on Android, unlike other
platforms, allowing us to remove the CHECK() that
was triggered by the platform bug.
- Add a new unit test to verify that the new code
doesn't crash on Android 4.1.2 anymore, and returns
the correct values.
BUG=287821
R=jar@chromium.org,mark@chromium.org,brettw@chromium.org
Review URL: https://codereview.chromium.org/27472003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229567 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/time')
-rw-r--r-- | base/time/time_posix.cc | 51 | ||||
-rw-r--r-- | base/time/time_unittest.cc | 24 |
2 files changed, 68 insertions, 7 deletions
diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc index 5e06ce9..c1edb7a 100644 --- a/base/time/time_posix.cc +++ b/base/time/time_posix.cc @@ -214,9 +214,41 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore #endif - SysTime seconds = SysTimeFromTimeStruct(×truct, is_local); int64 milliseconds; + SysTime seconds; + + // Certain exploded dates do not really exist due to daylight saving times, + // and this causes mktime() to return implementation-defined values when + // tm_isdst is set to -1. On Android, the function will return -1, while the + // C libraries of other platforms typically return a liberally-chosen value. + // Handling this requires the special code below. + + // SysTimeFromTimeStruct() modifies the input structure, save current value. + struct tm timestruct0 = timestruct; + + seconds = SysTimeFromTimeStruct(×truct, is_local); + if (seconds == -1) { + // Get the time values with tm_isdst == 0 and 1, then select the closest one + // to UTC 00:00:00 that isn't -1. + timestruct = timestruct0; + timestruct.tm_isdst = 0; + int64 seconds_isdst0 = SysTimeFromTimeStruct(×truct, is_local); + + timestruct = timestruct0; + timestruct.tm_isdst = 1; + int64 seconds_isdst1 = SysTimeFromTimeStruct(×truct, is_local); + + // seconds_isdst0 or seconds_isdst1 can be -1 for some timezones. + // E.g. "CLST" (Chile Summer Time) returns -1 for 'tm_isdt == 1'. + if (seconds_isdst0 < 0) + seconds = seconds_isdst1; + else if (seconds_isdst1 < 0) + seconds = seconds_isdst0; + else + seconds = std::min(seconds_isdst0, seconds_isdst1); + } + // Handle overflow. Clamping the range to what mktime and timegm might // return is the best that can be done here. It's not ideal, but it's better // than failing here or ignoring the overflow case and treating each time @@ -237,14 +269,19 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { // When representing the most distant time in the future, add in an extra // 999ms to avoid the time being less than any other possible value that // this function can return. + + // On Android, SysTime is int64, special care must be taken to avoid + // overflows. + const int64 min_seconds = (sizeof(SysTime) < sizeof(int64)) + ? std::numeric_limits<SysTime>::min() + : std::numeric_limits<int32_t>::min(); + const int64 max_seconds = (sizeof(SysTime) < sizeof(int64)) + ? std::numeric_limits<SysTime>::max() + : std::numeric_limits<int32_t>::max(); if (exploded.year < 1969) { - CHECK(sizeof(SysTime) < sizeof(int64)) << "integer overflow"; - milliseconds = std::numeric_limits<SysTime>::min(); - milliseconds *= kMillisecondsPerSecond; + milliseconds = min_seconds * kMillisecondsPerSecond; } else { - CHECK(sizeof(SysTime) < sizeof(int64)) << "integer overflow"; - milliseconds = std::numeric_limits<SysTime>::max(); - milliseconds *= kMillisecondsPerSecond; + milliseconds = max_seconds * kMillisecondsPerSecond; milliseconds += (kMillisecondsPerSecond - 1); } } else { diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc index 7f3fde0..81a3358 100644 --- a/base/time/time_unittest.cc +++ b/base/time/time_unittest.cc @@ -7,6 +7,8 @@ #include <time.h> #include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -541,6 +543,28 @@ TEST_F(TimeTest, TimeTOverflow) { } #endif +#if defined(OS_ANDROID) +TEST_F(TimeTest, FromLocalExplodedCrashOnAndroid) { + // This crashed inside Time:: FromLocalExploded() on Android 4.1.2. + // See http://crbug.com/287821 + Time::Exploded midnight = {2013, // year + 10, // month + 0, // day_of_week + 13, // day_of_month + 0, // hour + 0, // minute + 0, // second + }; + // The string passed to putenv() must be a char* and the documentation states + // that it 'becomes part of the environment', so use a static buffer. + static char buffer[] = "TZ=America/Santiago"; + putenv(buffer); + tzset(); + Time t = Time::FromLocalExploded(midnight); + EXPECT_EQ(1381633200, t.ToTimeT()); +} +#endif // OS_ANDROID + TEST(TimeTicks, Deltas) { for (int index = 0; index < 50; index++) { TimeTicks ticks_start = TimeTicks::Now(); |