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