Index: runtime/lib/date_patch.dart |
diff --git a/runtime/lib/date_patch.dart b/runtime/lib/date_patch.dart |
index 6a09226ae620c2fa6fa86c9898e8a4eb8ae72241..48722e10f64af8c8d3f18f11b43c9b54c5ab3161 100644 |
--- a/runtime/lib/date_patch.dart |
+++ b/runtime/lib/date_patch.dart |
@@ -10,15 +10,21 @@ class DateTime { |
// The natives have been moved up here to work around Issue 10401. |
static int _getCurrentMicros() native "DateTime_currentTimeMicros"; |
- static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch) |
+ static String _timeZoneNameForSeconds(int secondsSinceEpoch) |
native "DateTime_timeZoneName"; |
- static int _timeZoneOffsetInSecondsForClampedSeconds(int secondsSinceEpoch) |
+ static int _timeZoneOffsetInSecondsForSeconds(int secondsSinceEpoch) |
native "DateTime_timeZoneOffsetInSeconds"; |
static int _localTimeZoneAdjustmentInSeconds() |
native "DateTime_localTimeZoneAdjustmentInSeconds"; |
+ // Returns `null` if an error occurred. |
+ static int |
+ _brokenDownToSeconds(int year, int month, int day, int hour, int minute, |
+ int second, bool isUtc) |
+ native "DateTime_brokenDownToSeconds"; // Would use mktime or _mkgmtime/timegm |
+ |
static const _MICROSECOND_INDEX = 0; |
static const _MILLISECOND_INDEX = 1; |
static const _SECOND_INDEX = 2; |
@@ -261,155 +267,23 @@ class DateTime { |
@patch |
static int _brokenDownDateToValue(int year, int month, int day, int hour, |
int minute, int second, int millisecond, int microsecond, bool isUtc) { |
- // Simplify calculations by working with zero-based month. |
- --month; |
- // Deal with under and overflow. |
- if (month >= 12) { |
- year += month ~/ 12; |
- month = month % 12; |
- } else if (month < 0) { |
- int realMonth = month % 12; |
- year += (month - realMonth) ~/ 12; |
- month = realMonth; |
- } |
- |
- // First compute the seconds in UTC, independent of the [isUtc] flag. If |
- // necessary we will add the time-zone offset later on. |
- int days = day - 1; |
- days += _DAYS_UNTIL_MONTH[_isLeapYear(year) ? 1 : 0][month]; |
- days += _dayFromYear(year); |
- int microsecondsSinceEpoch = days * Duration.MICROSECONDS_PER_DAY + |
- hour * Duration.MICROSECONDS_PER_HOUR + |
- minute * Duration.MICROSECONDS_PER_MINUTE + |
- second * Duration.MICROSECONDS_PER_SECOND + |
- millisecond * Duration.MICROSECONDS_PER_MILLISECOND + |
- microsecond; |
- |
- // Since [_timeZoneOffsetInSeconds] will crash if the input is far out of |
- // the valid range we do a preliminary test that weeds out values that can |
- // not become valid even with timezone adjustments. |
- // The timezone adjustment is always less than a day, so adding a security |
- // margin of one day should be enough. |
- if (microsecondsSinceEpoch.abs() > |
- _MAX_MILLISECONDS_SINCE_EPOCH * 1000 + Duration.MICROSECONDS_PER_DAY) { |
- return null; |
- } |
- |
- if (!isUtc) { |
- // Note that we need to remove the local timezone adjustment before |
- // asking for the correct zone offset. |
- int adjustment = _localTimeZoneAdjustmentInSeconds() * |
- Duration.MICROSECONDS_PER_SECOND; |
- // The adjustment is independent of the actual date and of the daylight |
- // saving time. It is positive east of the Prime Meridian and negative |
- // west of it, e.g. -28800 sec for America/Los_Angeles timezone. |
- |
- int zoneOffset = |
- _timeZoneOffsetInSeconds(microsecondsSinceEpoch - adjustment); |
- // The zoneOffset depends on the actual date and reflects any daylight |
- // saving time and/or historical deviation relative to UTC time. |
- // It is positive east of the Prime Meridian and negative west of it, |
- // e.g. -25200 sec for America/Los_Angeles timezone during DST. |
- microsecondsSinceEpoch -= zoneOffset * Duration.MICROSECONDS_PER_SECOND; |
- // The resulting microsecondsSinceEpoch value is therefore the calculated |
- // UTC value decreased by a (positive if east of GMT) timezone adjustment |
- // and decreased by typically one hour if DST is in effect. |
- } |
- if (microsecondsSinceEpoch.abs() > |
- _MAX_MILLISECONDS_SINCE_EPOCH * Duration.MICROSECONDS_PER_MILLISECOND) { |
- return null; |
- } |
- return microsecondsSinceEpoch; |
- } |
- |
- static int _weekDay(y) { |
- // 1/1/1970 was a Thursday. |
- return (_dayFromYear(y) + 4) % 7; |
- } |
- |
- /** |
- * Returns a year in the range 2008-2035 matching |
- * * leap year, and |
- * * week day of first day. |
- * |
- * Leap seconds are ignored. |
- * Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9. |
- */ |
- static int _equivalentYear(int year) { |
- // Returns year y so that _weekDay(y) == _weekDay(year). |
- // _weekDay returns the week day (in range 0 - 6). |
- // 1/1/1956 was a Sunday (i.e. weekday 0). 1956 was a leap-year. |
- // 1/1/1967 was a Sunday (i.e. weekday 0). |
- // Without leap years a subsequent year has a week day + 1 (for example |
- // 1/1/1968 was a Monday). With leap-years it jumps over one week day |
- // (e.g. 1/1/1957 was a Tuesday). |
- // After 12 years the weekdays have advanced by 12 days + 3 leap days = |
- // 15 days. 15 % 7 = 1. So after 12 years the week day has always |
- // (now independently of leap-years) advanced by one. |
- // weekDay * 12 gives thus a year starting with the wanted weekDay. |
- int recentYear = (_isLeapYear(year) ? 1956 : 1967) + (_weekDay(year) * 12); |
- // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the |
- // leap years, 7 for the weekdays). |
- // Find the year in the range 2008..2037 that is equivalent mod 28. |
- return 2008 + (recentYear - 2008) % 28; |
- } |
- |
- /** |
- * Returns the UTC year for the corresponding [secondsSinceEpoch]. |
- * It is relatively fast for values in the range 0 to year 2098. |
- * |
- * Code is adapted from V8. |
- */ |
- static int _yearsFromSecondsSinceEpoch(int secondsSinceEpoch) { |
- const int DAYS_IN_4_YEARS = 4 * 365 + 1; |
- const int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; |
- const int DAYS_YEAR_2098 = DAYS_IN_100_YEARS + 6 * DAYS_IN_4_YEARS; |
- |
- int days = secondsSinceEpoch ~/ Duration.SECONDS_PER_DAY; |
- if (days > 0 && days < DAYS_YEAR_2098) { |
- // According to V8 this fast case works for dates from 1970 to 2099. |
- return 1970 + (4 * days + 2) ~/ DAYS_IN_4_YEARS; |
- } |
- int micros = secondsSinceEpoch * Duration.MICROSECONDS_PER_SECOND; |
- return _computeUpperPart(micros)[_YEAR_INDEX]; |
- } |
- |
- /** |
- * Returns a date in seconds that is equivalent to the given |
- * date in microseconds [microsecondsSinceEpoch]. An equivalent |
- * date has the same fields (`month`, `day`, etc.) as the given |
- * date, but the `year` is in the range [1901..2038]. |
- * |
- * * The time since the beginning of the year is the same. |
- * * If the given date is in a leap year then the returned |
- * seconds are in a leap year, too. |
- * * The week day of given date is the same as the one for the |
- * returned date. |
- */ |
- static int _equivalentSeconds(int microsecondsSinceEpoch) { |
- const int CUT_OFF_SECONDS = 0x7FFFFFFF; |
- |
- int secondsSinceEpoch = _flooredDivision( |
- microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); |
- |
- if (secondsSinceEpoch.abs() > CUT_OFF_SECONDS) { |
- int year = _yearsFromSecondsSinceEpoch(secondsSinceEpoch); |
- int days = _dayFromYear(year); |
- int equivalentYear = _equivalentYear(year); |
- int equivalentDays = _dayFromYear(equivalentYear); |
- int diffDays = equivalentDays - days; |
- secondsSinceEpoch += diffDays * Duration.SECONDS_PER_DAY; |
- } |
- return secondsSinceEpoch; |
+ var seconds = |
+ _brokenDownToSeconds(year, month - 1, day, hour, minute, second, isUtc); |
+ if (seconds == null) return null; |
+ return seconds * Duration.MICROSECONDS_PER_SECOND + |
+ milliseconds * Duration.MICROSECONDS_PER_MILLISECONDS + |
+ microseconds; |
} |
static int _timeZoneOffsetInSeconds(int microsecondsSinceEpoch) { |
- int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); |
- return _timeZoneOffsetInSecondsForClampedSeconds(equivalentSeconds); |
+ int seconds = _flooredDivision( |
+ microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); |
+ return _timeZoneOffsetInSecondsForSeconds(seconds); |
} |
static String _timeZoneName(int microsecondsSinceEpoch) { |
- int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); |
- return _timeZoneNameForClampedSeconds(equivalentSeconds); |
+ int seconds = _flooredDivision( |
+ microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); |
+ return _timeZoneNameForSeconds(seconds); |
} |
} |