1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_DATE_H_ 6 #define V8_DATE_H_ 7 8 #include "src/allocation.h" 9 #include "src/globals.h" 10 #include "src/platform.h" 11 12 13 namespace v8 { 14 namespace internal { 15 16 class DateCache { 17 public: 18 static const int kMsPerMin = 60 * 1000; 19 static const int kSecPerDay = 24 * 60 * 60; 20 static const int64_t kMsPerDay = kSecPerDay * 1000; 21 22 // The largest time that can be passed to OS date-time library functions. 23 static const int kMaxEpochTimeInSec = kMaxInt; 24 static const int64_t kMaxEpochTimeInMs = 25 static_cast<int64_t>(kMaxInt) * 1000; 26 27 // The largest time that can be stored in JSDate. 28 static const int64_t kMaxTimeInMs = 29 static_cast<int64_t>(864000000) * 10000000; 30 31 // Conservative upper bound on time that can be stored in JSDate 32 // before UTC conversion. 33 static const int64_t kMaxTimeBeforeUTCInMs = 34 kMaxTimeInMs + 10 * kMsPerDay; 35 36 // Sentinel that denotes an invalid local offset. 37 static const int kInvalidLocalOffsetInMs = kMaxInt; 38 // Sentinel that denotes an invalid cache stamp. 39 // It is an invariant of DateCache that cache stamp is non-negative. 40 static const int kInvalidStamp = -1; 41 42 DateCache() : stamp_(0), tz_cache_(OS::CreateTimezoneCache()) { 43 ResetDateCache(); 44 } 45 46 virtual ~DateCache() { 47 OS::DisposeTimezoneCache(tz_cache_); 48 tz_cache_ = NULL; 49 } 50 51 52 // Clears cached timezone information and increments the cache stamp. 53 void ResetDateCache(); 54 55 56 // Computes floor(time_ms / kMsPerDay). 57 static int DaysFromTime(int64_t time_ms) { 58 if (time_ms < 0) time_ms -= (kMsPerDay - 1); 59 return static_cast<int>(time_ms / kMsPerDay); 60 } 61 62 63 // Computes modulo(time_ms, kMsPerDay) given that 64 // days = floor(time_ms / kMsPerDay). 65 static int TimeInDay(int64_t time_ms, int days) { 66 return static_cast<int>(time_ms - days * kMsPerDay); 67 } 68 69 70 // Given the number of days since the epoch, computes the weekday. 71 // ECMA 262 - 15.9.1.6. 72 int Weekday(int days) { 73 int result = (days + 4) % 7; 74 return result >= 0 ? result : result + 7; 75 } 76 77 78 bool IsLeap(int year) { 79 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 80 } 81 82 83 // ECMA 262 - 15.9.1.7. 84 int LocalOffsetInMs() { 85 if (local_offset_ms_ == kInvalidLocalOffsetInMs) { 86 local_offset_ms_ = GetLocalOffsetFromOS(); 87 } 88 return local_offset_ms_; 89 } 90 91 92 const char* LocalTimezone(int64_t time_ms) { 93 if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) { 94 time_ms = EquivalentTime(time_ms); 95 } 96 return OS::LocalTimezone(static_cast<double>(time_ms), tz_cache_); 97 } 98 99 // ECMA 262 - 15.9.5.26 100 int TimezoneOffset(int64_t time_ms) { 101 int64_t local_ms = ToLocal(time_ms); 102 return static_cast<int>((time_ms - local_ms) / kMsPerMin); 103 } 104 105 // ECMA 262 - 15.9.1.9 106 int64_t ToLocal(int64_t time_ms) { 107 return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms); 108 } 109 110 // ECMA 262 - 15.9.1.9 111 int64_t ToUTC(int64_t time_ms) { 112 time_ms -= LocalOffsetInMs(); 113 return time_ms - DaylightSavingsOffsetInMs(time_ms); 114 } 115 116 117 // Computes a time equivalent to the given time according 118 // to ECMA 262 - 15.9.1.9. 119 // The issue here is that some library calls don't work right for dates 120 // that cannot be represented using a non-negative signed 32 bit integer 121 // (measured in whole seconds based on the 1970 epoch). 122 // We solve this by mapping the time to a year with same leap-year-ness 123 // and same starting day for the year. The ECMAscript specification says 124 // we must do this, but for compatibility with other browsers, we use 125 // the actual year if it is in the range 1970..2037 126 int64_t EquivalentTime(int64_t time_ms) { 127 int days = DaysFromTime(time_ms); 128 int time_within_day_ms = static_cast<int>(time_ms - days * kMsPerDay); 129 int year, month, day; 130 YearMonthDayFromDays(days, &year, &month, &day); 131 int new_days = DaysFromYearMonth(EquivalentYear(year), month) + day - 1; 132 return static_cast<int64_t>(new_days) * kMsPerDay + time_within_day_ms; 133 } 134 135 // Returns an equivalent year in the range [2008-2035] matching 136 // - leap year, 137 // - week day of first day. 138 // ECMA 262 - 15.9.1.9. 139 int EquivalentYear(int year) { 140 int week_day = Weekday(DaysFromYearMonth(year, 0)); 141 int recent_year = (IsLeap(year) ? 1956 : 1967) + (week_day * 12) % 28; 142 // Find the year in the range 2008..2037 that is equivalent mod 28. 143 // Add 3*28 to give a positive argument to the modulus operator. 144 return 2008 + (recent_year + 3 * 28 - 2008) % 28; 145 } 146 147 // Given the number of days since the epoch, computes 148 // the corresponding year, month, and day. 149 void YearMonthDayFromDays(int days, int* year, int* month, int* day); 150 151 // Computes the number of days since the epoch for 152 // the first day of the given month in the given year. 153 int DaysFromYearMonth(int year, int month); 154 155 // Cache stamp is used for invalidating caches in JSDate. 156 // We increment the stamp each time when the timezone information changes. 157 // JSDate objects perform stamp check and invalidate their caches if 158 // their saved stamp is not equal to the current stamp. 159 Smi* stamp() { return stamp_; } 160 void* stamp_address() { return &stamp_; } 161 162 // These functions are virtual so that we can override them when testing. 163 virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) { 164 double time_ms = static_cast<double>(time_sec * 1000); 165 return static_cast<int>(OS::DaylightSavingsOffset(time_ms, tz_cache_)); 166 } 167 168 virtual int GetLocalOffsetFromOS() { 169 double offset = OS::LocalTimeOffset(tz_cache_); 170 ASSERT(offset < kInvalidLocalOffsetInMs); 171 return static_cast<int>(offset); 172 } 173 174 private: 175 // The implementation relies on the fact that no time zones have 176 // more than one daylight savings offset change per 19 days. 177 // In Egypt in 2010 they decided to suspend DST during Ramadan. This 178 // led to a short interval where DST is in effect from September 10 to 179 // September 30. 180 static const int kDefaultDSTDeltaInSec = 19 * kSecPerDay; 181 182 // Size of the Daylight Savings Time cache. 183 static const int kDSTSize = 32; 184 185 // Daylight Savings Time segment stores a segment of time where 186 // daylight savings offset does not change. 187 struct DST { 188 int start_sec; 189 int end_sec; 190 int offset_ms; 191 int last_used; 192 }; 193 194 // Computes the daylight savings offset for the given time. 195 // ECMA 262 - 15.9.1.8 196 int DaylightSavingsOffsetInMs(int64_t time_ms); 197 198 // Sets the before_ and the after_ segments from the DST cache such that 199 // the before_ segment starts earlier than the given time and 200 // the after_ segment start later than the given time. 201 // Both segments might be invalid. 202 // The last_used counters of the before_ and after_ are updated. 203 void ProbeDST(int time_sec); 204 205 // Finds the least recently used segment from the DST cache that is not 206 // equal to the given 'skip' segment. 207 DST* LeastRecentlyUsedDST(DST* skip); 208 209 // Extends the after_ segment with the given point or resets it 210 // if it starts later than the given time + kDefaultDSTDeltaInSec. 211 inline void ExtendTheAfterSegment(int time_sec, int offset_ms); 212 213 // Makes the given segment invalid. 214 inline void ClearSegment(DST* segment); 215 216 bool InvalidSegment(DST* segment) { 217 return segment->start_sec > segment->end_sec; 218 } 219 220 Smi* stamp_; 221 222 // Daylight Saving Time cache. 223 DST dst_[kDSTSize]; 224 int dst_usage_counter_; 225 DST* before_; 226 DST* after_; 227 228 int local_offset_ms_; 229 230 // Year/Month/Day cache. 231 bool ymd_valid_; 232 int ymd_days_; 233 int ymd_year_; 234 int ymd_month_; 235 int ymd_day_; 236 237 TimezoneCache* tz_cache_; 238 }; 239 240 } } // namespace v8::internal 241 242 #endif 243