Index: src/date.cc |
diff --git a/src/date.cc b/src/date.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a377451770fb7d7c63821f6143a4eb30b4a5a072 |
--- /dev/null |
+++ b/src/date.cc |
@@ -0,0 +1,384 @@ |
+// Copyright 2012 the V8 project authors. All rights reserved. |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following |
+// disclaimer in the documentation and/or other materials provided |
+// with the distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived |
+// from this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+#include "date.h" |
+ |
+#include "v8.h" |
+ |
+#include "objects.h" |
+#include "objects-inl.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+ |
+static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1}; |
+static const int kDaysIn4Years = 4 * 365 + 1; |
+static const int kDaysIn100Years = 25 * kDaysIn4Years - 1; |
+static const int kDaysIn400Years = 4 * kDaysIn100Years + 1; |
+static const int kDays1970to2000 = 30 * 365 + 7; |
+static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years - |
+ kDays1970to2000; |
+static const int kYearsOffset = 400000; |
+static const char kDaysInMonths[] = |
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
+ |
+ |
+void DateCache::ResetDateCache() { |
+ static const int kMaxStamp = Smi::kMaxValue; |
+ stamp_ = Smi::FromInt(stamp_->value() + 1); |
+ if (stamp_->value() > kMaxStamp) { |
+ stamp_ = Smi::FromInt(0); |
+ } |
+ ASSERT(stamp_ != Smi::FromInt(kInvalidStamp)); |
+ for (int i = 0; i < kDSTSize; ++i) { |
+ ClearSegment(&dst_[i]); |
+ } |
+ dst_usage_counter_ = 0; |
+ before_ = &dst_[0]; |
+ after_ = &dst_[1]; |
+ local_offset_ms_ = kInvalidLocalOffsetInMs; |
+ ymd_valid_ = false; |
+} |
+ |
+ |
+void DateCache::ClearSegment(DST* segment) { |
+ segment->start_sec = kMaxEpochTimeInSec; |
+ segment->end_sec = -kMaxEpochTimeInSec; |
+ segment->offset_ms = 0; |
+ segment->last_used = 0; |
+} |
+ |
+ |
+void DateCache::YearMonthDayFromDays( |
+ int days, int* year, int* month, int* day) { |
+ if (ymd_valid_) { |
+ // Check conservatively if the given 'days' has |
+ // the same year and month as the cached 'days'. |
+ int new_day = ymd_day_ + (days - ymd_days_); |
+ if (new_day >= 1 && new_day <= 28) { |
+ ymd_day_ = new_day; |
+ ymd_days_ = days; |
+ *year = ymd_year_; |
+ *month = ymd_month_; |
+ *day = new_day; |
+ return; |
+ } |
+ } |
+ int save_days = days; |
+ |
+ days += kDaysOffset; |
+ *year = 400 * (days / kDaysIn400Years) - kYearsOffset; |
+ days %= kDaysIn400Years; |
+ |
+ ASSERT(DaysFromYearMonth(*year, 0) + days == save_days); |
+ |
+ days--; |
+ int yd1 = days / kDaysIn100Years; |
+ days %= kDaysIn100Years; |
+ *year += 100 * yd1; |
+ |
+ days++; |
+ int yd2 = days / kDaysIn4Years; |
+ days %= kDaysIn4Years; |
+ *year += 4 * yd2; |
+ |
+ days--; |
+ int yd3 = days / 365; |
+ days %= 365; |
+ *year += yd3; |
+ |
+ |
+ bool is_leap = (!yd1 || yd2) && !yd3; |
+ |
+ ASSERT(days >= -1); |
+ ASSERT(is_leap || (days >= 0)); |
+ ASSERT((days < 365) || (is_leap && (days < 366))); |
+ ASSERT(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0)))); |
+ ASSERT(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days)); |
+ ASSERT(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days)); |
+ |
+ days += is_leap; |
+ |
+ // Check if the date is after February. |
+ if (days >= 31 + 28 + is_leap) { |
+ days -= 31 + 28 + is_leap; |
+ // Find the date starting from March. |
+ for (int i = 2; i < 12; i++) { |
+ if (days < kDaysInMonths[i]) { |
+ *month = i; |
+ *day = days + 1; |
+ break; |
+ } |
+ days -= kDaysInMonths[i]; |
+ } |
+ } else { |
+ // Check January and February. |
+ if (days < 31) { |
+ *month = 0; |
+ *day = days + 1; |
+ } else { |
+ *month = 1; |
+ *day = days - 31 + 1; |
+ } |
+ } |
+ ASSERT(DaysFromYearMonth(*year, *month) + *day - 1 == save_days); |
+ ymd_valid_ = true; |
+ ymd_year_ = *year; |
+ ymd_month_ = *month; |
+ ymd_day_ = *day; |
+ ymd_days_ = save_days; |
+} |
+ |
+ |
+int DateCache::DaysFromYearMonth(int year, int month) { |
+ static const int day_from_month[] = {0, 31, 59, 90, 120, 151, |
+ 181, 212, 243, 273, 304, 334}; |
+ static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152, |
+ 182, 213, 244, 274, 305, 335}; |
+ |
+ year += month / 12; |
+ month %= 12; |
+ if (month < 0) { |
+ year--; |
+ month += 12; |
+ } |
+ |
+ ASSERT(month >= 0); |
+ ASSERT(month < 12); |
+ |
+ // year_delta is an arbitrary number such that: |
+ // a) year_delta = -1 (mod 400) |
+ // b) year + year_delta > 0 for years in the range defined by |
+ // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of |
+ // Jan 1 1970. This is required so that we don't run into integer |
+ // division of negative numbers. |
+ // c) there shouldn't be an overflow for 32-bit integers in the following |
+ // operations. |
+ static const int year_delta = 399999; |
+ static const int base_day = 365 * (1970 + year_delta) + |
+ (1970 + year_delta) / 4 - |
+ (1970 + year_delta) / 100 + |
+ (1970 + year_delta) / 400; |
+ |
+ int year1 = year + year_delta; |
+ int day_from_year = 365 * year1 + |
+ year1 / 4 - |
+ year1 / 100 + |
+ year1 / 400 - |
+ base_day; |
+ |
+ if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) { |
+ return day_from_year + day_from_month[month]; |
+ } |
+ return day_from_year + day_from_month_leap[month]; |
+} |
+ |
+ |
+void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) { |
+ if (after_->offset_ms == offset_ms && |
+ after_->start_sec <= time_sec + kDefaultDSTDeltaInSec && |
+ time_sec <= after_->end_sec) { |
+ // Extend the after_ segment. |
+ after_->start_sec = time_sec; |
+ } else { |
+ // The after_ segment is either invalid or starts too late. |
+ if (after_->start_sec <= after_->end_sec) { |
+ // If the after_ segment is valid, replace it with a new segment. |
+ after_ = LeastRecentlyUsedDST(before_); |
+ } |
+ after_->start_sec = time_sec; |
+ after_->end_sec = time_sec; |
+ after_->offset_ms = offset_ms; |
+ after_->last_used = ++dst_usage_counter_; |
+ } |
+} |
+ |
+ |
+int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) { |
+ int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs) |
+ ? static_cast<int>(time_ms / 1000) |
+ : static_cast<int>(EquivalentTime(time_ms) / 1000); |
+ |
+ // Invalidate cache if the usage counter is close to overflow. |
+ // Note that dst_usage_counter is incremented less than ten times |
+ // in this function. |
+ if (dst_usage_counter_ >= kMaxInt - 10) { |
+ dst_usage_counter_ = 0; |
+ for (int i = 0; i < kDSTSize; ++i) { |
+ ClearSegment(&dst_[i]); |
+ } |
+ } |
+ |
+ // Optimistic fast check. |
+ if (before_->start_sec <= time_sec && |
+ time_sec <= before_->end_sec) { |
+ // Cache hit. |
+ before_->last_used = ++dst_usage_counter_; |
+ return before_->offset_ms; |
+ } |
+ |
+ ProbeDST(time_sec); |
+ |
+ ASSERT(InvalidSegment(before_) || before_->start_sec <= time_sec); |
+ ASSERT(InvalidSegment(after_) || time_sec < after_->start_sec); |
+ |
+ if (InvalidSegment(before_)) { |
+ // Cache miss. |
+ before_->start_sec = time_sec; |
+ before_->end_sec = time_sec; |
+ before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
+ before_->last_used = ++dst_usage_counter_; |
+ return before_->offset_ms; |
+ } |
+ |
+ if (time_sec <= before_->end_sec) { |
+ // Cache hit. |
+ before_->last_used = ++dst_usage_counter_; |
+ return before_->offset_ms; |
+ } |
+ |
+ if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) { |
+ // If the before_ segment ends too early, then just |
+ // query for the offset of the time_sec |
+ int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
+ ExtendTheAfterSegment(time_sec, offset_ms); |
+ // This swap helps the optimistic fast check in subsequent invocations. |
+ DST* temp = before_; |
+ before_ = after_; |
+ after_ = temp; |
+ return offset_ms; |
+ } |
+ |
+ // Now the time_sec is between |
+ // before_->end_sec and before_->end_sec + default DST delta. |
+ // Update the usage counter of before_ since it is going to be used. |
+ before_->last_used = ++dst_usage_counter_; |
+ |
+ // Check if after_ segment is invalid or starts too late. |
+ // Note that start_sec of invalid segments is kMaxEpochTimeInSec. |
+ if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) { |
+ int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec; |
+ int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec); |
+ ExtendTheAfterSegment(new_after_start_sec, new_offset_ms); |
+ } else { |
+ ASSERT(!InvalidSegment(after_)); |
+ // Update the usage counter of after_ since it is going to be used. |
+ after_->last_used = ++dst_usage_counter_; |
+ } |
+ |
+ // Now the time_sec is between before_->end_sec and after_->start_sec. |
+ // Only one daylight savings offset change can occur in this interval. |
+ |
+ if (before_->offset_ms == after_->offset_ms) { |
+ // Merge two segments if they have the same offset. |
+ before_->end_sec = after_->end_sec; |
+ ClearSegment(after_); |
+ return before_->offset_ms; |
+ } |
+ |
+ // Binary search for daylight savings offset change point, |
+ // but give up if we don't find it in four iterations. |
+ for (int i = 4; i >= 0; --i) { |
+ int delta = after_->start_sec - before_->end_sec; |
+ int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2; |
+ int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec); |
+ if (before_->offset_ms == offset_ms) { |
+ before_->end_sec = middle_sec; |
+ if (time_sec <= before_->end_sec) { |
+ return offset_ms; |
+ } |
+ } else { |
+ ASSERT(after_->offset_ms == offset_ms); |
+ after_->start_sec = middle_sec; |
+ if (time_sec >= after_->start_sec) { |
+ // This swap helps the optimistic fast check in subsequent invocations. |
+ DST* temp = before_; |
+ before_ = after_; |
+ after_ = temp; |
+ return offset_ms; |
+ } |
+ } |
+ } |
+ UNREACHABLE(); |
+ return 0; |
+} |
+ |
+ |
+void DateCache::ProbeDST(int time_sec) { |
+ DST* before = NULL; |
+ DST* after = NULL; |
+ ASSERT(before_ != after_); |
+ |
+ for (int i = 0; i < kDSTSize; ++i) { |
+ if (dst_[i].start_sec <= time_sec) { |
+ if (before == NULL || before->start_sec < dst_[i].start_sec) { |
+ before = &dst_[i]; |
+ } |
+ } else if (time_sec < dst_[i].end_sec) { |
+ if (after == NULL || after->end_sec > dst_[i].end_sec) { |
+ after = &dst_[i]; |
+ } |
+ } |
+ } |
+ |
+ // If before or after segments were not found, |
+ // then set them to any invalid segment. |
+ if (before == NULL) { |
+ before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after); |
+ } |
+ if (after == NULL) { |
+ after = InvalidSegment(after_) && before != after_ |
+ ? after_ : LeastRecentlyUsedDST(before); |
+ } |
+ |
+ ASSERT(before != NULL); |
+ ASSERT(after != NULL); |
+ ASSERT(before != after); |
+ ASSERT(InvalidSegment(before) || before->start_sec <= time_sec); |
+ ASSERT(InvalidSegment(after) || time_sec < after->start_sec); |
+ ASSERT(InvalidSegment(before) || InvalidSegment(after) || |
+ before->end_sec < after->start_sec); |
+ |
+ before_ = before; |
+ after_ = after; |
+} |
+ |
+ |
+DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) { |
+ DST* result = NULL; |
+ for (int i = 0; i < kDSTSize; ++i) { |
+ if (&dst_[i] == skip) continue; |
+ if (result == NULL || result->last_used > dst_[i].last_used) { |
+ result = &dst_[i]; |
+ } |
+ } |
+ ClearSegment(result); |
+ return result; |
+} |
+ |
+} } // namespace v8::internal |