Cherry pick android: fix base::Time::FromLocalExploded() crash.

Cherry pick of https://codereview.chromium.org/27472003/

Bug: 11313033

Original description:

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

Committed:
https://src.chromium.org/viewvc/chrome?view=rev&revision=229567

Change-Id: Icca4d67b827fbca14d6218493db776fb79db85b8
diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc
index 16eb83c..69e80e1 100644
--- a/base/time/time_posix.cc
+++ b/base/time/time_posix.cc
@@ -211,9 +211,41 @@
   timestruct.tm_zone   = NULL;  // not a POSIX field, so mktime/timegm ignore
 #endif
 
-  SysTime seconds = SysTimeFromTimeStruct(&timestruct, 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(&timestruct, 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(&timestruct, is_local);
+
+    timestruct = timestruct0;
+    timestruct.tm_isdst = 1;
+    int64 seconds_isdst1 = SysTimeFromTimeStruct(&timestruct, 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
@@ -234,14 +266,19 @@
     // 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 @@
 }
 #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();