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