Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1 | // Copyright 2012 the V8 project authors. All rights reserved. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 5 | #include "src/date.h" |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 6 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 7 | #include "src/objects.h" |
| 8 | #include "src/objects-inl.h" |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 9 | |
| 10 | namespace v8 { |
| 11 | namespace internal { |
| 12 | |
| 13 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 14 | static const int kDaysIn4Years = 4 * 365 + 1; |
| 15 | static const int kDaysIn100Years = 25 * kDaysIn4Years - 1; |
| 16 | static const int kDaysIn400Years = 4 * kDaysIn100Years + 1; |
| 17 | static const int kDays1970to2000 = 30 * 365 + 7; |
| 18 | static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years - |
| 19 | kDays1970to2000; |
| 20 | static const int kYearsOffset = 400000; |
| 21 | static const char kDaysInMonths[] = |
| 22 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
| 23 | |
| 24 | |
| 25 | void DateCache::ResetDateCache() { |
| 26 | static const int kMaxStamp = Smi::kMaxValue; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 27 | if (stamp_->value() >= kMaxStamp) { |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 28 | stamp_ = Smi::FromInt(0); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 29 | } else { |
| 30 | stamp_ = Smi::FromInt(stamp_->value() + 1); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 31 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 32 | DCHECK(stamp_ != Smi::FromInt(kInvalidStamp)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 33 | for (int i = 0; i < kDSTSize; ++i) { |
| 34 | ClearSegment(&dst_[i]); |
| 35 | } |
| 36 | dst_usage_counter_ = 0; |
| 37 | before_ = &dst_[0]; |
| 38 | after_ = &dst_[1]; |
| 39 | local_offset_ms_ = kInvalidLocalOffsetInMs; |
| 40 | ymd_valid_ = false; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 41 | base::OS::ClearTimezoneCache(tz_cache_); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | |
| 45 | void DateCache::ClearSegment(DST* segment) { |
| 46 | segment->start_sec = kMaxEpochTimeInSec; |
| 47 | segment->end_sec = -kMaxEpochTimeInSec; |
| 48 | segment->offset_ms = 0; |
| 49 | segment->last_used = 0; |
| 50 | } |
| 51 | |
| 52 | |
| 53 | void DateCache::YearMonthDayFromDays( |
| 54 | int days, int* year, int* month, int* day) { |
| 55 | if (ymd_valid_) { |
| 56 | // Check conservatively if the given 'days' has |
| 57 | // the same year and month as the cached 'days'. |
| 58 | int new_day = ymd_day_ + (days - ymd_days_); |
| 59 | if (new_day >= 1 && new_day <= 28) { |
| 60 | ymd_day_ = new_day; |
| 61 | ymd_days_ = days; |
| 62 | *year = ymd_year_; |
| 63 | *month = ymd_month_; |
| 64 | *day = new_day; |
| 65 | return; |
| 66 | } |
| 67 | } |
| 68 | int save_days = days; |
| 69 | |
| 70 | days += kDaysOffset; |
| 71 | *year = 400 * (days / kDaysIn400Years) - kYearsOffset; |
| 72 | days %= kDaysIn400Years; |
| 73 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 74 | DCHECK_EQ(save_days, DaysFromYearMonth(*year, 0) + days); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 75 | |
| 76 | days--; |
| 77 | int yd1 = days / kDaysIn100Years; |
| 78 | days %= kDaysIn100Years; |
| 79 | *year += 100 * yd1; |
| 80 | |
| 81 | days++; |
| 82 | int yd2 = days / kDaysIn4Years; |
| 83 | days %= kDaysIn4Years; |
| 84 | *year += 4 * yd2; |
| 85 | |
| 86 | days--; |
| 87 | int yd3 = days / 365; |
| 88 | days %= 365; |
| 89 | *year += yd3; |
| 90 | |
| 91 | |
| 92 | bool is_leap = (!yd1 || yd2) && !yd3; |
| 93 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 94 | DCHECK(days >= -1); |
| 95 | DCHECK(is_leap || (days >= 0)); |
| 96 | DCHECK((days < 365) || (is_leap && (days < 366))); |
| 97 | DCHECK(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0)))); |
| 98 | DCHECK(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days)); |
| 99 | DCHECK(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 100 | |
| 101 | days += is_leap; |
| 102 | |
| 103 | // Check if the date is after February. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 104 | if (days >= 31 + 28 + BoolToInt(is_leap)) { |
| 105 | days -= 31 + 28 + BoolToInt(is_leap); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 106 | // Find the date starting from March. |
| 107 | for (int i = 2; i < 12; i++) { |
| 108 | if (days < kDaysInMonths[i]) { |
| 109 | *month = i; |
| 110 | *day = days + 1; |
| 111 | break; |
| 112 | } |
| 113 | days -= kDaysInMonths[i]; |
| 114 | } |
| 115 | } else { |
| 116 | // Check January and February. |
| 117 | if (days < 31) { |
| 118 | *month = 0; |
| 119 | *day = days + 1; |
| 120 | } else { |
| 121 | *month = 1; |
| 122 | *day = days - 31 + 1; |
| 123 | } |
| 124 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 125 | DCHECK(DaysFromYearMonth(*year, *month) + *day - 1 == save_days); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 126 | ymd_valid_ = true; |
| 127 | ymd_year_ = *year; |
| 128 | ymd_month_ = *month; |
| 129 | ymd_day_ = *day; |
| 130 | ymd_days_ = save_days; |
| 131 | } |
| 132 | |
| 133 | |
| 134 | int DateCache::DaysFromYearMonth(int year, int month) { |
| 135 | static const int day_from_month[] = {0, 31, 59, 90, 120, 151, |
| 136 | 181, 212, 243, 273, 304, 334}; |
| 137 | static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152, |
| 138 | 182, 213, 244, 274, 305, 335}; |
| 139 | |
| 140 | year += month / 12; |
| 141 | month %= 12; |
| 142 | if (month < 0) { |
| 143 | year--; |
| 144 | month += 12; |
| 145 | } |
| 146 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 147 | DCHECK(month >= 0); |
| 148 | DCHECK(month < 12); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 149 | |
| 150 | // year_delta is an arbitrary number such that: |
| 151 | // a) year_delta = -1 (mod 400) |
| 152 | // b) year + year_delta > 0 for years in the range defined by |
| 153 | // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of |
| 154 | // Jan 1 1970. This is required so that we don't run into integer |
| 155 | // division of negative numbers. |
| 156 | // c) there shouldn't be an overflow for 32-bit integers in the following |
| 157 | // operations. |
| 158 | static const int year_delta = 399999; |
| 159 | static const int base_day = 365 * (1970 + year_delta) + |
| 160 | (1970 + year_delta) / 4 - |
| 161 | (1970 + year_delta) / 100 + |
| 162 | (1970 + year_delta) / 400; |
| 163 | |
| 164 | int year1 = year + year_delta; |
| 165 | int day_from_year = 365 * year1 + |
| 166 | year1 / 4 - |
| 167 | year1 / 100 + |
| 168 | year1 / 400 - |
| 169 | base_day; |
| 170 | |
| 171 | if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) { |
| 172 | return day_from_year + day_from_month[month]; |
| 173 | } |
| 174 | return day_from_year + day_from_month_leap[month]; |
| 175 | } |
| 176 | |
| 177 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 178 | void DateCache::BreakDownTime(int64_t time_ms, int* year, int* month, int* day, |
| 179 | int* weekday, int* hour, int* min, int* sec, |
| 180 | int* ms) { |
| 181 | int const days = DaysFromTime(time_ms); |
| 182 | int const time_in_day_ms = TimeInDay(time_ms, days); |
| 183 | YearMonthDayFromDays(days, year, month, day); |
| 184 | *weekday = Weekday(days); |
| 185 | *hour = time_in_day_ms / (60 * 60 * 1000); |
| 186 | *min = (time_in_day_ms / (60 * 1000)) % 60; |
| 187 | *sec = (time_in_day_ms / 1000) % 60; |
| 188 | *ms = time_in_day_ms % 1000; |
| 189 | } |
| 190 | |
| 191 | |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 192 | void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) { |
| 193 | if (after_->offset_ms == offset_ms && |
| 194 | after_->start_sec <= time_sec + kDefaultDSTDeltaInSec && |
| 195 | time_sec <= after_->end_sec) { |
| 196 | // Extend the after_ segment. |
| 197 | after_->start_sec = time_sec; |
| 198 | } else { |
| 199 | // The after_ segment is either invalid or starts too late. |
| 200 | if (after_->start_sec <= after_->end_sec) { |
| 201 | // If the after_ segment is valid, replace it with a new segment. |
| 202 | after_ = LeastRecentlyUsedDST(before_); |
| 203 | } |
| 204 | after_->start_sec = time_sec; |
| 205 | after_->end_sec = time_sec; |
| 206 | after_->offset_ms = offset_ms; |
| 207 | after_->last_used = ++dst_usage_counter_; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | |
| 212 | int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) { |
| 213 | int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs) |
| 214 | ? static_cast<int>(time_ms / 1000) |
| 215 | : static_cast<int>(EquivalentTime(time_ms) / 1000); |
| 216 | |
| 217 | // Invalidate cache if the usage counter is close to overflow. |
| 218 | // Note that dst_usage_counter is incremented less than ten times |
| 219 | // in this function. |
| 220 | if (dst_usage_counter_ >= kMaxInt - 10) { |
| 221 | dst_usage_counter_ = 0; |
| 222 | for (int i = 0; i < kDSTSize; ++i) { |
| 223 | ClearSegment(&dst_[i]); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | // Optimistic fast check. |
| 228 | if (before_->start_sec <= time_sec && |
| 229 | time_sec <= before_->end_sec) { |
| 230 | // Cache hit. |
| 231 | before_->last_used = ++dst_usage_counter_; |
| 232 | return before_->offset_ms; |
| 233 | } |
| 234 | |
| 235 | ProbeDST(time_sec); |
| 236 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 237 | DCHECK(InvalidSegment(before_) || before_->start_sec <= time_sec); |
| 238 | DCHECK(InvalidSegment(after_) || time_sec < after_->start_sec); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 239 | |
| 240 | if (InvalidSegment(before_)) { |
| 241 | // Cache miss. |
| 242 | before_->start_sec = time_sec; |
| 243 | before_->end_sec = time_sec; |
| 244 | before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
| 245 | before_->last_used = ++dst_usage_counter_; |
| 246 | return before_->offset_ms; |
| 247 | } |
| 248 | |
| 249 | if (time_sec <= before_->end_sec) { |
| 250 | // Cache hit. |
| 251 | before_->last_used = ++dst_usage_counter_; |
| 252 | return before_->offset_ms; |
| 253 | } |
| 254 | |
| 255 | if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) { |
| 256 | // If the before_ segment ends too early, then just |
| 257 | // query for the offset of the time_sec |
| 258 | int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
| 259 | ExtendTheAfterSegment(time_sec, offset_ms); |
| 260 | // This swap helps the optimistic fast check in subsequent invocations. |
| 261 | DST* temp = before_; |
| 262 | before_ = after_; |
| 263 | after_ = temp; |
| 264 | return offset_ms; |
| 265 | } |
| 266 | |
| 267 | // Now the time_sec is between |
| 268 | // before_->end_sec and before_->end_sec + default DST delta. |
| 269 | // Update the usage counter of before_ since it is going to be used. |
| 270 | before_->last_used = ++dst_usage_counter_; |
| 271 | |
| 272 | // Check if after_ segment is invalid or starts too late. |
| 273 | // Note that start_sec of invalid segments is kMaxEpochTimeInSec. |
| 274 | if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) { |
| 275 | int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec; |
| 276 | int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec); |
| 277 | ExtendTheAfterSegment(new_after_start_sec, new_offset_ms); |
| 278 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 279 | DCHECK(!InvalidSegment(after_)); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 280 | // Update the usage counter of after_ since it is going to be used. |
| 281 | after_->last_used = ++dst_usage_counter_; |
| 282 | } |
| 283 | |
| 284 | // Now the time_sec is between before_->end_sec and after_->start_sec. |
| 285 | // Only one daylight savings offset change can occur in this interval. |
| 286 | |
| 287 | if (before_->offset_ms == after_->offset_ms) { |
| 288 | // Merge two segments if they have the same offset. |
| 289 | before_->end_sec = after_->end_sec; |
| 290 | ClearSegment(after_); |
| 291 | return before_->offset_ms; |
| 292 | } |
| 293 | |
| 294 | // Binary search for daylight savings offset change point, |
| 295 | // but give up if we don't find it in four iterations. |
| 296 | for (int i = 4; i >= 0; --i) { |
| 297 | int delta = after_->start_sec - before_->end_sec; |
| 298 | int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2; |
| 299 | int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec); |
| 300 | if (before_->offset_ms == offset_ms) { |
| 301 | before_->end_sec = middle_sec; |
| 302 | if (time_sec <= before_->end_sec) { |
| 303 | return offset_ms; |
| 304 | } |
| 305 | } else { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 306 | DCHECK(after_->offset_ms == offset_ms); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 307 | after_->start_sec = middle_sec; |
| 308 | if (time_sec >= after_->start_sec) { |
| 309 | // This swap helps the optimistic fast check in subsequent invocations. |
| 310 | DST* temp = before_; |
| 311 | before_ = after_; |
| 312 | after_ = temp; |
| 313 | return offset_ms; |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | UNREACHABLE(); |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | |
| 322 | void DateCache::ProbeDST(int time_sec) { |
| 323 | DST* before = NULL; |
| 324 | DST* after = NULL; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 325 | DCHECK(before_ != after_); |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 326 | |
| 327 | for (int i = 0; i < kDSTSize; ++i) { |
| 328 | if (dst_[i].start_sec <= time_sec) { |
| 329 | if (before == NULL || before->start_sec < dst_[i].start_sec) { |
| 330 | before = &dst_[i]; |
| 331 | } |
| 332 | } else if (time_sec < dst_[i].end_sec) { |
| 333 | if (after == NULL || after->end_sec > dst_[i].end_sec) { |
| 334 | after = &dst_[i]; |
| 335 | } |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | // If before or after segments were not found, |
| 340 | // then set them to any invalid segment. |
| 341 | if (before == NULL) { |
| 342 | before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after); |
| 343 | } |
| 344 | if (after == NULL) { |
| 345 | after = InvalidSegment(after_) && before != after_ |
| 346 | ? after_ : LeastRecentlyUsedDST(before); |
| 347 | } |
| 348 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 349 | DCHECK(before != NULL); |
| 350 | DCHECK(after != NULL); |
| 351 | DCHECK(before != after); |
| 352 | DCHECK(InvalidSegment(before) || before->start_sec <= time_sec); |
| 353 | DCHECK(InvalidSegment(after) || time_sec < after->start_sec); |
| 354 | DCHECK(InvalidSegment(before) || InvalidSegment(after) || |
Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 355 | before->end_sec < after->start_sec); |
| 356 | |
| 357 | before_ = before; |
| 358 | after_ = after; |
| 359 | } |
| 360 | |
| 361 | |
| 362 | DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) { |
| 363 | DST* result = NULL; |
| 364 | for (int i = 0; i < kDSTSize; ++i) { |
| 365 | if (&dst_[i] == skip) continue; |
| 366 | if (result == NULL || result->last_used > dst_[i].last_used) { |
| 367 | result = &dst_[i]; |
| 368 | } |
| 369 | } |
| 370 | ClearSegment(result); |
| 371 | return result; |
| 372 | } |
| 373 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 374 | } // namespace internal |
| 375 | } // namespace v8 |