Change Windows daylight saving time calculations to
authorwhessev8 <whessev8@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Dec 2008 13:20:00 +0000 (13:20 +0000)
committerwhessev8 <whessev8@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Dec 2008 13:20:00 +0000 (13:20 +0000)
use C stdlib local time functions, not Win32 functions.
Win32 time functions slowed down with Windows XP SP3.
Change all platforms to use equivalent years between 2008 and
2035 for years outside the range 1970..2037.
Review URL: http://codereview.chromium.org/12824

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@896 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/date-delay.js
src/platform-win32.cc

index c1b19e5c0b72605c60b78f8fbf83efa0bd70f7e9..bef1fb42a8c4c49da953fad07284f5cb8d351d09 100644 (file)
@@ -111,24 +111,27 @@ function DateFromTime(time) {
 
 // ECMA 262 - 15.9.1.9
 function EquivalentYear(year) {
-  // Returns an equivalent year in the range [1956-2000] matching
+  // Returns an equivalent year in the range [2008-2035] matching
   // - leap year.
   // - week day of first day.
   var time = TimeFromYear(year);
-  return (InLeapYear(time) == 0 ? 1967 : 1956) + (WeekDay(time) * 12) % 28;
+  var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + 
+      (WeekDay(time) * 12) % 28;
+  // Find the year in the range 2008..2037 that is equivalent mod 28.
+  // Add 3*28 to give a positive argument to the modulus operator.
+  return 2008 + (recent_year + 3*28 - 2008) % 28;
 }
 
 
 function EquivalentTime(t) {
   // The issue here is that some library calls don't work right for dates
-  // that cannot be represented using a signed 32 bit integer (measured in
-  // whole seconds based on the 1970 epoch).
+  // that cannot be represented using a non-negative signed 32 bit integer
+  // (measured in whole seconds based on the 1970 epoch).
   // We solve this by mapping the time to a year with same leap-year-ness
-  // and same starting day for the year.
-  // As an optimization we avoid finding an equivalent year in the common
-  // case.  We are measuring in ms here so the 32 bit signed integer range
-  // is +-(1<<30)*1000 ie approximately +-2.1e20.
-  if (t >= -2.1e12 && t <= 2.1e12) return t;
+  // and same starting day for the year.  The ECMAscript specification says
+  // we must do this, but for compatability with other browsers, we use
+  // the actual year if it is in the range 1970..2037
+  if (t >= 0 && t <= 2.1e12) return t;
   var day = MakeDay(EquivalentYear(YearFromTime(t)), MonthFromTime(t), DateFromTime(t));
   return TimeClip(MakeDate(day, TimeWithinDay(t)));
 }
index 43e88f34d6652f43eb15dd2336a662d264a7d514..bd65dde6ed8b9e9ca0bf7d13c9a7f2d9e7f4b848 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <windows.h>
 
+#include <time.h>  // For LocalOffset() implementation.
 #include <mmsystem.h>  // For timeGetTime().
 #include <dbghelp.h>  // For SymLoadModule64 and al.
 #include <tlhelp32.h>  // For Module32First and al.
@@ -323,6 +324,8 @@ void Time::TzSet() {
   // Just return if timezone information has already been initialized.
   if (tz_initialized_) return;
 
+  // Initialize POSIX time zone data.
+  _tzset();
   // Obtain timezone information from operating system.
   memset(&tzinfo_, 0, sizeof(tzinfo_));
   if (GetTimeZoneInformation(&tzinfo_) == TIME_ZONE_ID_INVALID) {
@@ -396,9 +399,9 @@ void Time::SetToCurrentTime() {
   static bool initialized = false;
   static TimeStamp init_time;
   static DWORD init_ticks;
-  static const int kHundredNanosecondsPerSecond = 10000;
-  static const int kMaxClockElapsedTime =
-      60*60*24*kHundredNanosecondsPerSecond;  // 1 day
+  static const int64_t kHundredNanosecondsPerSecond = 10000000;
+  static const int64_t kMaxClockElapsedTime =
+      60*kHundredNanosecondsPerSecond;  // 1 minute
 
   // If we are uninitialized, we need to resync the clock.
   bool needs_resync = !initialized;
@@ -429,30 +432,37 @@ void Time::SetToCurrentTime() {
 
 // Return the local timezone offset in milliseconds east of UTC. This
 // takes into account whether daylight saving is in effect at the time.
+// Only times in the 32-bit Unix range may be passed to this function.
+// Also, adding the time-zone offset to the input must not overflow.
+// The function EquivalentTime() in date-delay.js guarantees this.
 int64_t Time::LocalOffset() {
   // Initialize timezone information, if needed.
   TzSet();
 
-  // Convert timestamp to date/time components. These are now in UTC
-  // format. NB: Please do not replace the following three calls with one
-  // call to FileTimeToLocalFileTime(), because it does not handle
-  // daylight saving correctly.
-  SYSTEMTIME utc;
-  FileTimeToSystemTime(&ft(), &utc);
-
-  // Convert to local time, using timezone information.
-  SYSTEMTIME local;
-  SystemTimeToTzSpecificLocalTime(&tzinfo_, &utc, &local);
-
-  // Convert local time back to a timestamp. This timestamp now
-  // has a bias similar to the local timezone bias in effect
-  // at the time of the original timestamp.
-  Time localtime;
-  SystemTimeToFileTime(&local, &localtime.ft());
-
-  // The difference between the new local timestamp and the original
-  // timestamp and is the local timezone offset.
-  return localtime.Diff(this);
+  Time rounded_to_second(*this);
+  rounded_to_second.t() = rounded_to_second.t() / 1000 / kTimeScaler *
+      1000 * kTimeScaler;
+  // Convert to local time using POSIX localtime function.
+  // Windows XP Service Pack 3 made SystemTimeToTzSpecificLocalTime()
+  // very slow.  Other browsers use localtime().
+
+  // Convert from JavaScript milliseconds past 1/1/1970 0:00:00 to
+  // POSIX seconds past 1/1/1970 0:00:00.
+  double unchecked_posix_time = rounded_to_second.ToJSTime() / 1000;
+  if (unchecked_posix_time > INT_MAX || unchecked_posix_time < 0) {
+    return 0;
+  }
+  // Because _USE_32BIT_TIME_T is defined, time_t is a 32-bit int.
+  time_t posix_time = static_cast<time_t>(unchecked_posix_time);
+
+  // Convert to local time, as struct with fields for day, hour, year, etc.
+  tm posix_local_time_struct;
+  if (localtime_s(&posix_local_time_struct, &posix_time)) return 0;
+  // Convert local time in struct to POSIX time as if it were a UTC time.
+  time_t local_posix_time = _mkgmtime(&posix_local_time_struct);
+  Time localtime(1000.0 * local_posix_time);
+
+  return localtime.Diff(&rounded_to_second);
 }