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