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/base/timezone-cache.h" 9 #include "src/globals.h" 10 11 namespace v8 { 12 namespace internal { 13 14 class DateCache { 15 public: 16 static const int kMsPerMin = 60 * 1000; 17 static const int kSecPerDay = 24 * 60 * 60; 18 static const int64_t kMsPerDay = kSecPerDay * 1000; 19 static const int64_t kMsPerMonth = kMsPerDay * 30; 20 21 // The largest time that can be passed to OS date-time library functions. 22 static const int kMaxEpochTimeInSec = kMaxInt; 23 static const int64_t kMaxEpochTimeInMs = 24 static_cast<int64_t>(kMaxInt) * 1000; 25 26 // The largest time that can be stored in JSDate. 27 static const int64_t kMaxTimeInMs = 28 static_cast<int64_t>(864000000) * 10000000; 29 30 // Conservative upper bound on time that can be stored in JSDate 31 // before UTC conversion. 32 static const int64_t kMaxTimeBeforeUTCInMs = kMaxTimeInMs + kMsPerMonth; 33 34 // Sentinel that denotes an invalid local offset. 35 static const int kInvalidLocalOffsetInMs = kMaxInt; 36 // Sentinel that denotes an invalid cache stamp. 37 // It is an invariant of DateCache that cache stamp is non-negative. 38 static const int kInvalidStamp = -1; 39 40 DateCache(); 41 ~DateCache()42 virtual ~DateCache() { 43 delete tz_cache_; 44 tz_cache_ = nullptr; 45 } 46 47 48 // Clears cached timezone information and increments the cache stamp. 49 void ResetDateCache(); 50 51 52 // Computes floor(time_ms / kMsPerDay). DaysFromTime(int64_t time_ms)53 static int DaysFromTime(int64_t time_ms) { 54 if (time_ms < 0) time_ms -= (kMsPerDay - 1); 55 return static_cast<int>(time_ms / kMsPerDay); 56 } 57 58 59 // Computes modulo(time_ms, kMsPerDay) given that 60 // days = floor(time_ms / kMsPerDay). TimeInDay(int64_t time_ms,int days)61 static int TimeInDay(int64_t time_ms, int days) { 62 return static_cast<int>(time_ms - days * kMsPerDay); 63 } 64 65 // ECMA 262 - ES#sec-timeclip TimeClip (time) 66 static double TimeClip(double time); 67 68 // Given the number of days since the epoch, computes the weekday. 69 // ECMA 262 - 15.9.1.6. Weekday(int days)70 int Weekday(int days) { 71 int result = (days + 4) % 7; 72 return result >= 0 ? result : result + 7; 73 } 74 75 IsLeap(int year)76 bool IsLeap(int year) { 77 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 78 } 79 80 // ECMA 262 - ES#sec-local-time-zone-adjustment LocalOffsetInMs(int64_t time,bool is_utc)81 int LocalOffsetInMs(int64_t time, bool is_utc) { 82 return GetLocalOffsetFromOS(time, is_utc); 83 } 84 85 LocalTimezone(int64_t time_ms)86 const char* LocalTimezone(int64_t time_ms) { 87 if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) { 88 time_ms = EquivalentTime(time_ms); 89 } 90 bool is_dst = DaylightSavingsOffsetInMs(time_ms) != 0; 91 const char** name = is_dst ? &dst_tz_name_ : &tz_name_; 92 if (*name == nullptr) { 93 *name = tz_cache_->LocalTimezone(static_cast<double>(time_ms)); 94 } 95 return *name; 96 } 97 98 // ECMA 262 - 15.9.5.26 TimezoneOffset(int64_t time_ms)99 int TimezoneOffset(int64_t time_ms) { 100 int64_t local_ms = ToLocal(time_ms); 101 return static_cast<int>((time_ms - local_ms) / kMsPerMin); 102 } 103 104 // ECMA 262 - ES#sec-localtime-t 105 // LocalTime(t) = t + LocalTZA(t, true) ToLocal(int64_t time_ms)106 int64_t ToLocal(int64_t time_ms) { 107 return time_ms + LocalOffsetInMs(time_ms, true); 108 } 109 110 // ECMA 262 - ES#sec-utc-t 111 // UTC(t) = t - LocalTZA(t, false) ToUTC(int64_t time_ms)112 int64_t ToUTC(int64_t time_ms) { 113 return time_ms - LocalOffsetInMs(time_ms, false); 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 EquivalentTime(int64_t time_ms)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. EquivalentYear(int year)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 // Breaks down the time value. 156 void BreakDownTime(int64_t time_ms, int* year, int* month, int* day, 157 int* weekday, int* hour, int* min, int* sec, int* ms); 158 159 // Cache stamp is used for invalidating caches in JSDate. 160 // We increment the stamp each time when the timezone information changes. 161 // JSDate objects perform stamp check and invalidate their caches if 162 // their saved stamp is not equal to the current stamp. stamp()163 Smi* stamp() { return stamp_; } stamp_address()164 void* stamp_address() { return &stamp_; } 165 166 // These functions are virtual so that we can override them when testing. GetDaylightSavingsOffsetFromOS(int64_t time_sec)167 virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) { 168 double time_ms = static_cast<double>(time_sec * 1000); 169 return static_cast<int>(tz_cache_->DaylightSavingsOffset(time_ms)); 170 } 171 172 virtual int GetLocalOffsetFromOS(int64_t time_ms, bool is_utc); 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 InvalidSegment(DST * segment)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 // Timezone name cache 238 const char* tz_name_; 239 const char* dst_tz_name_; 240 241 base::TimezoneCache* tz_cache_; 242 }; 243 244 } // namespace internal 245 } // namespace v8 246 247 #endif // V8_DATE_H_ 248