Revert "Refactor Date implementation in VM."

Also revert "Round days-computation to get rid of daylight-savings differences."

This reverts commit 7542 and commit 8544.

Review URL: https://chromiumcodereview.appspot.com//10534111

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@8547 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
floitsch@google.com
2012-06-12 13:44:45 +00:00
parent 71f123f9e9
commit eb92af4080
13 changed files with 579 additions and 421 deletions
+4 -8
View File
@@ -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):].
*/
@@ -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;
}
@@ -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;
+217 -18
View File
@@ -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<int>(year);
tm_broken_down.tm_mon = static_cast<int>(month);
tm_broken_down.tm_mday = static_cast<int>(broken_down.day);
tm_broken_down.tm_hour = static_cast<int>(broken_down.hours);
tm_broken_down.tm_min = static_cast<int>(broken_down.minutes);
tm_broken_down.tm_sec = static_cast<int>(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<const Object*> 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<const Object*> 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
+170 -224
View File
@@ -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<List<int>> _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<int> _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<int> 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 <int>[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";
}
+7 -1
View File
@@ -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) \
+28 -7
View File
@@ -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.
+46 -17
View File
@@ -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<time_t>(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<time_t>(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<int>(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<int>(-timezone);
*offset_result = static_cast<int>(decomposed.tm_gmtoff);
return true;
}
+46 -17
View File
@@ -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<time_t>(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<time_t>(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<int>(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<int>(-timezone);
*offset_result = static_cast<int>(decomposed.tm_gmtoff);
return true;
}
+54 -20
View File
@@ -10,8 +10,16 @@
namespace dart {
bool OS::GmTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(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<time_t>(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<int>(-_timezone);
*offset_result = static_cast<int>(-_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<int>(-_timezone);
return true;
}
-1
View File
@@ -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
+1 -2
View File
@@ -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
+4 -100
View File
@@ -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();
}