• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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