LCOV - code coverage report
Current view: top level - src - date.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 166 175 94.9 %
Date: 2017-04-26 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       60789 : 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       60789 :               ) {
      38       60789 :   ResetDateCache();
      39       60789 : }
      40             : 
      41       60796 : void DateCache::ResetDateCache() {
      42             :   static const int kMaxStamp = Smi::kMaxValue;
      43      121592 :   if (stamp_->value() >= kMaxStamp) {
      44           0 :     stamp_ = Smi::kZero;
      45             :   } else {
      46      121592 :     stamp_ = Smi::FromInt(stamp_->value() + 1);
      47             :   }
      48             :   DCHECK(stamp_ != Smi::FromInt(kInvalidStamp));
      49     1945472 :   for (int i = 0; i < kDSTSize; ++i) {
      50     1945472 :     ClearSegment(&dst_[i]);
      51             :   }
      52       60796 :   dst_usage_counter_ = 0;
      53       60796 :   before_ = &dst_[0];
      54       60796 :   after_ = &dst_[1];
      55       60796 :   local_offset_ms_ = kInvalidLocalOffsetInMs;
      56       60796 :   ymd_valid_ = false;
      57       60796 :   tz_cache_->Clear();
      58       60796 :   tz_name_ = nullptr;
      59       60796 :   dst_tz_name_ = nullptr;
      60       60796 : }
      61             : 
      62             : 
      63             : void DateCache::ClearSegment(DST* segment) {
      64     1981140 :   segment->start_sec = kMaxEpochTimeInSec;
      65     1981140 :   segment->end_sec = -kMaxEpochTimeInSec;
      66     1981140 :   segment->offset_ms = 0;
      67     1981140 :   segment->last_used = 0;
      68             : }
      69             : 
      70             : 
      71      192427 : void DateCache::YearMonthDayFromDays(
      72             :     int days, int* year, int* month, int* day) {
      73      192427 :   if (ymd_valid_) {
      74             :     // Check conservatively if the given 'days' has
      75             :     // the same year and month as the cached 'days'.
      76      192212 :     int new_day = ymd_day_ + (days - ymd_days_);
      77      192212 :     if (new_day >= 1 && new_day <= 28) {
      78      156783 :       ymd_day_ = new_day;
      79      156783 :       ymd_days_ = days;
      80      156783 :       *year = ymd_year_;
      81      156783 :       *month = ymd_month_;
      82      156783 :       *day = new_day;
      83      349210 :       return;
      84             :     }
      85             :   }
      86             :   int save_days = days;
      87             : 
      88       35644 :   days += kDaysOffset;
      89       35644 :   *year = 400 * (days / kDaysIn400Years) - kYearsOffset;
      90       35644 :   days %= kDaysIn400Years;
      91             : 
      92             :   DCHECK_EQ(save_days, DaysFromYearMonth(*year, 0) + days);
      93             : 
      94       35644 :   days--;
      95       35644 :   int yd1 = days / kDaysIn100Years;
      96       35644 :   days %= kDaysIn100Years;
      97       35644 :   *year += 100 * yd1;
      98             : 
      99       35644 :   days++;
     100       35644 :   int yd2 = days / kDaysIn4Years;
     101       35644 :   days %= kDaysIn4Years;
     102       35644 :   *year += 4 * yd2;
     103             : 
     104       35644 :   days--;
     105       35644 :   int yd3 = days / 365;
     106       35644 :   days %= 365;
     107       35644 :   *year += yd3;
     108             : 
     109             : 
     110       35644 :   bool is_leap = (!yd1 || yd2) && !yd3;
     111             : 
     112             :   DCHECK(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       35644 :   days += is_leap;
     120             : 
     121             :   // Check if the date is after February.
     122       35644 :   if (days >= 31 + 28 + BoolToInt(is_leap)) {
     123       30603 :     days -= 31 + 28 + BoolToInt(is_leap);
     124             :     // Find the date starting from March.
     125      169036 :     for (int i = 2; i < 12; i++) {
     126      169036 :       if (days < kDaysInMonths[i]) {
     127       30603 :         *month = i;
     128       30603 :         *day = days + 1;
     129       30603 :         break;
     130             :       }
     131      138433 :       days -= kDaysInMonths[i];
     132             :     }
     133             :   } else {
     134             :     // Check January and February.
     135        5041 :     if (days < 31) {
     136        3281 :       *month = 0;
     137        3281 :       *day = days + 1;
     138             :     } else {
     139        1760 :       *month = 1;
     140        1760 :       *day = days - 31 + 1;
     141             :     }
     142             :   }
     143             :   DCHECK(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
     144       35644 :   ymd_valid_ = true;
     145       35644 :   ymd_year_ = *year;
     146       35644 :   ymd_month_ = *month;
     147       35644 :   ymd_day_ = *day;
     148       35644 :   ymd_days_ = save_days;
     149             : }
     150             : 
     151             : 
     152      200601 : 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      200601 :   year += month / 12;
     159      200601 :   month %= 12;
     160      200601 :   if (month < 0) {
     161           0 :     year--;
     162           0 :     month += 12;
     163             :   }
     164             : 
     165             :   DCHECK(month >= 0);
     166             :   DCHECK(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      200601 :   int year1 = year + year_delta;
     183      401202 :   int day_from_year = 365 * year1 +
     184      401202 :                       year1 / 4 -
     185      401202 :                       year1 / 100 +
     186      200601 :                       year1 / 400 -
     187      200601 :                       base_day;
     188             : 
     189      200601 :   if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
     190      157097 :     return day_from_year + day_from_month[month];
     191             :   }
     192       43504 :   return day_from_year + day_from_month_leap[month];
     193             : }
     194             : 
     195             : 
     196       89344 : 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       89344 :   YearMonthDayFromDays(days, year, month, day);
     202       89344 :   *weekday = Weekday(days);
     203       89344 :   *hour = time_in_day_ms / (60 * 60 * 1000);
     204       89344 :   *min = (time_in_day_ms / (60 * 1000)) % 60;
     205       89344 :   *sec = (time_in_day_ms / 1000) % 60;
     206       89344 :   *ms = time_in_day_ms % 1000;
     207       89344 : }
     208             : 
     209             : 
     210       24889 : void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
     211       35714 :   if (after_->offset_ms == offset_ms &&
     212       12129 :       after_->start_sec <= time_sec + kDefaultDSTDeltaInSec &&
     213        1304 :       time_sec <= after_->end_sec) {
     214             :     // Extend the after_ segment.
     215        1304 :     after_->start_sec = time_sec;
     216             :   } else {
     217             :     // The after_ segment is either invalid or starts too late.
     218       23585 :     if (after_->start_sec <= after_->end_sec) {
     219             :       // If the after_ segment is valid, replace it with a new segment.
     220       15932 :       after_ = LeastRecentlyUsedDST(before_);
     221             :     }
     222       23585 :     after_->start_sec = time_sec;
     223       23585 :     after_->end_sec = time_sec;
     224       23585 :     after_->offset_ms = offset_ms;
     225       23585 :     after_->last_used = ++dst_usage_counter_;
     226             :   }
     227       24889 : }
     228             : 
     229             : 
     230      287046 : int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) {
     231      287046 :   int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
     232      229635 :       ? static_cast<int>(time_ms / 1000)
     233      516681 :       : 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      287046 :   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      602949 :   if (before_->start_sec <= time_sec &&
     247      281099 :       time_sec <= before_->end_sec) {
     248             :     // Cache hit.
     249      252242 :     before_->last_used = ++dst_usage_counter_;
     250      252242 :     return before_->offset_ms;
     251             :   }
     252             : 
     253       34804 :   ProbeDST(time_sec);
     254             : 
     255             :   DCHECK(InvalidSegment(before_) || before_->start_sec <= time_sec);
     256             :   DCHECK(InvalidSegment(after_) || time_sec < after_->start_sec);
     257             : 
     258       69608 :   if (InvalidSegment(before_)) {
     259             :     // Cache miss.
     260        3618 :     before_->start_sec = time_sec;
     261        3618 :     before_->end_sec = time_sec;
     262        3618 :     before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
     263        3618 :     before_->last_used = ++dst_usage_counter_;
     264        3618 :     return before_->offset_ms;
     265             :   }
     266             : 
     267       31186 :   if (time_sec <= before_->end_sec) {
     268             :     // Cache hit.
     269        2633 :     before_->last_used = ++dst_usage_counter_;
     270        2633 :     return before_->offset_ms;
     271             :   }
     272             : 
     273       28553 :   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        1769 :     int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
     277        1769 :     ExtendTheAfterSegment(time_sec, offset_ms);
     278             :     // This swap helps the optimistic fast check in subsequent invocations.
     279        1769 :     DST* temp = before_;
     280        1769 :     before_ = after_;
     281        1769 :     after_ = temp;
     282        1769 :     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       26784 :   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       26784 :   if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) {
     293       23120 :     int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec;
     294       23120 :     int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec);
     295       23120 :     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        3664 :     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       26784 :   if (before_->offset_ms == after_->offset_ms) {
     306             :     // Merge two segments if they have the same offset.
     307       22295 :     before_->end_sec = after_->end_sec;
     308             :     ClearSegment(after_);
     309       22295 :     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 four iterations.
     314        4425 :   for (int i = 4; i >= 0; --i) {
     315        8914 :     int delta = after_->start_sec - before_->end_sec;
     316        8914 :     int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2;
     317        8914 :     int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec);
     318        8914 :     if (before_->offset_ms == offset_ms) {
     319        4331 :       before_->end_sec = middle_sec;
     320        4331 :       if (time_sec <= before_->end_sec) {
     321             :         return offset_ms;
     322             :       }
     323             :     } else {
     324             :       DCHECK(after_->offset_ms == offset_ms);
     325        4583 :       after_->start_sec = middle_sec;
     326        4583 :       if (time_sec >= after_->start_sec) {
     327             :         // This swap helps the optimistic fast check in subsequent invocations.
     328             :         DST* temp = before_;
     329        1497 :         before_ = after_;
     330        1497 :         after_ = temp;
     331        1497 :         return offset_ms;
     332             :       }
     333             :     }
     334             :   }
     335           0 :   UNREACHABLE();
     336             :   return 0;
     337             : }
     338             : 
     339             : 
     340       34804 : void DateCache::ProbeDST(int time_sec) {
     341             :   DST* before = NULL;
     342             :   DST* after = NULL;
     343             :   DCHECK(before_ != after_);
     344             : 
     345     1148532 :   for (int i = 0; i < kDSTSize; ++i) {
     346     1113728 :     if (dst_[i].start_sec <= time_sec) {
     347      686723 :       if (before == NULL || before->start_sec < dst_[i].start_sec) {
     348      243860 :         before = &dst_[i];
     349             :       }
     350      427005 :     } else if (time_sec < dst_[i].end_sec) {
     351      287476 :       if (after == NULL || after->end_sec > dst_[i].end_sec) {
     352       85254 :         after = &dst_[i];
     353             :       }
     354             :     }
     355             :   }
     356             : 
     357             :   // If before or after segments were not found,
     358             :   // then set them to any invalid segment.
     359       34804 :   if (before == NULL) {
     360        3618 :     before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after);
     361             :   }
     362       34804 :   if (after == NULL) {
     363       31138 :     after = InvalidSegment(after_) && before != after_
     364       16577 :             ? after_ : LeastRecentlyUsedDST(before);
     365             :   }
     366             : 
     367             :   DCHECK(before != NULL);
     368             :   DCHECK(after != NULL);
     369             :   DCHECK(before != after);
     370             :   DCHECK(InvalidSegment(before) || before->start_sec <= time_sec);
     371             :   DCHECK(InvalidSegment(after) || time_sec < after->start_sec);
     372             :   DCHECK(InvalidSegment(before) || InvalidSegment(after) ||
     373             :          before->end_sec < after->start_sec);
     374             : 
     375       34804 :   before_ = before;
     376       34804 :   after_ = after;
     377       34804 : }
     378             : 
     379             : 
     380           0 : DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) {
     381             :   DST* result = NULL;
     382      427936 :   for (int i = 0; i < kDSTSize; ++i) {
     383      427936 :     if (&dst_[i] == skip) continue;
     384      414563 :     if (result == NULL || result->last_used > dst_[i].last_used) {
     385             :       result = &dst_[i];
     386             :     }
     387             :   }
     388             :   ClearSegment(result);
     389           0 :   return result;
     390             : }
     391             : 
     392             : }  // namespace internal
     393             : }  // namespace v8

Generated by: LCOV version 1.10