diff --git a/corelib/src/date.dart b/corelib/src/date.dart index e1dcabde1bf..0b35247cd98 100644 --- a/corelib/src/date.dart +++ b/corelib/src/date.dart @@ -6,10 +6,6 @@ /** * Date is the public interface to a point in time. - * - * It can represent time values that are at a distance of at most - * 8,640,000,000,000,000ms (100,000,000 days) from epoch (1970-01-01 UTC). In - * other words: [:value.abs() <= 8640000000000000:]. */ interface Date extends Comparable, Hashable default DateImplementation { // Weekday constants that are returned by [weekday] method: @@ -38,7 +34,7 @@ interface Date extends Comparable, Hashable default DateImplementation { /** * Constructs a [Date] instance based on the individual parts. The date is - * in the local time zone if [isUtc] is false. + * in the local time-zone if [isUtc] is false. */ // TODO(floitsch): the spec allows default values in interfaces, but our // tools don't yet. Eventually we want to have default values here. @@ -64,10 +60,10 @@ interface Date extends Comparable, Hashable default DateImplementation { /** * Constructs a new [Date] instance with the given [value]. If [isUtc] is - * false then the date is in the local time zone. + * false then the date is in the local time-zone. * * The constructed [Date] represents 1970-01-01T00:00:00Z + [value]ms in - * the given time zone (local or UTC). + * the given time-zone (local or UTC). */ // TODO(floitsch): the spec allows default values in interfaces, but our // tools don't yet. Eventually we want to have default values here. @@ -104,7 +100,7 @@ interface Date extends Comparable, Hashable default DateImplementation { /** - * Returns [this] in the local time zone. Returns itself if it is already in + * Returns [this] in the local time-zone. Returns itself if it is already in * the local time zone. Otherwise, this method is equivalent to * [:new Date.fromEpoch(this.value, isUtc: false):]. */ diff --git a/lib/compiler/implementation/lib/js_helper.dart b/lib/compiler/implementation/lib/js_helper.dart index f25f41ad1f8..69c3823d601 100644 --- a/lib/compiler/implementation/lib/js_helper.dart +++ b/lib/compiler/implementation/lib/js_helper.dart @@ -489,7 +489,7 @@ class Primitives { value = JS('num', @'new Date(#, #, #, #, #, #, #).valueOf()', years, jsMonth, day, hours, minutes, seconds, milliseconds); } - if (value.isNaN()) throw new IllegalArgumentException(); + if (value.isNaN()) throw new IllegalArgumentException(''); if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); return value; } diff --git a/lib/compiler/implementation/lib/mockimpl.dart b/lib/compiler/implementation/lib/mockimpl.dart index b6f18055e7e..08648b96321 100644 --- a/lib/compiler/implementation/lib/mockimpl.dart +++ b/lib/compiler/implementation/lib/mockimpl.dart @@ -243,12 +243,8 @@ class DateImplementation implements Date { } } - static final int _MAX_VALUE = 8640000000000000; - DateImplementation.fromEpoch(this.value, [bool isUtc = false]) - : _isUtc = checkNull(isUtc) { - if (value.abs() > _MAX_VALUE) throw new IllegalArgumentException(value); - } + : _isUtc = checkNull(isUtc); bool operator ==(other) { if (!(other is DateImplementation)) return false; diff --git a/runtime/lib/date.cc b/runtime/lib/date.cc index bd28ee419eb..0c49e5b3fa0 100644 --- a/runtime/lib/date.cc +++ b/runtime/lib/date.cc @@ -13,17 +13,140 @@ namespace dart { -static int32_t kMaxAllowedSeconds = 2100000000; +typedef struct BrokenDownDate { + intptr_t year; + intptr_t month; // [1..12] + intptr_t day; // [1..31] + intptr_t hours; + intptr_t minutes; + intptr_t seconds; +} BrokenDownDate; + + +// Takes the seconds since epoch (midnight, January 1, 1970 UTC) and breaks it +// down into date and time. +// If 'dart_is_utc', then the broken down date and time are in the UTC timezone, +// otherwise the local timezone is used. +// The returned year is offset by 1900. The returned month is 0-based. +// Returns true if the conversion succeeds, false otherwise. +static bool BreakDownSecondsSinceEpoch(const Integer& dart_seconds, + const Bool& dart_is_utc, + BrokenDownDate* result) { + // Always fill the result to avoid unitialized use warnings. + result->year = 0; + result->month = 0; + result->day = 0; + result->hours = 0; + result->minutes = 0; + result->seconds = 0; + + bool is_utc = dart_is_utc.value(); + int64_t seconds = dart_seconds.AsInt64Value(); + + struct tm tm_result; + bool succeeded; + if (is_utc) { + succeeded = OS::GmTime(seconds, &tm_result); + } else { + succeeded = OS::LocalTime(seconds, &tm_result); + } + if (succeeded) { + result->year = tm_result.tm_year; + // C uses years since 1900, and not full years. + // Adding 1900 could overflow the intptr_t. + if (result->year > kIntptrMax - 1900) return false; + result->year += 1900; + // Dart has 1-based months (contrary to C's 0-based). + result->month= tm_result.tm_mon + 1; + result->day = tm_result.tm_mday; + result->hours = tm_result.tm_hour; + result->minutes = tm_result.tm_min; + result->seconds = tm_result.tm_sec; + } + return succeeded; +} + + +static bool BrokenDownToSecondsSinceEpoch(const BrokenDownDate& broken_down, + bool in_utc, + int64_t* result) { + // Always set the result to avoid unitialized use warnings. + *result = 0; + + struct tm tm_broken_down; + intptr_t year = broken_down.year; + // C works with years since 1900. + // Removing 1900 could underflow the intptr_t. + if (year < kIntptrMin + 1900) return false; + year -= 1900; + intptr_t month = broken_down.month; + // C works with 0-based months. + // Avoid underflows (even though they should not matter since the date would + // be invalid anyways. + if (month < 0) return false; + month--; + tm_broken_down.tm_year = static_cast(year); + tm_broken_down.tm_mon = static_cast(month); + tm_broken_down.tm_mday = static_cast(broken_down.day); + tm_broken_down.tm_hour = static_cast(broken_down.hours); + tm_broken_down.tm_min = static_cast(broken_down.minutes); + tm_broken_down.tm_sec = static_cast(broken_down.seconds); + // Verify that casting to int did not change the value. + if (tm_broken_down.tm_year != year + || tm_broken_down.tm_mon != month + || tm_broken_down.tm_mday != broken_down.day + || tm_broken_down.tm_hour != broken_down.hours + || tm_broken_down.tm_min != broken_down.minutes + || tm_broken_down.tm_sec != broken_down.seconds) { + return false; + } + if (in_utc) { + return OS::MkGmTime(&tm_broken_down, result); + } else { + return OS::MkTime(&tm_broken_down, result); + } +} + + +DEFINE_NATIVE_ENTRY(DateNatives_brokenDownToSecondsSinceEpoch, 7) { + GET_NATIVE_ARGUMENT(Integer, dart_years, arguments->At(0)); + GET_NATIVE_ARGUMENT(Smi, dart_month, arguments->At(1)); + GET_NATIVE_ARGUMENT(Smi, dart_day, arguments->At(2)); + GET_NATIVE_ARGUMENT(Smi, dart_hours, arguments->At(3)); + GET_NATIVE_ARGUMENT(Smi, dart_minutes, arguments->At(4)); + GET_NATIVE_ARGUMENT(Smi, dart_seconds, arguments->At(5)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(6)); + if (!dart_years.IsSmi()) { + UNIMPLEMENTED(); + } + Smi& smi_years = Smi::Handle(); + smi_years ^= dart_years.raw(); + BrokenDownDate broken_down; + broken_down.year = smi_years.Value(); + broken_down.month = dart_month.Value(); + broken_down.day = dart_day.Value(); + broken_down.hours = dart_hours.Value(); + broken_down.minutes = dart_minutes.Value(); + broken_down.seconds = dart_seconds.Value(); + int64_t value; + bool succeeded = BrokenDownToSecondsSinceEpoch(broken_down, + dart_is_utc.value(), + &value); + if (!succeeded) { + UNIMPLEMENTED(); + } + arguments->SetReturn(Integer::Handle(Integer::New(value))); +} + DEFINE_NATIVE_ENTRY(DateNatives_timeZoneName, 1) { GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); int64_t seconds = dart_seconds.AsInt64Value(); - if (seconds < 0 || seconds > kMaxAllowedSeconds) { - GrowableArray args; - args.Add(&dart_seconds); - Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + const char* name; + bool succeeded = OS::GetTimeZoneName(seconds, &name); + if (!succeeded) { + UNIMPLEMENTED(); } - const char* name = OS::GetTimeZoneName(seconds); const String& dart_name = String::Handle(String::New(name)); arguments->SetReturn(dart_name); } @@ -32,28 +155,104 @@ DEFINE_NATIVE_ENTRY(DateNatives_timeZoneName, 1) { DEFINE_NATIVE_ENTRY(DateNatives_timeZoneOffsetInSeconds, 1) { GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); int64_t seconds = dart_seconds.AsInt64Value(); - if (seconds < 0 || seconds > kMaxAllowedSeconds) { - GrowableArray args; - args.Add(&dart_seconds); - Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + int offset; + bool succeeded = OS::GetTimeZoneOffsetInSeconds(seconds, &offset); + if (!succeeded) { + UNIMPLEMENTED(); } - int offset = OS::GetTimeZoneOffsetInSeconds(seconds); const Integer& dart_offset = Integer::Handle(Integer::New(offset)); arguments->SetReturn(dart_offset); } -DEFINE_NATIVE_ENTRY(DateNatives_localTimeZoneAdjustmentInSeconds, 0) { - int adjustment = OS::GetLocalTimeZoneAdjustmentInSeconds(); - const Integer& dart_adjustment = Integer::Handle(Integer::New(adjustment)); - arguments->SetReturn(dart_adjustment); -} - - DEFINE_NATIVE_ENTRY(DateNatives_currentTimeMillis, 0) { const Integer& time = Integer::Handle( Integer::New(OS::GetCurrentTimeMillis())); arguments->SetReturn(time); } + +DEFINE_NATIVE_ENTRY(DateNatives_getYear, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + intptr_t year = broken_down.year; + arguments->SetReturn(Integer::Handle(Integer::New(year))); +} + + +DEFINE_NATIVE_ENTRY(DateNatives_getMonth, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.month)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateNatives_getDay, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.day)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateNatives_getHours, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.hours)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateNatives_getMinutes, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.minutes)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateNatives_getSeconds, 2) { + GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0)); + GET_NATIVE_ARGUMENT(Bool, dart_is_utc, arguments->At(1)); + BrokenDownDate broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.seconds)); + arguments->SetReturn(result); +} + } // namespace dart diff --git a/runtime/lib/date.dart b/runtime/lib/date.dart index 7ba0b805118..d9c91ac5789 100644 --- a/runtime/lib/date.dart +++ b/runtime/lib/date.dart @@ -5,7 +5,7 @@ // VM implementation of DateImplementation. class DateImplementation implements Date { - static final int _MAX_VALUE = 8640000000000000; + static final int _SECONDS_YEAR_2035 = 2051222400; DateImplementation(int years, [int month = 1, @@ -80,14 +80,11 @@ class DateImplementation implements Date { } DateImplementation.fromEpoch(int this.value, [bool isUtc = false]) - : _isUtc = isUtc { - if (value.abs() > _MAX_VALUE) throw new IllegalArgumentException(value); - } + : _isUtc = isUtc; bool operator ==(Object other) { if (other is !DateImplementation) return false; - DateImplementation otherDate = other; - return value == otherDate.value; + return value == other.value; } bool operator <(Date other) => value < other.value; @@ -113,56 +110,79 @@ class DateImplementation implements Date { String get timeZoneName() { if (isUtc()) return "UTC"; - return _timeZoneName(value); + return _timeZoneName(_equivalentSeconds(_secondsSinceEpoch)); } Duration get timeZoneOffset() { if (isUtc()) return new Duration(0); - int offsetInSeconds = _timeZoneOffsetInSeconds(value); + int offsetInSeconds = + _timeZoneOffsetInSeconds(_equivalentSeconds(_secondsSinceEpoch)); return new Duration(seconds: offsetInSeconds); } int get year() { - return _decomposeIntoYearMonthDay(_localDateInUtcValue)[0]; + int secondsSinceEpoch = _secondsSinceEpoch; + // According to V8 some library calls have troubles with negative values. + // Therefore clamp to 0 - year 2035 (which is less than the size of 32bit). + if (secondsSinceEpoch >= 0 && secondsSinceEpoch < _SECONDS_YEAR_2035) { + return _getYear(secondsSinceEpoch, isUtc()); + } + + // Approximate the result. We don't take timeZone into account. + int approximateYear = _yearsFromSecondsSinceEpoch(secondsSinceEpoch); + int equivalentYear = _equivalentYear(approximateYear); + int y = _getYear(_equivalentSeconds(_secondsSinceEpoch), isUtc()); + return approximateYear + (y - equivalentYear); } int get month() { - return _decomposeIntoYearMonthDay(_localDateInUtcValue)[1]; + return _getMonth(_equivalentSeconds(_secondsSinceEpoch), isUtc()); } int get day() { - return _decomposeIntoYearMonthDay(_localDateInUtcValue)[2]; + return _getDay(_equivalentSeconds(_secondsSinceEpoch), isUtc()); } int get hours() { - int valueInHours = _flooredDivision(_localDateInUtcValue, - Duration.MILLISECONDS_PER_HOUR); - return valueInHours % Duration.HOURS_PER_DAY; + return _getHours(_equivalentSeconds(_secondsSinceEpoch), isUtc()); } int get minutes() { - int valueInMinutes = _flooredDivision(_localDateInUtcValue, - Duration.MILLISECONDS_PER_MINUTE); - return valueInMinutes % Duration.MINUTES_PER_HOUR; + return _getMinutes(_equivalentSeconds(_secondsSinceEpoch), isUtc()); } int get seconds() { - // Seconds are unaffected by the timezone the user is in. So we can - // directly use the value and not the [_localDateInUtcValue]. - int valueInSeconds = - _flooredDivision(value, Duration.MILLISECONDS_PER_SECOND); - return valueInSeconds % Duration.SECONDS_PER_MINUTE; + return _getSeconds(_equivalentSeconds(_secondsSinceEpoch), isUtc()); } int get milliseconds() { - // Milliseconds are unaffected by the timezone the user is in. So we can - // directly use the value and not the [_localDateInUtcValue]. return value % Duration.MILLISECONDS_PER_SECOND; } + int get _secondsSinceEpoch() { + // Always round down. + if (value < 0) { + return (value + 1) ~/ Duration.MILLISECONDS_PER_SECOND - 1; + } else { + return value ~/ Duration.MILLISECONDS_PER_SECOND; + } + } + int get weekday() { - int daysSince1970 = - _flooredDivision(_localDateInUtcValue, Duration.MILLISECONDS_PER_DAY); + final Date unixTimeStart = new Date(1970, 1, 1, 0, 0, 0, 0, isUtc()); + int msSince1970 = this.difference(unixTimeStart).inMilliseconds; + // Adjust the milliseconds to avoid problems with summer-time. + if (hours < 2) { + msSince1970 += 2 * Duration.MILLISECONDS_PER_HOUR; + } + // Compute the floor of msSince1970 / Duration.MS_PER_DAY. + int daysSince1970; + if (msSince1970 >= 0) { + daysSince1970 = msSince1970 ~/ Duration.MILLISECONDS_PER_DAY; + } else { + daysSince1970 = (msSince1970 - Duration.MILLISECONDS_PER_DAY + 1) ~/ + Duration.MILLISECONDS_PER_DAY; + } // 1970-1-1 was a Thursday. return ((daysSince1970 + Date.THU) % Date.DAYS_IN_WEEK); } @@ -202,34 +222,27 @@ class DateImplementation implements Date { } } - /** Returns a new [Date] with the [duration] added to [this]. */ + // Adds the [duration] to this Date instance. Date add(Duration duration) { return new DateImplementation.fromEpoch(value + duration.inMilliseconds, isUtc()); } - /** Returns a new [Date] with the [duration] subtracted from [this]. */ + // Subtracts the [duration] from this Date instance. Date subtract(Duration duration) { return new DateImplementation.fromEpoch(value - duration.inMilliseconds, isUtc()); } - /** Returns a [Duration] with the difference of [this] and [other]. */ + // Returns a [Duration] with the difference of [this] and [other]. Duration difference(Date other) { return new DurationImplementation(milliseconds: value - other.value); } - /** The first list contains the days until each month in non-leap years. The - * second list contains the days in leap years. */ - static final List> _DAYS_UNTIL_MONTH = - const [const [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], - const [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]]; - - // Returns the UTC year, month and day for the corresponding - // [millisecondsSinceEpoch]. + // 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 List _decomposeIntoYearMonthDay(int millisecondsSinceEpoch) { - // TODO(floitsch): cache result. + static int _yearsFromSecondsSinceEpoch(int secondsSinceEpoch) { final int DAYS_IN_4_YEARS = 4 * 365 + 1; final int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; final int DAYS_IN_400_YEARS = 4 * DAYS_IN_100_YEARS + 1; @@ -237,81 +250,91 @@ class DateImplementation implements Date { final int DAYS_OFFSET = 1000 * DAYS_IN_400_YEARS + 5 * DAYS_IN_400_YEARS - DAYS_1970_TO_2000; final int YEARS_OFFSET = 400000; + final int DAYS_YEAR_2098 = DAYS_IN_100_YEARS + 6 * DAYS_IN_4_YEARS; - int resultYear = 0; - int resultMonth = 0; - int resultDay = 0; - - // Always round down. - int days = _flooredDivision(millisecondsSinceEpoch, - Duration.MILLISECONDS_PER_DAY); - days += DAYS_OFFSET; - resultYear = 400 * (days ~/ DAYS_IN_400_YEARS) - YEARS_OFFSET; - days = days.remainder(DAYS_IN_400_YEARS); - days--; - int yd1 = days ~/ DAYS_IN_100_YEARS; - days = days.remainder(DAYS_IN_100_YEARS); - resultYear += 100 * yd1; - days++; - int yd2 = days ~/ DAYS_IN_4_YEARS; - days = days.remainder(DAYS_IN_4_YEARS); - resultYear += 4 * yd2; - days--; - int yd3 = days ~/ 365; - days = days.remainder(365); - resultYear += yd3; - - bool isLeap = (yd1 == 0 || yd2 != 0) && yd3 == 0; - if (isLeap) days++; - - List daysUntilMonth = _DAYS_UNTIL_MONTH[isLeap ? 1 : 0]; - for (resultMonth = 12; - daysUntilMonth[resultMonth - 1] > days; - resultMonth--) { - // Do nothing. + 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; + } else { + days += DAYS_OFFSET; + int result = 400 * (days ~/ DAYS_IN_400_YEARS) - YEARS_OFFSET; + days = days.remainder(DAYS_IN_400_YEARS); + days--; + int yd1 = days ~/ DAYS_IN_100_YEARS; + days = days.remainder(DAYS_IN_100_YEARS); + result += 100 * yd1; + days++; + int yd2 = days ~/ DAYS_IN_4_YEARS; + days = days.remainder(DAYS_IN_4_YEARS); + result += 4 * yd2; + days--; + int yd3 = days ~/ 365; + days = days.remainder(365); + result += yd3; + return result; } - resultDay = days - daysUntilMonth[resultMonth - 1] + 1; - return [resultYear, resultMonth, resultDay]; } - /** - * Returns the amount of milliseconds in UTC that represent the same values as - * [this]. - * - * Say [:t:] is the result of this function, then - * * [:this.year == new Date.fromEpoch(t, isUtc: true).year:], - * * [:this.month == new Date.fromEpoch(t, isUtc: true).month:], - * * [:this.day == new Date.fromEpoch(t, isUtc: true).day:], - * * [:this.hours == new Date.fromEpoch(t, isUtc: true).hours:], - * * ... - * - * Daylight savings is computed as if the date was computed in [1970..2037]. - * If [this] lies outside this range then it is a year with similar properties - * (leap year, weekdays) is used instead. - */ - int get _localDateInUtcValue() { - if (isUtc()) return value; - int offset = - _timeZoneOffsetInSeconds(value) * Duration.MILLISECONDS_PER_SECOND; - return value - offset; - } - - static int _flooredDivision(int a, int b) { - return (a - (a < 0 ? b - 1 : 0)) ~/ b; + // Given [secondsSinceEpoch] returns seconds such that they are at the same + // time in an equivalent year (see [_equivalentYear]). + // Leap seconds are ignored. + static int _equivalentSeconds(int secondsSinceEpoch) { + if (secondsSinceEpoch >= 0 && secondsSinceEpoch < _SECONDS_YEAR_2035) { + return secondsSinceEpoch; + } + int year = _yearsFromSecondsSinceEpoch(secondsSinceEpoch); + int days = _dayFromYear(year); + int equivalentYear = _equivalentYear(year); + int equivalentDays = _dayFromYear(equivalentYear); + int diffDays = equivalentDays - days; + return secondsSinceEpoch + diffDays * Duration.SECONDS_PER_DAY; } // Returns the days since 1970 for the start of the given [year]. // [year] may be before epoch. static int _dayFromYear(int year) { + int flooredDivision(int a, int b) { + return (a - (a < 0 ? b - 1 : 0)) ~/ b; + } + return 365 * (year - 1970) - + _flooredDivision(year - 1969, 4) - - _flooredDivision(year - 1901, 100) - + _flooredDivision(year - 1601, 400); + + flooredDivision(year - 1969, 4) + - flooredDivision(year - 1901, 100) + + flooredDivision(year - 1601, 400); } - static bool _isLeapYear(y) { - return (y.remainder(4) == 0) && - ((y.remainder(100) != 0) || (y.remainder(400) == 0)); + // 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 _equivalentYear(int year) { + // Returns 1 if in leap year. 0 otherwise. + bool inLeapYear(year) { + return (year.remainder(4) == 0) && + ((year.remainder(100) != 0) || (year.remainder(400) == 0)); + } + + // Returns the week day (in range 0 - 6). + int weekDay(year) { + // 1/1/1970 was a Thursday. + return (_dayFromYear(year) + 4) % 7; + } + // 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 = (inLeapYear(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; } static _brokenDownDateToMillisecondsSinceEpoch( @@ -327,139 +350,62 @@ class DateImplementation implements Date { if ((seconds < 0) || (seconds > 59)) return null; if ((milliseconds < 0) || (milliseconds > 999)) return null; - // 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(years) ? 1 : 0][month - 1]; - days += _dayFromYear(years); - int millisecondsSinceEpoch = days * Duration.MILLISECONDS_PER_DAY + - hours * Duration.MILLISECONDS_PER_HOUR + - minutes * Duration.MILLISECONDS_PER_MINUTE+ - seconds * Duration.MILLISECONDS_PER_SECOND + - milliseconds; - - // 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 (millisecondsSinceEpoch.abs() > - (_MAX_VALUE + Duration.MILLISECONDS_PER_DAY)) { - return null; + int equivalentYear; + int offsetInSeconds; + // According to V8 some library calls have troubles with negative values. + // Therefore clamp to 1970 - year 2035 (which is less than the size of + // 32bit). + // We exclude the year 1970 when the time is not UTC, since the epoch + // value could then be negative. + if (years < (isUtc ? 1970 : 1971) || years > 2035) { + equivalentYear = _equivalentYear(years); + int offsetInDays = (_dayFromYear(years) - _dayFromYear(equivalentYear)); + // Leap seconds are ignored. + offsetInSeconds = offsetInDays * Duration.SECONDS_PER_DAY; + } else { + equivalentYear = years; + offsetInSeconds = 0; } - - if (!isUtc) { - // Note that we need to add the local timezone adjustement before asking - // for the correct zone offset. - int adjustment = _localTimeZoneAdjustmentInSeconds() * - Duration.MILLISECONDS_PER_SECOND; - int zoneOffset = - _timeZoneOffsetInSeconds(millisecondsSinceEpoch + adjustment); - millisecondsSinceEpoch += zoneOffset * Duration.MILLISECONDS_PER_SECOND; - } - if (millisecondsSinceEpoch.abs() > _MAX_VALUE) return null; - return millisecondsSinceEpoch; - } - - /** - * 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 _equivalentYear(int year) { - // Returns the week day (in range 0 - 6). - int weekDay(y) { - // 1/1/1970 was a Thursday. - return (_dayFromYear(y) + 4) % 7; - } - // 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) { - final int DAYS_IN_4_YEARS = 4 * 365 + 1; - final int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; - final 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 ms = secondsSinceEpoch * Duration.MILLISECONDS_PER_SECOND; - return _decomposeIntoYearMonthDay(ms)[0]; - } - - /** - * Returns a date in seconds that is equivalent to the current date. An - * equivalent date has the same fields ([:month:], [:day:], etc.) as the - * [this], but the [:year:] is in the range [1970..2037]. - * - * * The time since the beginning of the year is the same. - * * If [this] is in a leap year then the returned seconds are in a leap - * year, too. - * * The week day of [this] is the same as the one for the returned date. - */ - static int _equivalentSeconds(int millisecondsSinceEpoch) { - final int CUT_OFF_SECONDS = 2100000000; - - int secondsSinceEpoch = _flooredDivision(millisecondsSinceEpoch, - Duration.MILLISECONDS_PER_SECOND); - - if (secondsSinceEpoch < 0 || secondsSinceEpoch >= 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; - } - - static int _timeZoneOffsetInSeconds(int millisecondsSinceEpoch) { - int equivalentSeconds = _equivalentSeconds(millisecondsSinceEpoch); - return _timeZoneOffsetInSecondsForClampedSeconds(equivalentSeconds); - } - - static String _timeZoneName(int millisecondsSinceEpoch) { - int equivalentSeconds = _equivalentSeconds(millisecondsSinceEpoch); - return _timeZoneNameForClampedSeconds(equivalentSeconds); + int secondsSinceEpoch = _brokenDownDateToSecondsSinceEpoch( + equivalentYear, month, day, hours, minutes, seconds, isUtc); + int adjustedSeconds = secondsSinceEpoch + offsetInSeconds; + return adjustedSeconds * Duration.MILLISECONDS_PER_SECOND + milliseconds; } final bool _isUtc; final int value; + // Natives + static _brokenDownDateToSecondsSinceEpoch( + int years, int month, int day, int hours, int minutes, int seconds, + bool isUtc) native "DateNatives_brokenDownToSecondsSinceEpoch"; + static int _getCurrentMs() native "DateNatives_currentTimeMillis"; - static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch) + static String _timeZoneName(int secondsSinceEpoch) native "DateNatives_timeZoneName"; - static int _timeZoneOffsetInSecondsForClampedSeconds(int secondsSinceEpoch) + static int _timeZoneOffsetInSeconds(int secondsSinceEpoch) native "DateNatives_timeZoneOffsetInSeconds"; - static int _localTimeZoneAdjustmentInSeconds() - native "DateNatives_localTimeZoneAdjustmentInSeconds"; + // TODO(floitsch): it would be more efficient if we didn't call the native + // function for every member, but cached the broken-down date. + static int _getYear(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getYear"; + + static int _getMonth(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getMonth"; + + static int _getDay(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getDay"; + + static int _getHours(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getHours"; + + static int _getMinutes(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getMinutes"; + + static int _getSeconds(int secondsSinceEpoch, bool isUtc) + native "DateNatives_getSeconds"; } diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 85402edd3e5..332c7e24b33 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -92,10 +92,16 @@ namespace dart { V(MathNatives_random, 0) \ V(MathNatives_parseInt, 1) \ V(MathNatives_parseDouble, 1) \ + V(DateNatives_brokenDownToSecondsSinceEpoch, 7) \ V(DateNatives_currentTimeMillis, 0) \ + V(DateNatives_getYear, 2) \ + V(DateNatives_getMonth, 2) \ + V(DateNatives_getDay, 2) \ + V(DateNatives_getHours, 2) \ + V(DateNatives_getMinutes, 2) \ + V(DateNatives_getSeconds, 2) \ V(DateNatives_timeZoneName, 1) \ V(DateNatives_timeZoneOffsetInSeconds, 1) \ - V(DateNatives_localTimeZoneAdjustmentInSeconds, 0) \ V(AssertionError_throwNew, 2) \ V(TypeError_throwNew, 5) \ V(FallThroughError_throwNew, 1) \ diff --git a/runtime/vm/os.h b/runtime/vm/os.h index 43c680d6075..49009838415 100644 --- a/runtime/vm/os.h +++ b/runtime/vm/os.h @@ -18,19 +18,40 @@ class Isolate; // Interface to the underlying OS platform. class OS { public: + // Takes the seconds since epoch (midnight, January 1, 1970 UTC) and breaks it + // down into date and time in the UTC timezone. + // The returned year is offset by 1900. The returned month is 0-based. + // Returns true if the conversion succeeds, false otherwise. + static bool GmTime(int64_t seconds_since_epoch, tm* tm_result); + + // Takes the seconds since epoch (midnight, January 1, 1970 UTC) and breaks it + // down into date and time in the local time. + // The returned year is offset by 1900. The returned month is 0-based. + // Returns true if the conversion succeeds, false otherwise. + static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result); + + // Takes the broken down date and time in UTC timezone and computes the + // seconds since epoch (midnight, January 1, 1970 UTC). + // The given year is offset by 1900. The given month is 0-based. + // Returns true if the conversion succeeds, false otherwise. + static bool MkGmTime(tm* tm, int64_t* seconds_result); + + // Takes the broken down date and time in local timezone and computes the + // seconds since epoch (midnight, January 1, 1970 UTC). + // The given year is offset by 1900. The given month is 0-based. + // Returns true if the conversion succeeds, false otherwise. + static bool MkTime(tm* tm, int64_t* seconds_result); + // Returns the abbreviated time-zone name for the given instant. // For example "CET" or "CEST". - static const char* GetTimeZoneName(int64_t seconds_since_epoch); + static bool GetTimeZoneName(int64_t seconds_since_epoch, + const char** name_result); // Returns the difference in seconds between local time and UTC for the given // instant. // For example 3600 for CET, and 7200 for CEST. - static int GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch); - - // Returns the difference in seconds between local time and UTC when no - // daylight saving is active. - // For example 3600 in CET and CEST. - static int GetLocalTimeZoneAdjustmentInSeconds(); + static bool GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch, + int* offset_result); // Returns the current time in milliseconds measured // from midnight January 1, 1970 UTC. diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc index e89b68f914b..ff857cbe351 100644 --- a/runtime/vm/os_linux.cc +++ b/runtime/vm/os_linux.cc @@ -17,7 +17,15 @@ namespace dart { -static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { +bool OS::GmTime(int64_t seconds_since_epoch, tm* tm_result) { + time_t seconds = static_cast(seconds_since_epoch); + if (seconds != seconds_since_epoch) return false; + struct tm* error_code = gmtime_r(&seconds, tm_result); + return error_code != NULL; +} + + +bool OS::LocalTime(int64_t seconds_since_epoch, tm* tm_result) { time_t seconds = static_cast(seconds_since_epoch); if (seconds != seconds_since_epoch) return false; struct tm* error_code = localtime_r(&seconds, tm_result); @@ -25,29 +33,50 @@ static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { } -const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { - tm decomposed; - bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); - return decomposed.tm_zone; +bool OS::MkGmTime(tm* tm, int64_t* seconds_result) { + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = timegm(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; } -int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { - tm decomposed; - bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); - // Even if the offset was 24 hours it would still easily fit into 32 bits. - return static_cast(decomposed.tm_gmtoff); +bool OS::MkTime(tm* tm, int64_t* seconds_result) { + // Let the libc figure out if daylight saving is active. + tm->tm_isdst = -1; + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = mktime(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; } -int OS::GetLocalTimeZoneAdjustmentInSeconds() { - // TODO(floitsch): avoid excessive calls to tzset? - tzset(); +bool OS::GetTimeZoneName(int64_t seconds_since_epoch, + const char** name_result) { + tm decomposed; + bool succeeded = LocalTime(seconds_since_epoch, &decomposed); + if (!succeeded) return false; + *name_result = decomposed.tm_zone; + return true; +} + + +bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch, + int* offset_result) { + tm decomposed; + bool succeeded = LocalTime(seconds_since_epoch, &decomposed); + if (!succeeded) return false; // Even if the offset was 24 hours it would still easily fit into 32 bits. - // Note that Unix and Dart disagree on the sign. - return static_cast(-timezone); + *offset_result = static_cast(decomposed.tm_gmtoff); + return true; } diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc index 7910b828ecb..3936e99e93e 100644 --- a/runtime/vm/os_macos.cc +++ b/runtime/vm/os_macos.cc @@ -18,7 +18,15 @@ namespace dart { -static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { +bool OS::GmTime(int64_t seconds_since_epoch, tm* tm_result) { + time_t seconds = static_cast(seconds_since_epoch); + if (seconds != seconds_since_epoch) return false; + struct tm* error_code = gmtime_r(&seconds, tm_result); + return error_code != NULL; +} + + +bool OS::LocalTime(int64_t seconds_since_epoch, tm* tm_result) { time_t seconds = static_cast(seconds_since_epoch); if (seconds != seconds_since_epoch) return false; struct tm* error_code = localtime_r(&seconds, tm_result); @@ -26,29 +34,50 @@ static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { } -const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { - tm decomposed; - bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); - return decomposed.tm_zone; +bool OS::MkGmTime(tm* tm, int64_t* seconds_result) { + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = timegm(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; } -int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { - tm decomposed; - bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); - // Even if the offset was 24 hours it would still easily fit into 32 bits. - return static_cast(decomposed.tm_gmtoff); +bool OS::MkTime(tm* tm, int64_t* seconds_result) { + // Let the libc figure out if daylight saving is active. + tm->tm_isdst = -1; + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = mktime(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; } -int OS::GetLocalTimeZoneAdjustmentInSeconds() { - // TODO(floitsch): avoid excessive calls to tzset? - tzset(); +bool OS::GetTimeZoneName(int64_t seconds_since_epoch, + const char** name_result) { + tm decomposed; + bool succeeded = LocalTime(seconds_since_epoch, &decomposed); + if (!succeeded) return false; + *name_result = decomposed.tm_zone; + return true; +} + + +bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch, + int* offset_result) { + tm decomposed; + bool succeeded = LocalTime(seconds_since_epoch, &decomposed); + if (!succeeded) return false; // Even if the offset was 24 hours it would still easily fit into 32 bits. - // Note that Unix and Dart disagree on the sign. - return static_cast(-timezone); + *offset_result = static_cast(decomposed.tm_gmtoff); + return true; } diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc index e0ff669ea71..778f9953d8c 100644 --- a/runtime/vm/os_win.cc +++ b/runtime/vm/os_win.cc @@ -10,8 +10,16 @@ namespace dart { +bool OS::GmTime(int64_t seconds_since_epoch, tm* tm_result) { + time_t seconds = static_cast(seconds_since_epoch); + if (seconds != seconds_since_epoch) return false; + errno_t error_code = gmtime_s(tm_result, &seconds); + return error_code == 0; +} + + // As a side-effect sets the globals _timezone, _daylight and _tzname. -static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { +bool OS::LocalTime(int64_t seconds_since_epoch, tm* tm_result) { time_t seconds = static_cast(seconds_since_epoch); if (seconds != seconds_since_epoch) return false; // localtime_s implicitly sets _timezone, _daylight and _tzname. @@ -20,6 +28,34 @@ static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { } +bool OS::MkGmTime(tm* tm, int64_t* seconds_result) { + // Disable daylight saving. + tm->tm_isdst = 0; + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = _mkgmtime(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; +} + + +bool OS::MkTime(tm* tm, int64_t* seconds_result) { + // Let the libc figure out if daylight saving is active. + tm->tm_isdst = -1; + // Set wday to an impossible day, so that we can catch bad input. + tm->tm_wday = -1; + time_t seconds = mktime(tm); + if ((seconds == -1) && (tm->tm_wday == -1)) { + return false; + } + *seconds_result = seconds; + return true; +} + + static int GetDaylightSavingBiasInSeconds() { TIME_ZONE_INFORMATION zone_information; memset(&zone_information, 0, sizeof(zone_information)); @@ -31,41 +67,39 @@ static int GetDaylightSavingBiasInSeconds() { } } - -const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { +bool OS::GetTimeZoneName(int64_t seconds_since_epoch, + const char** name_result) { tm decomposed; // LocalTime will set _tzname. bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); + if (!succeeded) return false; int inDaylightSavingsTime = decomposed.tm_isdst; - ASSERT(inDaylightSavingsTime == 0 || inDaylightSavingsTime == 1); - return _tzname[inDaylightSavingsTime]; + if (inDaylightSavingsTime != 0 && inDaylightSavingsTime != 1) { + return false; + } + *name_result = _tzname[inDaylightSavingsTime]; + return true; } -int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { +bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch, + int* offset_result) { tm decomposed; // LocalTime will set _timezone. bool succeeded = LocalTime(seconds_since_epoch, &decomposed); - ASSERT(succeeded); + if (!succeeded) return false; int inDaylightSavingsTime = decomposed.tm_isdst; - ASSERT(inDaylightSavingsTime == 0 || inDaylightSavingsTime == 1); + if (inDaylightSavingsTime != 0 && inDaylightSavingsTime != 1) { + return false; + } // Dart and Windows disagree on the sign of the bias. - int offset = static_cast(-_timezone); + *offset_result = static_cast(-_timezone); if (inDaylightSavingsTime == 1) { static int daylight_bias = GetDaylightSavingBiasInSeconds(); // Subtract because windows and Dart disagree on the sign. - offset = offset - daylight_bias; + *offset_result = *offset_result - daylight_bias; } - return offset; -} - - -int OS::GetLocalTimeZoneAdjustmentInSeconds() { - // TODO(floitsch): avoid excessive calls to _tzset? - _tzset(); - // Dart and Windows disagree on the sign of the bias. - return static_cast(-_timezone); + return true; } diff --git a/tests/co19/co19-leg.status b/tests/co19/co19-leg.status index 4f688b05d8d..92f67d46f02 100644 --- a/tests/co19/co19-leg.status +++ b/tests/co19/co19-leg.status @@ -135,7 +135,6 @@ LibTest/isolate/isolate_api/spawnFunction_A03_t01: Fail # Runtime error: TypeErr # The following tests use the deprecated Date interface. -# Issue co19 - 125 LibTest/core/Date/Date_A02_t01: Fail, OK LibTest/core/Date/Date.now_A02_t01: Fail, OK LibTest/core/Date/add_A04_t01: Fail, OK diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status index cfe1d205829..c91608fcdac 100644 --- a/tests/co19/co19-runtime.status +++ b/tests/co19/co19-runtime.status @@ -108,7 +108,6 @@ LibTest/core/int/operator_remainder_A01_t02: Fail [ $runtime == vm ] # The following tests use the deprecated Date interface. -# Issue co19 - 125 LibTest/core/Date/Date_A02_t01: Fail LibTest/core/Date/Date.now_A02_t01: Fail LibTest/core/Date/add_A04_t01: Fail @@ -136,7 +135,7 @@ LibTest/core/Date/Date.fromEpoch_A01_t01: Skip LibTest/core/Date/Date.withTimeZone_A01_t01: Skip LibTest/core/Date/Date.withTimeZone_A01_t02: Skip LibTest/core/Date/Date.withTimeZone_A01_t03: Skip -LibTest/core/Date/year_A01_t01: Fail, Pass + LibTest/core/TimeZone/TimeZone.local_A01_t01: Skip LibTest/core/TimeZone/TimeZone.utc_A01_t01: Skip diff --git a/tests/corelib/date_time_test.dart b/tests/corelib/date_time_test.dart index 6ef740a7b15..e40f8d9d6b0 100644 --- a/tests/corelib/date_time_test.dart +++ b/tests/corelib/date_time_test.dart @@ -206,64 +206,6 @@ class DateTest { dt.year, dt.month, dt.day, dt.hours, dt.minutes, dt.seconds, dt.milliseconds); Expect.equals(dt.value, dt2.value); - dt = new Date.fromEpoch(2100000000 * 1000, isUtc: true); - Expect.equals(2036, dt.year); - Expect.equals(7, dt.month); - Expect.equals(18, dt.day); - Expect.equals(13, dt.hours); - Expect.equals(20, dt.minutes); - Expect.equals(0, dt.seconds); - Expect.equals(0, dt.milliseconds); - // Internally this will use the maximum value for the native calls. - dt = new Date(2036, 7, 18, 13, 20); - Expect.equals(2036, dt.year); - Expect.equals(7, dt.month); - Expect.equals(18, dt.day); - Expect.equals(13, dt.hours); - Expect.equals(20, dt.minutes); - Expect.equals(0, dt.seconds); - Expect.equals(0, dt.milliseconds); - Expect.equals("2036-07-18 13:20:00.000", dt.toString()); - } - - static void testExtremes() { - var dt = new Date.fromEpoch(8640000000000000, isUtc: true); - Expect.equals(275760, dt.year); - Expect.equals(9, dt.month); - Expect.equals(13, dt.day); - Expect.equals(0, dt.hours); - Expect.equals(0, dt.minutes); - Expect.equals(0, dt.seconds); - Expect.equals(0, dt.milliseconds); - dt = new Date.fromEpoch(-8640000000000000, isUtc: true); - Expect.equals(-271821, dt.year); - Expect.equals(4, dt.month); - Expect.equals(20, dt.day); - Expect.equals(0, dt.hours); - Expect.equals(0, dt.minutes); - Expect.equals(0, dt.seconds); - Expect.equals(0, dt.milliseconds); - // Make sure that we can build the extreme dates in local too. - dt = new Date.fromEpoch(8640000000000000); - dt = new Date(dt.year, dt.month, dt.day, dt.hours, dt.minutes); - Expect.equals(8640000000000000, dt.value); - dt = new Date.fromEpoch(-8640000000000000); - dt = new Date(dt.year, dt.month, dt.day, dt.hours, dt.minutes); - Expect.equals(-8640000000000000, dt.value); - Expect.throws(() => new Date.fromEpoch(8640000000000001, isUtc: true)); - Expect.throws(() => new Date.fromEpoch(-8640000000000001, isUtc: true)); - Expect.throws(() => new Date.fromEpoch(8640000000000001)); - Expect.throws(() => new Date.fromEpoch(-8640000000000001)); - dt = new Date.fromEpoch(8640000000000000); - Expect.throws(() => new Date(dt.year, dt.month, dt.day, - dt.hours, dt.minutes, 0, 1)); - dt = new Date.fromEpoch(-8640000000000000); - // TODO(floitsch): Update comment after refactoring. - // This test currently fails because the arguments must not be negative. - // However we are going to allow negative (and overflowing) arguments and - // this line will then throw for the correct reason. - Expect.throws(() => new Date(dt.year, dt.month, dt.day, - dt.hours, dt.minutes, 0, -1)); } static void testUTCGetters() { @@ -310,11 +252,7 @@ class DateTest { } static void testConstructors() { - var dt0 = new Date(2011, 5, 11, 18, 58, 35, 0, isUtc: true); - Expect.equals(1305140315000, dt0.value); var dt1 = new Date.fromEpoch(1305140315000); - Expect.equals(dt1.value, dt0.value); - Expect.equals(true, dt1 == dt0); var dt3 = new Date(dt1.year, dt1.month, dt1.day, dt1.hours, dt1.minutes, dt1.seconds, dt1.milliseconds); Expect.equals(dt1.value, dt3.value); @@ -324,6 +262,9 @@ class DateTest { dt1.seconds, dt1.milliseconds); Expect.equals(dt1.value, dt3.value); Expect.equals(true, dt1 == dt3); + dt3 = new Date(2011, 5, 11, 18, 58, 35, 0, isUtc: true); + Expect.equals(dt1.value, dt3.value); + Expect.equals(true, dt1 == dt3); var dt2 = dt1.toLocal(); dt3 = new Date(2011, 5, dt1.day, dt1.hours, dt1.minutes, 35, 0); Expect.equals(dt2.value, dt3.value); @@ -342,42 +283,6 @@ class DateTest { Expect.equals(12, dt3.seconds); Expect.equals(0, dt3.milliseconds); Expect.equals(true, dt3.isUtc()); - var dt4 = new Date(99, 1, 2); - Expect.equals(99, dt4.year); - Expect.equals(1, dt4.month); - Expect.equals(2, dt4.day); - Expect.equals(0, dt4.hours); - Expect.equals(0, dt4.minutes); - Expect.equals(0, dt4.seconds); - Expect.equals(0, dt4.milliseconds); - Expect.isFalse(dt4.isUtc()); - var dt5 = new Date(99, 1, 2, isUtc: true); - Expect.equals(99, dt5.year); - Expect.equals(1, dt5.month); - Expect.equals(2, dt5.day); - Expect.equals(0, dt5.hours); - Expect.equals(0, dt5.minutes); - Expect.equals(0, dt5.seconds); - Expect.equals(0, dt5.milliseconds); - Expect.isTrue(dt5.isUtc()); - var dt6 = new Date(2012, 2, 27, 13, 27, 0); - Expect.equals(2012, dt6.year); - Expect.equals(2, dt6.month); - Expect.equals(27, dt6.day); - Expect.equals(13, dt6.hours); - Expect.equals(27, dt6.minutes); - Expect.equals(0, dt6.seconds); - Expect.equals(0, dt6.milliseconds); - Expect.isFalse(dt6.isUtc()); - var dt7 = new Date(2012, 2, 27, 13, 27, 0, isUtc: true); - Expect.equals(2012, dt7.year); - Expect.equals(2, dt7.month); - Expect.equals(27, dt7.day); - Expect.equals(13, dt7.hours); - Expect.equals(27, dt7.minutes); - Expect.equals(0, dt7.seconds); - Expect.equals(0, dt7.milliseconds); - Expect.isTrue(dt7.isUtc()); } static void testChangeTimeZone() { @@ -624,14 +529,13 @@ class DateTest { static void testMain() { testNow(); testValue(); - testConstructors(); testUTCGetters(); testLocalGetters(); + testConstructors(); testChangeTimeZone(); testSubAdd(); testDateStrings(); testEquivalentYears(); - testExtremes(); testFarAwayDates(); testWeekday(); }