LCOV - code coverage report
Current view: top level - src - date.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 168 176 95.5 %
Date: 2017-10-20 Functions: 8 9 88.9 %

          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

Generated by: LCOV version 1.10