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