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