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