Cherry-pick http://codereview.chromium.org/1742012

This reverts commit d69d2e30f295acb0d25771f412ca271869d0ba43, which is now included in the above change.

Fix bug: 2629965

Change-Id: I5b07ba8f9a39c4337bc03b0c9aa91eafcd6863be
diff --git a/src/date.js b/src/date.js
index b4027c1..6b6ed42 100644
--- a/src/date.js
+++ b/src/date.js
@@ -118,9 +118,16 @@
 }
 
 
-// Because computing the DST offset is a pretty expensive operation
-// we keep a cache of last computed offset along with a time interval
+// local_time_offset is initialized when the DST_offset_cache is missed.
+// It must not be used until after a call to DaylightSavingsOffset().
+// In this way, only one check, for a DST cache miss, is needed.
+var local_time_offset;
+
+
+// Because computing the DST offset is an expensive operation,
+// we keep a cache of the last computed DST offset along with a time interval
 // where we know the cache is valid.
+// When the cache is valid, local_time_offset is also valid.
 var DST_offset_cache = {
   // Cached DST offset.
   offset: 0,
@@ -146,6 +153,11 @@
     // If the time fits in the cached interval, return the cached offset.
     if (t <= end) return cache.offset;
 
+    // If the cache misses, the local_time_offset may not be initialized.
+    if (IS_UNDEFINED(local_time_offset)) {
+      local_time_offset = %DateLocalTimeOffset();
+    }
+
     // Compute a possible new interval end.
     var new_end = end + cache.increment;
 
@@ -182,6 +194,10 @@
     }
   }
 
+  // If the cache misses, the local_time_offset may not be initialized.
+  if (IS_UNDEFINED(local_time_offset)) {
+    local_time_offset = %DateLocalTimeOffset();
+  }
   // Compute the DST offset for the time and shrink the cache interval
   // to only contain the time. This allows fast repeated DST offset
   // computations for the same time.
@@ -212,15 +228,17 @@
   return Modulo(DAY(time) + 4, 7);
 }
 
-var local_time_offset = %DateLocalTimeOffset();
 
 function LocalTime(time) {
   if (NUMBER_IS_NAN(time)) return time;
-  return time + local_time_offset + DaylightSavingsOffset(time);
+  // DaylightSavingsOffset called before local_time_offset used.
+  return time + DaylightSavingsOffset(time) + local_time_offset;
 }
 
 function LocalTimeNoCheck(time) {
   // Inline the DST offset cache checks for speed.
+  // The cache is hit, or DaylightSavingsOffset is called,
+  // before local_time_offset is used.
   var cache = DST_offset_cache;
   if (cache.start <= time && time <= cache.end) {
     var dst_offset = cache.offset;
@@ -233,6 +251,11 @@
 
 function UTC(time) {
   if (NUMBER_IS_NAN(time)) return time;
+  // local_time_offset is needed before the call to DaylightSavingsOffset,
+  // so it may be uninitialized.
+  if (IS_UNDEFINED(local_time_offset)) {
+    local_time_offset = %DateLocalTimeOffset();
+  }
   var tmp = time - local_time_offset;
   return tmp - DaylightSavingsOffset(tmp);
 }
@@ -628,7 +651,7 @@
   }
 
   var timezoneOffset =
-      (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute;
+      (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
   var sign = (timezoneOffset >= 0) ? 1 : -1;
   var hours = FLOOR((sign * timezoneOffset)/60);
   var min   = FLOOR((sign * timezoneOffset)%60);
@@ -693,7 +716,8 @@
 function DateToString() {
   var t = DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t);
+  var time_zone_string = LocalTimezoneString(t);  // May update local offset.
+  return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
 }
 
 
@@ -709,8 +733,8 @@
 function DateToTimeString() {
   var t = DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  var lt = LocalTimeNoCheck(t);
-  return TimeString(lt) + LocalTimezoneString(lt);
+  var time_zone_string = LocalTimezoneString(t);  // May update local offset.
+  return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
 }