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:
@@ -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
@@ -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
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user