Line data Source code
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 : #include "src/date.h"
6 :
7 : #include "src/base/overflowing-math.h"
8 : #include "src/conversions.h"
9 : #include "src/objects-inl.h"
10 : #ifdef V8_INTL_SUPPORT
11 : #include "src/objects/intl-objects.h"
12 : #endif
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 :
18 : static const int kDaysIn4Years = 4 * 365 + 1;
19 : static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
20 : static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
21 : static const int kDays1970to2000 = 30 * 365 + 7;
22 : static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
23 : kDays1970to2000;
24 : static const int kYearsOffset = 400000;
25 : static const char kDaysInMonths[] =
26 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
27 :
28 62431 : DateCache::DateCache()
29 : : stamp_(kNullAddress),
30 : tz_cache_(
31 : #ifdef V8_INTL_SUPPORT
32 62431 : Intl::CreateTimeZoneCache()
33 : #else
34 : base::OS::CreateTimezoneCache()
35 : #endif
36 124862 : ) {
37 62431 : ResetDateCache(base::TimezoneCache::TimeZoneDetection::kSkip);
38 62431 : }
39 :
40 62431 : void DateCache::ResetDateCache(
41 : base::TimezoneCache::TimeZoneDetection time_zone_detection) {
42 62431 : if (stamp_->value() >= Smi::kMaxValue) {
43 0 : stamp_ = Smi::zero();
44 : } else {
45 124862 : stamp_ = Smi::FromInt(stamp_->value() + 1);
46 : }
47 : DCHECK(stamp_ != Smi::FromInt(kInvalidStamp));
48 4057887 : for (int i = 0; i < kDSTSize; ++i) {
49 : ClearSegment(&dst_[i]);
50 : }
51 62431 : dst_usage_counter_ = 0;
52 62431 : before_ = &dst_[0];
53 62431 : after_ = &dst_[1];
54 62431 : ymd_valid_ = false;
55 : #ifdef V8_INTL_SUPPORT
56 62431 : if (!FLAG_icu_timezone_data) {
57 : #endif
58 10 : local_offset_ms_ = kInvalidLocalOffsetInMs;
59 : #ifdef V8_INTL_SUPPORT
60 : }
61 : #endif
62 62431 : tz_cache_->Clear(time_zone_detection);
63 62430 : tz_name_ = nullptr;
64 62430 : dst_tz_name_ = nullptr;
65 62430 : }
66 :
67 : // ECMA 262 - ES#sec-timeclip TimeClip (time)
68 72661 : double DateCache::TimeClip(double time) {
69 72661 : if (-kMaxTimeInMs <= time && time <= kMaxTimeInMs) {
70 61303 : return DoubleToInteger(time) + 0.0;
71 : }
72 : return std::numeric_limits<double>::quiet_NaN();
73 : }
74 :
75 : void DateCache::ClearSegment(DST* segment) {
76 2014446 : segment->start_sec = kMaxEpochTimeInSec;
77 2014446 : segment->end_sec = -kMaxEpochTimeInSec;
78 2014446 : segment->offset_ms = 0;
79 2014446 : segment->last_used = 0;
80 : }
81 :
82 :
83 85940 : void DateCache::YearMonthDayFromDays(
84 : int days, int* year, int* month, int* day) {
85 85940 : if (ymd_valid_) {
86 : // Check conservatively if the given 'days' has
87 : // the same year and month as the cached 'days'.
88 85758 : int new_day = ymd_day_ + (days - ymd_days_);
89 85758 : if (new_day >= 1 && new_day <= 28) {
90 65758 : ymd_day_ = new_day;
91 65758 : ymd_days_ = days;
92 65758 : *year = ymd_year_;
93 65758 : *month = ymd_month_;
94 65758 : *day = new_day;
95 65758 : return;
96 : }
97 : }
98 : int save_days = days;
99 :
100 20182 : days += kDaysOffset;
101 20182 : *year = 400 * (days / kDaysIn400Years) - kYearsOffset;
102 20182 : days %= kDaysIn400Years;
103 :
104 : DCHECK_EQ(save_days, DaysFromYearMonth(*year, 0) + days);
105 :
106 20182 : days--;
107 20182 : int yd1 = days / kDaysIn100Years;
108 20182 : days %= kDaysIn100Years;
109 20182 : *year += 100 * yd1;
110 :
111 20182 : days++;
112 20182 : int yd2 = days / kDaysIn4Years;
113 20182 : days %= kDaysIn4Years;
114 20182 : *year += 4 * yd2;
115 :
116 20182 : days--;
117 20182 : int yd3 = days / 365;
118 20182 : days %= 365;
119 20182 : *year += yd3;
120 :
121 :
122 20182 : bool is_leap = (!yd1 || yd2) && !yd3;
123 :
124 : DCHECK_GE(days, -1);
125 : DCHECK(is_leap || (days >= 0));
126 : DCHECK((days < 365) || (is_leap && (days < 366)));
127 : DCHECK(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0))));
128 : DCHECK(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days));
129 : DCHECK(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days));
130 :
131 20182 : days += is_leap;
132 :
133 : // Check if the date is after February.
134 20182 : if (days >= 31 + 28 + BoolToInt(is_leap)) {
135 17126 : days -= 31 + 28 + BoolToInt(is_leap);
136 : // Find the date starting from March.
137 172108 : for (int i = 2; i < 12; i++) {
138 94617 : if (days < kDaysInMonths[i]) {
139 17126 : *month = i;
140 17126 : *day = days + 1;
141 17126 : break;
142 : }
143 77491 : days -= kDaysInMonths[i];
144 : }
145 : } else {
146 : // Check January and February.
147 3056 : if (days < 31) {
148 1908 : *month = 0;
149 1908 : *day = days + 1;
150 : } else {
151 1148 : *month = 1;
152 1148 : *day = days - 31 + 1;
153 : }
154 : }
155 : DCHECK(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
156 20182 : ymd_valid_ = true;
157 20182 : ymd_year_ = *year;
158 20182 : ymd_month_ = *month;
159 20182 : ymd_day_ = *day;
160 20182 : ymd_days_ = save_days;
161 : }
162 :
163 :
164 52107 : int DateCache::DaysFromYearMonth(int year, int month) {
165 : static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
166 : 181, 212, 243, 273, 304, 334};
167 : static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
168 : 182, 213, 244, 274, 305, 335};
169 :
170 52107 : year += month / 12;
171 52107 : month %= 12;
172 52107 : if (month < 0) {
173 0 : year--;
174 0 : month += 12;
175 : }
176 :
177 : DCHECK_GE(month, 0);
178 : DCHECK_LT(month, 12);
179 :
180 : // year_delta is an arbitrary number such that:
181 : // a) year_delta = -1 (mod 400)
182 : // b) year + year_delta > 0 for years in the range defined by
183 : // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
184 : // Jan 1 1970. This is required so that we don't run into integer
185 : // division of negative numbers.
186 : // c) there shouldn't be an overflow for 32-bit integers in the following
187 : // operations.
188 : static const int year_delta = 399999;
189 : static const int base_day = 365 * (1970 + year_delta) +
190 : (1970 + year_delta) / 4 -
191 : (1970 + year_delta) / 100 +
192 : (1970 + year_delta) / 400;
193 :
194 52107 : int year1 = year + year_delta;
195 104214 : int day_from_year = 365 * year1 +
196 104214 : year1 / 4 -
197 104214 : year1 / 100 +
198 52107 : year1 / 400 -
199 52107 : base_day;
200 :
201 52107 : if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
202 38805 : return day_from_year + day_from_month[month];
203 : }
204 13302 : return day_from_year + day_from_month_leap[month];
205 : }
206 :
207 :
208 53822 : void DateCache::BreakDownTime(int64_t time_ms, int* year, int* month, int* day,
209 : int* weekday, int* hour, int* min, int* sec,
210 : int* ms) {
211 : int const days = DaysFromTime(time_ms);
212 : int const time_in_day_ms = TimeInDay(time_ms, days);
213 53822 : YearMonthDayFromDays(days, year, month, day);
214 53822 : *weekday = Weekday(days);
215 53822 : *hour = time_in_day_ms / (60 * 60 * 1000);
216 53822 : *min = (time_in_day_ms / (60 * 1000)) % 60;
217 53822 : *sec = (time_in_day_ms / 1000) % 60;
218 53822 : *ms = time_in_day_ms % 1000;
219 53822 : }
220 :
221 : // Implements LocalTimeZonedjustment(t, isUTC)
222 : // ECMA 262 - ES#sec-local-time-zone-adjustment
223 117028 : int DateCache::GetLocalOffsetFromOS(int64_t time_ms, bool is_utc) {
224 : double offset;
225 : #ifdef V8_INTL_SUPPORT
226 117028 : if (FLAG_icu_timezone_data) {
227 116839 : offset = tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
228 : } else {
229 : #endif
230 : // When ICU timezone data is not used, we need to compute the timezone
231 : // offset for a given local time.
232 : //
233 : // The following shows that using DST for (t - LocalTZA - hour) produces
234 : // correct conversion where LocalTZA is the timezone offset in winter (no
235 : // DST) and the timezone offset is assumed to have no historical change.
236 : // Note that it does not work for the past and the future if LocalTZA (no
237 : // DST) is different from the current LocalTZA (no DST). For instance,
238 : // this will break for Europe/Moscow in 2012 ~ 2013 because LocalTZA was
239 : // 4h instead of the current 3h (as of 2018).
240 : //
241 : // Consider transition to DST at local time L1.
242 : // Let L0 = L1 - hour, L2 = L1 + hour,
243 : // U1 = UTC time that corresponds to L1,
244 : // U0 = U1 - hour.
245 : // Transitioning to DST moves local clock one hour forward L1 => L2, so
246 : // U0 = UTC time that corresponds to L0 = L0 - LocalTZA,
247 : // U1 = UTC time that corresponds to L1 = L1 - LocalTZA,
248 : // U1 = UTC time that corresponds to L2 = L2 - LocalTZA - hour.
249 : // Note that DST(U0 - hour) = 0, DST(U0) = 0, DST(U1) = 1.
250 : // U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour),
251 : // U1 = L1 - LocalTZA - DST(L1 - LocalTZA - hour),
252 : // U1 = L2 - LocalTZA - DST(L2 - LocalTZA - hour).
253 : //
254 : // Consider transition from DST at local time L1.
255 : // Let L0 = L1 - hour,
256 : // U1 = UTC time that corresponds to L1,
257 : // U0 = U1 - hour, U2 = U1 + hour.
258 : // Transitioning from DST moves local clock one hour back L1 => L0, so
259 : // U0 = UTC time that corresponds to L0 (before transition)
260 : // = L0 - LocalTZA - hour.
261 : // U1 = UTC time that corresponds to L0 (after transition)
262 : // = L0 - LocalTZA = L1 - LocalTZA - hour
263 : // U2 = UTC time that corresponds to L1 = L1 - LocalTZA.
264 : // Note that DST(U0) = 1, DST(U1) = 0, DST(U2) = 0.
265 : // U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour) = L0 - LocalTZA - DST(U0).
266 : // U2 = L1 - LocalTZA - DST(L1 - LocalTZA - hour) = L1 - LocalTZA - DST(U1).
267 : // It is impossible to get U1 from local time.
268 189 : if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
269 : // This gets the constant LocalTZA (arguments are ignored).
270 : local_offset_ms_ =
271 10 : tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
272 : }
273 189 : offset = local_offset_ms_;
274 189 : if (!is_utc) {
275 : const int kMsPerHour = 3600 * 1000;
276 171 : time_ms -= (offset + kMsPerHour);
277 : }
278 189 : offset += DaylightSavingsOffsetInMs(time_ms);
279 : #ifdef V8_INTL_SUPPORT
280 : }
281 : #endif
282 : DCHECK_LT(offset, kInvalidLocalOffsetInMs);
283 117028 : return static_cast<int>(offset);
284 : }
285 :
286 13910 : void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
287 19462 : if (after_->offset_ms == offset_ms &&
288 5602 : after_->start_sec - kDefaultDSTDeltaInSec <= time_sec &&
289 50 : time_sec <= after_->end_sec) {
290 : // Extend the after_ segment.
291 36 : after_->start_sec = time_sec;
292 : } else {
293 : // The after_ segment is either invalid or starts too late.
294 13874 : if (!InvalidSegment(after_)) {
295 : // If the after_ segment is valid, replace it with a new segment.
296 6330 : after_ = LeastRecentlyUsedDST(before_);
297 : }
298 13874 : after_->start_sec = time_sec;
299 13874 : after_->end_sec = time_sec;
300 13874 : after_->offset_ms = offset_ms;
301 13874 : after_->last_used = ++dst_usage_counter_;
302 : }
303 13910 : }
304 :
305 :
306 53757 : int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) {
307 53757 : int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
308 53757 : ? static_cast<int>(time_ms / 1000)
309 107514 : : static_cast<int>(EquivalentTime(time_ms) / 1000);
310 :
311 : // Invalidate cache if the usage counter is close to overflow.
312 : // Note that dst_usage_counter is incremented less than ten times
313 : // in this function.
314 53757 : if (dst_usage_counter_ >= kMaxInt - 10) {
315 0 : dst_usage_counter_ = 0;
316 0 : for (int i = 0; i < kDSTSize; ++i) {
317 : ClearSegment(&dst_[i]);
318 : }
319 : }
320 :
321 : // Optimistic fast check.
322 107230 : if (before_->start_sec <= time_sec &&
323 53473 : time_sec <= before_->end_sec) {
324 : // Cache hit.
325 37525 : before_->last_used = ++dst_usage_counter_;
326 37525 : return before_->offset_ms;
327 : }
328 :
329 16232 : ProbeDST(time_sec);
330 :
331 : DCHECK(InvalidSegment(before_) || before_->start_sec <= time_sec);
332 : DCHECK(InvalidSegment(after_) || time_sec < after_->start_sec);
333 :
334 16232 : if (InvalidSegment(before_)) {
335 : // Cache miss.
336 162 : before_->start_sec = time_sec;
337 162 : before_->end_sec = time_sec;
338 162 : before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
339 162 : before_->last_used = ++dst_usage_counter_;
340 162 : return before_->offset_ms;
341 : }
342 :
343 16070 : if (time_sec <= before_->end_sec) {
344 : // Cache hit.
345 974 : before_->last_used = ++dst_usage_counter_;
346 974 : return before_->offset_ms;
347 : }
348 :
349 15096 : if (time_sec - kDefaultDSTDeltaInSec > before_->end_sec) {
350 : // If the before_ segment ends too early, then just
351 : // query for the offset of the time_sec
352 133 : int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
353 133 : ExtendTheAfterSegment(time_sec, offset_ms);
354 : // This swap helps the optimistic fast check in subsequent invocations.
355 133 : DST* temp = before_;
356 133 : before_ = after_;
357 133 : after_ = temp;
358 133 : return offset_ms;
359 : }
360 :
361 : // Now the time_sec is between
362 : // before_->end_sec and before_->end_sec + default DST delta.
363 : // Update the usage counter of before_ since it is going to be used.
364 14963 : before_->last_used = ++dst_usage_counter_;
365 :
366 : // Check if after_ segment is invalid or starts too late.
367 : // Note that start_sec of invalid segments is kMaxEpochTimeInSec.
368 : int new_after_start_sec =
369 14963 : before_->end_sec < kMaxEpochTimeInSec - kDefaultDSTDeltaInSec
370 : ? before_->end_sec + kDefaultDSTDeltaInSec
371 14963 : : kMaxEpochTimeInSec;
372 14963 : if (new_after_start_sec <= after_->start_sec) {
373 13777 : int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec);
374 13777 : ExtendTheAfterSegment(new_after_start_sec, new_offset_ms);
375 : } else {
376 : DCHECK(!InvalidSegment(after_));
377 : // Update the usage counter of after_ since it is going to be used.
378 1186 : after_->last_used = ++dst_usage_counter_;
379 : }
380 :
381 : // Now the time_sec is between before_->end_sec and after_->start_sec.
382 : // Only one daylight savings offset change can occur in this interval.
383 :
384 14963 : if (before_->offset_ms == after_->offset_ms) {
385 : // Merge two segments if they have the same offset.
386 12327 : before_->end_sec = after_->end_sec;
387 12327 : ClearSegment(after_);
388 12327 : return before_->offset_ms;
389 : }
390 :
391 : // Binary search for daylight savings offset change point,
392 : // but give up if we don't find it in five iterations.
393 7848 : for (int i = 4; i >= 0; --i) {
394 5242 : int delta = after_->start_sec - before_->end_sec;
395 5242 : int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2;
396 5242 : int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec);
397 5242 : if (before_->offset_ms == offset_ms) {
398 2557 : before_->end_sec = middle_sec;
399 2557 : if (time_sec <= before_->end_sec) {
400 : return offset_ms;
401 : }
402 : } else {
403 : DCHECK(after_->offset_ms == offset_ms);
404 2685 : after_->start_sec = middle_sec;
405 2685 : if (time_sec >= after_->start_sec) {
406 : // This swap helps the optimistic fast check in subsequent invocations.
407 883 : DST* temp = before_;
408 883 : before_ = after_;
409 883 : after_ = temp;
410 883 : return offset_ms;
411 : }
412 : }
413 : }
414 : return 0;
415 : }
416 :
417 :
418 16232 : void DateCache::ProbeDST(int time_sec) {
419 : DST* before = nullptr;
420 : DST* after = nullptr;
421 : DCHECK(before_ != after_);
422 :
423 1055080 : for (int i = 0; i < kDSTSize; ++i) {
424 519424 : if (dst_[i].start_sec <= time_sec) {
425 396000 : if (before == nullptr || before->start_sec < dst_[i].start_sec) {
426 156188 : before = &dst_[i];
427 : }
428 123424 : } else if (time_sec < dst_[i].end_sec) {
429 74980 : if (after == nullptr || after->end_sec > dst_[i].end_sec) {
430 12090 : after = &dst_[i];
431 : }
432 : }
433 : }
434 :
435 : // If before or after segments were not found,
436 : // then set them to any invalid segment.
437 16232 : if (before == nullptr) {
438 162 : before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after);
439 : }
440 16232 : if (after == nullptr) {
441 21170 : after = InvalidSegment(after_) && before != after_
442 11182 : ? after_ : LeastRecentlyUsedDST(before);
443 : }
444 :
445 : DCHECK_NOT_NULL(before);
446 : DCHECK_NOT_NULL(after);
447 : DCHECK(before != after);
448 : DCHECK(InvalidSegment(before) || before->start_sec <= time_sec);
449 : DCHECK(InvalidSegment(after) || time_sec < after->start_sec);
450 : DCHECK(InvalidSegment(before) || InvalidSegment(after) ||
451 : before->end_sec < after->start_sec);
452 :
453 16232 : before_ = before;
454 16232 : after_ = after;
455 16232 : }
456 :
457 :
458 0 : DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) {
459 : DST* result = nullptr;
460 285415 : for (int i = 0; i < kDSTSize; ++i) {
461 140512 : if (&dst_[i] == skip) continue;
462 136121 : if (result == nullptr || result->last_used > dst_[i].last_used) {
463 : result = &dst_[i];
464 : }
465 : }
466 : ClearSegment(result);
467 0 : return result;
468 : }
469 :
470 : } // namespace internal
471 122004 : } // namespace v8
|