1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 00:08:46 +00:00

Revert "[js_runtime, js_dev_runtime] Implement microsecond field of DataTime"

This reverts commit fb057ea4e0.

Reason for revert: b/342552853

Original change's description:
> [js_runtime, js_dev_runtime] Implement `microsecond` field of `DataTime`
>
> - Move DateTime implementation for dart2js and DDC into a shared place to reduce duplication.
>
> - Add a _microsecond field to the web DateTime to track microseconds outside of the JavaScript Date.
>
> - The cute dart2js optimization whereby `DateTime.now().millisecondsSinceEpoch` is compiled to `Date.now()` still works.
>
> - Both implementations report better errors.
>
> - Fixed VM bug with in-range sentinel.
>
>
> Change-Id: I9156255bdb6ecc195500ae9bc88f91fb315b6297
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/366963
> Reviewed-by: Alexander Aprelev <aam@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Reviewed-by: Lasse Nielsen <lrn@google.com>
> Commit-Queue: Stephen Adams <sra@google.com>

Change-Id: I58572256a7710df4589bb5e41c7afee295c2388b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368103
Reviewed-by: Alexander Thomas <athom@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Auto-Submit: Ivan Inozemtsev <iinozemtsev@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
This commit is contained in:
Ivan Inozemtsev 2024-05-27 07:54:14 +00:00 committed by Alexander Thomas
parent a3f83a9e44
commit 72b2883c6f
12 changed files with 474 additions and 466 deletions

View File

@ -17,20 +17,6 @@
[#55418]: https://github.com/dart-lang/sdk/issues/55418
[#55436]: https://github.com/dart-lang/sdk/issues/55436
### Libraries
#### `dart:core`
- `DateTime` on the web platform now stores microseconds. Fixes [#44876][].
The web imlementation is now practically compatible with the native
implementation. Small discrepancies due to rounding of web integers may still
occur for (1) `microsecondsSinceEpoch` outside the safe range, corresponding
to dates with a year outside of 1685..2255, and (2) arithmetic (`add`,
`subtract`, `difference`) where the `Duration` argument or result exceeds 570
years.
[#44876]: https://github.com/dart-lang/sdk/issues/44876
### Tools
#### Linter

View File

@ -11,7 +11,7 @@ class Foo {
int? x;
/*member: Foo.:member_unit=4{libB}*/
Foo() {
x = DateTime.now().millisecondsSinceEpoch;
x = DateTime.now().millisecond;
}
/*member: Foo.method:member_unit=4{libB}*/
@pragma('dart2js:noInline')

View File

@ -267,6 +267,147 @@ class Error {
}
}
// Patch for DateTime implementation.
@patch
class DateTime {
@patch
DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
{bool isUtc = false})
: this._withValue(millisecondsSinceEpoch, isUtc: isUtc);
@patch
DateTime.fromMicrosecondsSinceEpoch(int microsecondsSinceEpoch,
{bool isUtc = false})
: this._withValue(
_microsecondInRoundedMilliseconds(microsecondsSinceEpoch),
isUtc: isUtc);
@patch
DateTime._internal(int year, int month, int day, int hour, int minute,
int second, int millisecond, int microsecond, bool isUtc)
: isUtc = isUtc,
_value = checkInt(Primitives.valueFromDecomposedDate(
year,
month,
day,
hour,
minute,
second,
millisecond + _microsecondInRoundedMilliseconds(microsecond),
isUtc));
@patch
DateTime._now()
: isUtc = false,
_value = Primitives.dateNow();
@patch
DateTime._nowUtc()
: isUtc = true,
_value = Primitives.dateNow();
/// Rounds the given [microsecond] to the nearest milliseconds value.
///
/// For example, invoked with argument `2600` returns `3`.
static int _microsecondInRoundedMilliseconds(int microsecond) {
return (microsecond / 1000).round();
}
@patch
static int? _brokenDownDateToValue(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc) {
return Primitives.valueFromDecomposedDate(
year,
month,
day,
hour,
minute,
second,
millisecond + _microsecondInRoundedMilliseconds(microsecond),
isUtc);
}
@patch
String get timeZoneName {
if (isUtc) return "UTC";
return Primitives.getTimeZoneName(this);
}
@patch
Duration get timeZoneOffset {
if (isUtc) return Duration.zero;
return Duration(minutes: Primitives.getTimeZoneOffsetInMinutes(this));
}
@patch
DateTime add(Duration duration) {
return DateTime._withValue(_value + duration.inMilliseconds, isUtc: isUtc);
}
@patch
DateTime subtract(Duration duration) {
return DateTime._withValue(_value - duration.inMilliseconds, isUtc: isUtc);
}
@patch
Duration difference(DateTime other) {
return Duration(milliseconds: _value - other.millisecondsSinceEpoch);
}
@patch
int get millisecondsSinceEpoch => _value;
@patch
int get microsecondsSinceEpoch => _value * 1000;
@patch
int get year => Primitives.getYear(this);
@patch
int get month => Primitives.getMonth(this);
@patch
int get day => Primitives.getDay(this);
@patch
int get hour => Primitives.getHours(this);
@patch
int get minute => Primitives.getMinutes(this);
@patch
int get second => Primitives.getSeconds(this);
@patch
int get millisecond => Primitives.getMilliseconds(this);
@patch
int get microsecond => 0;
@patch
int get weekday => Primitives.getWeekday(this);
@patch
bool operator ==(Object other) =>
other is DateTime &&
_value == other.millisecondsSinceEpoch &&
isUtc == other.isUtc;
@patch
bool isBefore(DateTime other) => _value < other.millisecondsSinceEpoch;
@patch
bool isAfter(DateTime other) => _value > other.millisecondsSinceEpoch;
@patch
bool isAtSameMomentAs(DateTime other) =>
_value == other.millisecondsSinceEpoch;
@patch
int compareTo(DateTime other) =>
_value.compareTo(other.millisecondsSinceEpoch);
}
// Patch for Stopwatch implementation.
@patch
class Stopwatch {

View File

@ -392,7 +392,6 @@ class Primitives {
@nullCheck int minutes,
@nullCheck int seconds,
@nullCheck int milliseconds,
@nullCheck int microseconds,
@nullCheck bool isUtc) {
final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000;
var jsMonth = month - 1;
@ -404,11 +403,6 @@ class Primitives {
years += 400;
jsMonth -= 400 * 12;
}
// JavaScript `Date` does not handle microseconds, so ensure the provided
// microseconds is in range [0..999].
final remainder = microseconds % 1000;
milliseconds += (microseconds - remainder) ~/ 1000;
microseconds = remainder;
int value;
if (isUtc) {
value = JS<int>('!', r'Date.UTC(#, #, #, #, #, #, #)', years, jsMonth,
@ -419,13 +413,23 @@ class Primitives {
}
if (value.isNaN ||
value < -MAX_MILLISECONDS_SINCE_EPOCH ||
value > MAX_MILLISECONDS_SINCE_EPOCH ||
value == MAX_MILLISECONDS_SINCE_EPOCH && microseconds != 0) {
value > MAX_MILLISECONDS_SINCE_EPOCH) {
return null;
}
if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc);
return value;
}
static int patchUpY2K(value, years, isUtc) {
var date = JS<int>('!', r'new Date(#)', value);
if (isUtc) {
JS<int>('!', r'#.setUTCFullYear(#)', date, years);
} else {
JS<int>('!', r'#.setFullYear(#)', date, years);
}
return JS<int>('!', r'#.valueOf()', date);
}
// Lazily keep a JS Date stored in the JS object.
static lazyAsJsDate(DateTime receiver) {
if (JS<bool>('!', r'#.date === (void 0)', receiver)) {
@ -489,6 +493,13 @@ class Primitives {
return (weekday + 6) % 7 + 1;
}
static num valueFromDateString(str) {
if (str is! String) throw argumentErrorValue(str);
num value = JS('!', r'Date.parse(#)', str);
if (value.isNaN) throw argumentErrorValue(str);
return value;
}
static Object? getProperty(Object? object, Object key) {
if (object == null || object is bool || object is num || object is String) {
throw argumentErrorValue(object);

View File

@ -281,6 +281,151 @@ class Error {
}
}
// Patch for DateTime implementation.
@patch
class DateTime {
@patch
DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
{bool isUtc = false})
// `0 + millisecondsSinceEpoch` forces the inferred result to be non-null.
: this._withValue(0 + millisecondsSinceEpoch, isUtc: isUtc);
@patch
DateTime.fromMicrosecondsSinceEpoch(int microsecondsSinceEpoch,
{bool isUtc = false})
: this._withValue(
_microsecondInRoundedMilliseconds(microsecondsSinceEpoch),
isUtc: isUtc);
@patch
DateTime._internal(int year, int month, int day, int hour, int minute,
int second, int millisecond, int microsecond, bool isUtc)
// checkBool is manually inlined here because dart2js doesn't inline it
// and [isUtc] is usually a constant.
: this.isUtc =
isUtc is bool ? isUtc : throw ArgumentError.value(isUtc, 'isUtc'),
_value = checkInt(Primitives.valueFromDecomposedDate(
year,
month,
day,
hour,
minute,
second,
millisecond + _microsecondInRoundedMilliseconds(microsecond),
isUtc));
@patch
DateTime._now()
: isUtc = false,
_value = Primitives.dateNow();
@patch
DateTime._nowUtc()
: isUtc = true,
_value = Primitives.dateNow();
/// Rounds the given [microsecond] to the nearest milliseconds value.
///
/// For example, invoked with argument `2600` returns `3`.
static int _microsecondInRoundedMilliseconds(int microsecond) {
return (microsecond / 1000).round();
}
@patch
static int? _brokenDownDateToValue(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc) {
return Primitives.valueFromDecomposedDate(
year,
month,
day,
hour,
minute,
second,
millisecond + _microsecondInRoundedMilliseconds(microsecond),
isUtc);
}
@patch
String get timeZoneName {
if (isUtc) return "UTC";
return Primitives.getTimeZoneName(this);
}
@patch
Duration get timeZoneOffset {
if (isUtc) return Duration();
return Duration(minutes: Primitives.getTimeZoneOffsetInMinutes(this));
}
@patch
DateTime add(Duration duration) {
return DateTime._withValue(_value + duration.inMilliseconds, isUtc: isUtc);
}
@patch
DateTime subtract(Duration duration) {
return DateTime._withValue(_value - duration.inMilliseconds, isUtc: isUtc);
}
@patch
Duration difference(DateTime other) {
return Duration(milliseconds: _value - other.millisecondsSinceEpoch);
}
@patch
int get millisecondsSinceEpoch => _value;
@patch
int get microsecondsSinceEpoch => 1000 * _value;
@patch
int get year => Primitives.getYear(this);
@patch
int get month => Primitives.getMonth(this);
@patch
int get day => Primitives.getDay(this);
@patch
int get hour => Primitives.getHours(this);
@patch
int get minute => Primitives.getMinutes(this);
@patch
int get second => Primitives.getSeconds(this);
@patch
int get millisecond => Primitives.getMilliseconds(this);
@patch
int get microsecond => 0;
@patch
int get weekday => Primitives.getWeekday(this);
@patch
bool operator ==(Object other) =>
other is DateTime &&
_value == other.millisecondsSinceEpoch &&
isUtc == other.isUtc;
@patch
bool isBefore(DateTime other) => _value < other.millisecondsSinceEpoch;
@patch
bool isAfter(DateTime other) => _value > other.millisecondsSinceEpoch;
@patch
bool isAtSameMomentAs(DateTime other) =>
_value == other.millisecondsSinceEpoch;
@patch
int compareTo(DateTime other) =>
_value.compareTo(other.millisecondsSinceEpoch);
}
// Patch for Stopwatch implementation.
@patch
class Stopwatch {

View File

@ -682,16 +682,8 @@ class Primitives {
as int;
}
static int? valueFromDecomposedDate(
int years,
int month,
int day,
int hours,
int minutes,
int seconds,
int milliseconds,
int microseconds,
bool isUtc) {
static int? valueFromDecomposedDate(int years, int month, int day, int hours,
int minutes, int seconds, int milliseconds, bool isUtc) {
final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000;
checkInt(years);
checkInt(month);
@ -700,7 +692,6 @@ class Primitives {
checkInt(minutes);
checkInt(seconds);
checkInt(milliseconds);
checkInt(microseconds);
checkBool(isUtc);
var jsMonth = month - 1;
// The JavaScript Date constructor 'corrects' year NN to 19NN. Sidestep that
@ -711,11 +702,6 @@ class Primitives {
years += 400;
jsMonth -= 400 * 12;
}
// JavaScript `Date` does not handle microseconds, so ensure the provided
// microseconds is in range [0..999].
final remainder = microseconds % 1000;
milliseconds += (microseconds - remainder) ~/ 1000;
microseconds = remainder;
num value;
if (isUtc) {
value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', years, jsMonth, day,
@ -726,8 +712,7 @@ class Primitives {
}
if (value.isNaN ||
value < -MAX_MILLISECONDS_SINCE_EPOCH ||
value > MAX_MILLISECONDS_SINCE_EPOCH ||
value == MAX_MILLISECONDS_SINCE_EPOCH && microseconds != 0) {
value > MAX_MILLISECONDS_SINCE_EPOCH) {
return null;
}
return JS('int', '#', value);
@ -759,7 +744,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getYear(DateTime receiver) {
static getYear(DateTime receiver) {
return (receiver.isUtc)
? JS('int', r'(#.getUTCFullYear() + 0)', lazyAsJsDate(receiver))
: JS('int', r'(#.getFullYear() + 0)', lazyAsJsDate(receiver));
@ -768,7 +753,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getMonth(DateTime receiver) {
static getMonth(DateTime receiver) {
return (receiver.isUtc)
? JS('JSUInt31', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver))
: JS('JSUInt31', r'#.getMonth() + 1', lazyAsJsDate(receiver));
@ -777,7 +762,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getDay(DateTime receiver) {
static getDay(DateTime receiver) {
return (receiver.isUtc)
? JS('JSUInt31', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver))
: JS('JSUInt31', r'(#.getDate() + 0)', lazyAsJsDate(receiver));
@ -786,7 +771,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getHours(DateTime receiver) {
static getHours(DateTime receiver) {
return (receiver.isUtc)
? JS('JSUInt31', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver))
: JS('JSUInt31', r'(#.getHours() + 0)', lazyAsJsDate(receiver));
@ -795,7 +780,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getMinutes(DateTime receiver) {
static getMinutes(DateTime receiver) {
return (receiver.isUtc)
? JS('JSUInt31', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver))
: JS('JSUInt31', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver));
@ -804,7 +789,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getSeconds(DateTime receiver) {
static getSeconds(DateTime receiver) {
return (receiver.isUtc)
? JS('JSUInt31', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver))
: JS('JSUInt31', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver));
@ -813,7 +798,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getMilliseconds(DateTime receiver) {
static getMilliseconds(DateTime receiver) {
return (receiver.isUtc)
? JS(
'JSUInt31', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver))
@ -823,7 +808,7 @@ class Primitives {
@pragma('dart2js:noSideEffects')
@pragma('dart2js:noThrows')
@pragma('dart2js:noInline')
static int getWeekday(DateTime receiver) {
static getWeekday(DateTime receiver) {
int weekday = (receiver.isUtc)
? JS('int', r'#.getUTCDay() + 0', lazyAsJsDate(receiver))
: JS('int', r'#.getDay() + 0', lazyAsJsDate(receiver));
@ -831,6 +816,13 @@ class Primitives {
return (weekday + 6) % 7 + 1;
}
static num valueFromDateString(str) {
if (str is! String) throw argumentErrorValue(str);
num value = JS('num', r'Date.parse(#)', str);
if (value.isNaN) throw argumentErrorValue(str);
return value;
}
static getProperty(object, key) {
if (object == null || object is bool || object is num || object is String) {
throw argumentErrorValue(object);

View File

@ -1,195 +0,0 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:_foreign_helper' show JS;
import 'dart:_internal' show patch;
import 'dart:_js_helper' show checkInt, Primitives;
// Patch for DateTime implementation.
@patch
class DateTime {
/// The value component of this DateTime, equal to [millisecondsSinceEpoch].
final int _value;
/// The [microsecond] component of this DateTime, in the range [0...999].
final int _microsecond;
/// Constructor for pre-validated components.
DateTime._(this._value, this._microsecond, {required this.isUtc});
/// Constructs a new [DateTime] instance with the given value.
///
/// If [isUtc] is false, then the date is in the local time zone.
DateTime._withValueChecked(int millisecondsSinceEpoch, int microsecond,
{required bool isUtc})
: _value = _validate(millisecondsSinceEpoch, microsecond, isUtc),
_microsecond = microsecond,
this.isUtc = isUtc;
@patch
DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
{bool isUtc = false})
: this._withValueChecked(millisecondsSinceEpoch, 0, isUtc: isUtc);
@patch
DateTime.fromMicrosecondsSinceEpoch(int microsecondsSinceEpoch,
{bool isUtc = false})
: this._withValueChecked(
(microsecondsSinceEpoch - microsecondsSinceEpoch % 1000) ~/ 1000,
microsecondsSinceEpoch % 1000,
isUtc: isUtc);
@patch
DateTime._internal(int year, int month, int day, int hour, int minute,
int second, int millisecond, int microsecond, bool isUtc)
// checkBool is manually inlined here because dart2js doesn't inline it
// and [isUtc] is usually a constant.
: this.isUtc =
isUtc is bool ? isUtc : throw ArgumentError.value(isUtc, 'isUtc'),
_value = Primitives.valueFromDecomposedDate(year, month, day, hour,
minute, second, millisecond, microsecond, isUtc) ??
_sentinel,
_microsecond = microsecond % 1000 {
if (_value == _sentinel) {
throw ArgumentError('($year, $month, $day,'
' $hour, $minute, $second, $millisecond, $microsecond)');
}
}
static const _sentinel = _maxMillisecondsSinceEpoch * 10;
static const _sentinelConstraint = _sentinel < -_maxMillisecondsSinceEpoch ||
_sentinel > _maxMillisecondsSinceEpoch;
static const _sentinelAssertion = 1 ~/ (_sentinelConstraint ? 1 : 0);
@patch
DateTime._now()
: isUtc = false,
_value = Primitives.dateNow(),
_microsecond = 0;
@patch
DateTime._nowUtc()
: isUtc = true,
_value = Primitives.dateNow(),
_microsecond = 0;
@patch
DateTime _withUtc({required bool isUtc}) {
return DateTime._(_value, _microsecond, isUtc: isUtc);
}
@patch
static DateTime? _finishParse(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc) {
final value = Primitives.valueFromDecomposedDate(year, month, day, hour,
minute, second, millisecond, microsecond, isUtc);
if (value == null) return null;
return DateTime._withValueChecked(value, microsecond, isUtc: isUtc);
}
@patch
String get timeZoneName {
if (isUtc) return "UTC";
return Primitives.getTimeZoneName(this);
}
@patch
Duration get timeZoneOffset {
if (isUtc) return Duration.zero;
return Duration(minutes: Primitives.getTimeZoneOffsetInMinutes(this));
}
@patch
DateTime add(Duration duration) => _addMicroseconds(duration.inMicroseconds);
@patch
DateTime subtract(Duration duration) =>
_addMicroseconds(0 - duration.inMicroseconds);
DateTime _addMicroseconds(int durationMicroseconds) {
final durationLo = durationMicroseconds % 1000;
final durationHi = (durationMicroseconds - durationLo) ~/ 1000;
final sumLo = _microsecond + durationLo;
final microsecond = sumLo % 1000;
final carry = (sumLo - microsecond) ~/ 1000;
final milliseconds = _value + carry + durationHi;
return DateTime._withValueChecked(milliseconds, microsecond, isUtc: isUtc);
}
@patch
Duration difference(DateTime other) {
final deltaMilliseconds =
millisecondsSinceEpoch - other.millisecondsSinceEpoch;
final deltaMicroseconds = microsecond - other.microsecond;
return Duration(
milliseconds: deltaMilliseconds, microseconds: deltaMicroseconds);
}
@patch
int get millisecondsSinceEpoch => _value;
@patch
int get microsecondsSinceEpoch => 1000 * _value + _microsecond;
@patch
int get year => Primitives.getYear(this);
@patch
int get month => Primitives.getMonth(this);
@patch
int get day => Primitives.getDay(this);
@patch
int get hour => Primitives.getHours(this);
@patch
int get minute => Primitives.getMinutes(this);
@patch
int get second => Primitives.getSeconds(this);
@patch
int get millisecond => Primitives.getMilliseconds(this);
@patch
int get microsecond => _microsecond;
@patch
int get weekday => Primitives.getWeekday(this);
@patch
bool operator ==(Object other) =>
other is DateTime &&
millisecondsSinceEpoch == other.millisecondsSinceEpoch &&
microsecond == other.microsecond &&
isUtc == other.isUtc;
@patch
int get hashCode => Object.hash(_value, _microsecond);
@patch
bool isBefore(DateTime other) =>
millisecondsSinceEpoch < other.millisecondsSinceEpoch ||
millisecondsSinceEpoch == other.millisecondsSinceEpoch &&
microsecond < other.microsecond;
@patch
bool isAfter(DateTime other) =>
millisecondsSinceEpoch > other.millisecondsSinceEpoch ||
millisecondsSinceEpoch == other.millisecondsSinceEpoch &&
microsecond > other.microsecond;
@patch
bool isAtSameMomentAs(DateTime other) =>
millisecondsSinceEpoch == other.millisecondsSinceEpoch &&
microsecond == other.microsecond;
@patch
int compareTo(DateTime other) {
final r = millisecondsSinceEpoch.compareTo(other.millisecondsSinceEpoch);
if (r != 0) return r;
return microsecond.compareTo(other.microsecond);
}
}

View File

@ -29,21 +29,8 @@ class DateTime {
static const _MONTH_INDEX = 7;
static const _YEAR_INDEX = 8;
/// The value of this DateTime, equal to [microsecondsSinceEpoch].
final int _value;
List<int>? __parts;
/// Constructor for pre-validated components.
DateTime._(this._value, {required this.isUtc});
/// Constructs a new [DateTime] instance with the given value.
///
/// If [isUtc] is false, then the date is in the local time zone.
DateTime._withValue(this._value, {required this.isUtc}) {
_validate(millisecondsSinceEpoch, microsecond, isUtc);
}
@patch
DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
{bool isUtc = false})
@ -57,10 +44,7 @@ class DateTime {
{bool isUtc = false})
: this._withValue(microsecondsSinceEpoch, isUtc: isUtc);
static const _sentinel = -_maxMicrosecondsSinceEpoch - 1;
static const _sentinelConstraint = _sentinel < -_maxMicrosecondsSinceEpoch ||
_sentinel > _maxMicrosecondsSinceEpoch;
static const _sentinelAssertion = 1 ~/ (_sentinelConstraint ? 1 : 0);
static const _sentinelMs = -_maxMillisecondsSinceEpoch - 1;
@patch
DateTime._internal(int year, int month, int day, int hour, int minute,
@ -68,11 +52,8 @@ class DateTime {
: this.isUtc = checkNotNullable(isUtc, "isUtc"),
this._value = _brokenDownDateToValue(year, month, day, hour, minute,
second, millisecond, microsecond, isUtc) ??
_sentinel {
if (_value == _sentinel) {
throw ArgumentError('($year, $month, $day,'
' $hour, $minute, $second, $millisecond, $microsecond)');
}
_sentinelMs {
if (_value == _sentinelMs) throw new ArgumentError();
}
static int _validateMilliseconds(int millisecondsSinceEpoch) =>
@ -92,11 +73,6 @@ class DateTime {
: isUtc = true,
_value = _getCurrentMicros();
@patch
DateTime _withUtc({required bool isUtc}) {
return DateTime._(_value, isUtc: isUtc);
}
@patch
String get timeZoneName {
if (isUtc) return "UTC";
@ -105,9 +81,9 @@ class DateTime {
@patch
Duration get timeZoneOffset {
if (isUtc) return Duration();
if (isUtc) return new Duration();
int offsetInSeconds = _timeZoneOffsetInSeconds(microsecondsSinceEpoch);
return Duration(seconds: offsetInSeconds);
return new Duration(seconds: offsetInSeconds);
}
@patch
@ -116,9 +92,6 @@ class DateTime {
_value == other.microsecondsSinceEpoch &&
isUtc == other.isUtc;
@patch
int get hashCode => (_value ^ (_value >> 30)) & 0x3FFFFFFF;
@patch
bool isBefore(DateTime other) => _value < other.microsecondsSinceEpoch;
@ -133,11 +106,11 @@ class DateTime {
int compareTo(DateTime other) =>
_value.compareTo(other.microsecondsSinceEpoch);
/// The first list contains the days until each month in non-leap years. The
/// second list contains the days in leap years.
static const List<List<int>> _DAYS_UNTIL_MONTH = [
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
/** The first list contains the days until each month in non-leap years. The
* second list contains the days in leap years. */
static const 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]
];
static List<int> _computeUpperPart(int localMicros) {
@ -207,7 +180,7 @@ class DateTime {
DateTime.daysPerWeek) +
DateTime.monday;
List<int> list = List<int>.filled(_YEAR_INDEX + 1, 0);
List<int> list = new List<int>.filled(_YEAR_INDEX + 1, 0);
list[_MICROSECOND_INDEX] = resultMicrosecond;
list[_MILLISECOND_INDEX] = resultMillisecond;
list[_SECOND_INDEX] = resultSecond;
@ -226,22 +199,24 @@ class DateTime {
@patch
DateTime add(Duration duration) {
return DateTime._withValue(_value + duration.inMicroseconds, isUtc: isUtc);
return new DateTime._withValue(_value + duration.inMicroseconds,
isUtc: isUtc);
}
@patch
DateTime subtract(Duration duration) {
return DateTime._withValue(_value - duration.inMicroseconds, isUtc: isUtc);
return new DateTime._withValue(_value - duration.inMicroseconds,
isUtc: isUtc);
}
@patch
Duration difference(DateTime other) {
return Duration(microseconds: _value - other.microsecondsSinceEpoch);
return new Duration(microseconds: _value - other.microsecondsSinceEpoch);
}
@patch
int get millisecondsSinceEpoch =>
_flooredDivision(_value, Duration.microsecondsPerMillisecond);
_value ~/ Duration.microsecondsPerMillisecond;
@patch
int get microsecondsSinceEpoch => _value;
@ -273,19 +248,21 @@ class DateTime {
@patch
int get year => _parts[_YEAR_INDEX];
/// Returns the amount of microseconds in UTC that represent the same values
/// as this [DateTime].
///
/// Say `t` is the result of this function, then
/// * `this.year == new DateTime.fromMicrosecondsSinceEpoch(t, true).year`,
/// * `this.month == new DateTime.fromMicrosecondsSinceEpoch(t, true).month`,
/// * `this.day == new DateTime.fromMicrosecondsSinceEpoch(t, true).day`,
/// * `this.hour == new DateTime.fromMicrosecondsSinceEpoch(t, true).hour`,
/// * ...
///
/// Daylight savings is computed as if the date was computed in [1970..2037].
/// If this [DateTime] lies outside this range then a year with similar
/// properties (leap year, weekdays) is used instead.
/**
* Returns the amount of microseconds in UTC that represent the same values
* as this [DateTime].
*
* Say `t` is the result of this function, then
* * `this.year == new DateTime.fromMicrosecondsSinceEpoch(t, true).year`,
* * `this.month == new DateTime.fromMicrosecondsSinceEpoch(t, true).month`,
* * `this.day == new DateTime.fromMicrosecondsSinceEpoch(t, true).day`,
* * `this.hour == new DateTime.fromMicrosecondsSinceEpoch(t, true).hour`,
* * ...
*
* Daylight savings is computed as if the date was computed in [1970..2037].
* If this [DateTime] lies outside this range then it is a year with similar
* properties (leap year, weekdays) is used instead.
*/
int get _localDateInUtcMicros {
int micros = _value;
if (isUtc) return micros;
@ -313,6 +290,7 @@ class DateTime {
}
/// Converts the given broken down date to microseconds.
@patch
static int? _brokenDownDateToValue(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc) {
// Simplify calculations by working with zero-based month.
@ -360,26 +338,19 @@ class DateTime {
return microsecondsSinceEpoch;
}
@patch
static DateTime? _finishParse(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc) {
final value = _brokenDownDateToValue(year, month, day, hour, minute, second,
millisecond, microsecond, isUtc);
if (value == null) return null;
return DateTime._withValue(value, isUtc: isUtc);
}
static int _weekDay(y) {
// 1/1/1970 was a Thursday.
return (_dayFromYear(y) + 4) % 7;
}
/// Returns a year in the range 2008-2035 matching
/// * leap year, and
/// * week day of first day.
///
/// Leap seconds are ignored.
/// Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9.
/**
* Returns a year in the range 2008-2035 matching
* * leap year, and
* * week day of first day.
*
* Leap seconds are ignored.
* Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9.
*/
static int _equivalentYear(int year) {
// Returns year y so that _weekDay(y) == _weekDay(year).
// _weekDay returns the week day (in range 0 - 6).
@ -399,10 +370,12 @@ class DateTime {
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.
/**
* Returns the UTC year for the corresponding [secondsSinceEpoch].
* It is relatively fast for values in the range 0 to year 2098.
*
* Code is adapted from V8.
*/
static int _yearsFromSecondsSinceEpoch(int secondsSinceEpoch) {
const int DAYS_IN_4_YEARS = 4 * 365 + 1;
const int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1;
@ -417,16 +390,18 @@ class DateTime {
return _computeUpperPart(micros)[_YEAR_INDEX];
}
/// Returns a date in seconds that is equivalent to the given
/// date in microseconds [microsecondsSinceEpoch]. An equivalent
/// date has the same fields (`month`, `day`, etc.) as the given
/// date, but the `year` is in the range [1901..2038].
///
/// * The time since the beginning of the year is the same.
/// * If the given date is in a leap year then the returned
/// seconds are in a leap year, too.
/// * The week day of given date is the same as the one for the
/// returned date.
/**
* Returns a date in seconds that is equivalent to the given
* date in microseconds [microsecondsSinceEpoch]. An equivalent
* date has the same fields (`month`, `day`, etc.) as the given
* date, but the `year` is in the range [1901..2038].
*
* * The time since the beginning of the year is the same.
* * If the given date is in a leap year then the returned
* seconds are in a leap year, too.
* * The week day of given date is the same as the one for the
* returned date.
*/
static int _equivalentSeconds(int microsecondsSinceEpoch) {
const int CUT_OFF_SECONDS = 0x7FFFFFFF;

View File

@ -159,6 +159,13 @@ class DateTime implements Comparable<DateTime> {
static const int december = 12;
static const int monthsPerYear = 12;
/// The value of this DateTime.
///
/// The content of this field is implementation dependent. On JavaScript it is
/// equal to [millisecondsSinceEpoch]. On the VM it is equal to
/// [microsecondsSinceEpoch].
final int _value;
/// True if this [DateTime] is set to UTC time.
///
/// ```dart
@ -341,12 +348,12 @@ class DateTime implements Comparable<DateTime> {
minute -= sign * minuteDifference;
}
}
DateTime? result = _finishParse(years, month, day, hour, minute, second,
millisecond, microsecond, isUtc);
if (result == null) {
int? value = _brokenDownDateToValue(years, month, day, hour, minute,
second, millisecond, microsecond, isUtc);
if (value == null) {
throw FormatException("Time out of range", formattedString);
}
return result;
return DateTime._withValue(value, isUtc: isUtc);
} else {
throw FormatException("Invalid date format", formattedString);
}
@ -366,8 +373,6 @@ class DateTime implements Comparable<DateTime> {
}
static const int _maxMillisecondsSinceEpoch = 8640000000000000;
static const int _maxMicrosecondsSinceEpoch =
_maxMillisecondsSinceEpoch * Duration.microsecondsPerMillisecond;
/// Constructs a new [DateTime] instance
/// with the given [millisecondsSinceEpoch].
@ -401,33 +406,18 @@ class DateTime implements Comparable<DateTime> {
external DateTime.fromMicrosecondsSinceEpoch(int microsecondsSinceEpoch,
{bool isUtc = false});
/// Throws an error if the millisecondsSinceEpoch and microsecond components
/// are out of range.
/// Constructs a new [DateTime] instance with the given value.
///
/// Returns the millisecondsSinceEpoch component.
static int _validate(
int millisecondsSinceEpoch, int microsecond, bool isUtc) {
if (microsecond < 0 || microsecond > 999) {
throw RangeError.range(microsecond, 0, 999, "microsecond");
/// If [isUtc] is false, then the date is in the local time zone.
DateTime._withValue(this._value, {required this.isUtc}) {
if (millisecondsSinceEpoch.abs() > _maxMillisecondsSinceEpoch ||
(millisecondsSinceEpoch.abs() == _maxMillisecondsSinceEpoch &&
microsecond != 0)) {
throw ArgumentError(
"DateTime is outside valid range: $millisecondsSinceEpoch");
}
if (millisecondsSinceEpoch < -_maxMillisecondsSinceEpoch ||
millisecondsSinceEpoch > _maxMillisecondsSinceEpoch) {
throw RangeError.range(
millisecondsSinceEpoch,
-_maxMillisecondsSinceEpoch,
_maxMillisecondsSinceEpoch,
"millisecondsSinceEpoch");
}
if (millisecondsSinceEpoch == _maxMillisecondsSinceEpoch &&
microsecond != 0) {
throw ArgumentError.value(microsecond, "microsecond",
"Time including microseconds is outside valid range");
}
// For backwards compatibility with legacy mode.
checkNotNullable(isUtc, "isUtc");
return millisecondsSinceEpoch;
}
/// Whether [other] is a [DateTime] at the same moment and in the
@ -446,8 +436,6 @@ class DateTime implements Comparable<DateTime> {
/// independently of their zones.
external bool operator ==(Object other);
external int get hashCode;
/// Whether this [DateTime] occurs before [other].
///
/// The comparison is independent
@ -528,6 +516,8 @@ class DateTime implements Comparable<DateTime> {
/// ```
external int compareTo(DateTime other);
int get hashCode => (_value ^ (_value >> 30)) & 0x3FFFFFFF;
/// Returns this DateTime value in the local time zone.
///
/// Returns this [DateTime] if it is already in the local time zone.
@ -539,7 +529,7 @@ class DateTime implements Comparable<DateTime> {
/// ```
DateTime toLocal() {
if (isUtc) {
return _withUtc(isUtc: false);
return DateTime._withValue(_value, isUtc: false);
}
return this;
}
@ -555,11 +545,9 @@ class DateTime implements Comparable<DateTime> {
/// ```
DateTime toUtc() {
if (isUtc) return this;
return _withUtc(isUtc: true);
return DateTime._withValue(_value, isUtc: true);
}
external DateTime _withUtc({required bool isUtc});
static String _fourDigits(int n) {
int absN = n.abs();
String sign = n < 0 ? "-" : "";
@ -724,10 +712,18 @@ class DateTime implements Comparable<DateTime> {
external DateTime._now();
/// Returns the [DateTime] corresponding to the given components, or `null` if
/// the values are out of range.
external static DateTime? _finishParse(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc);
/// Returns the time as value (millisecond or microsecond since epoch), or
/// null if the values are out of range.
external static int? _brokenDownDateToValue(
int year,
int month,
int day,
int hour,
int minute,
int second,
int millisecond,
int microsecond,
bool isUtc);
/// The number of milliseconds since
/// the "Unix epoch" 1970-01-01T00:00:00Z (UTC).
@ -748,10 +744,8 @@ class DateTime implements Comparable<DateTime> {
/// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch.
/// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`.
///
/// Note that this value does not always fit into 53 bits (the size of a IEEE
/// double). On the web JavaScript platforms, there may be a rounding error
/// for DateTime values sufficiently far from the epoch. The year range close
/// to the epoch to avoid rounding is approximately 1685..2254.
/// Note that this value does not fit into 53 bits (the size of a IEEE double).
/// A JavaScript number is not able to hold this value.
external int get microsecondsSinceEpoch;
/// The time zone name.

View File

@ -390,10 +390,7 @@
},
"core": {
"uri": "core/core.dart",
"patches": [
"_internal/js_shared/lib/date_time_patch.dart",
"_internal/js_runtime/lib/core_patch.dart"
]
"patches": "_internal/js_runtime/lib/core_patch.dart"
},
"developer": {
"uri": "developer/developer.dart",
@ -573,10 +570,7 @@
},
"core": {
"uri": "core/core.dart",
"patches": [
"_internal/js_shared/lib/date_time_patch.dart",
"_internal/js_dev_runtime/patch/core_patch.dart"
]
"patches": "_internal/js_dev_runtime/patch/core_patch.dart"
},
"developer": {
"uri": "developer/developer.dart",

View File

@ -319,9 +319,7 @@ _dart2js_common:
core:
uri: "core/core.dart"
patches:
- "_internal/js_shared/lib/date_time_patch.dart"
- "_internal/js_runtime/lib/core_patch.dart"
patches: "_internal/js_runtime/lib/core_patch.dart"
developer:
uri: "developer/developer.dart"
@ -497,9 +495,7 @@ dartdevc:
core:
uri: "core/core.dart"
patches:
- "_internal/js_shared/lib/date_time_patch.dart"
- "_internal/js_dev_runtime/patch/core_patch.dart"
patches: "_internal/js_dev_runtime/patch/core_patch.dart"
developer:
uri: "developer/developer.dart"

View File

@ -7,13 +7,14 @@ import "package:expect/expect.dart";
// Dart test program for DateTime, extreme values.
bool get supportsMicroseconds =>
DateTime.fromMicrosecondsSinceEpoch(1).microsecondsSinceEpoch == 1;
new DateTime.fromMicrosecondsSinceEpoch(1).microsecondsSinceEpoch == 1;
// Identical to _maxMillisecondsSinceEpoch in date_time.dart
const int _MAX_MILLISECONDS = 8640000000000000;
void testExtremes() {
var dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
var dt =
new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
Expect.equals(275760, dt.year);
Expect.equals(9, dt.month);
Expect.equals(13, dt.day);
@ -22,7 +23,7 @@ void testExtremes() {
Expect.equals(0, dt.second);
Expect.equals(0, dt.millisecond);
Expect.equals(0, dt.microsecond);
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
Expect.equals(-271821, dt.year);
Expect.equals(4, dt.month);
Expect.equals(20, dt.day);
@ -32,103 +33,71 @@ void testExtremes() {
Expect.equals(0, dt.millisecond);
Expect.equals(0, dt.microsecond);
// Make sure that we can build the extreme dates in local too.
dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
dt = DateTime(
dt = new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
dt = new DateTime(
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.millisecond);
Expect.equals(_MAX_MILLISECONDS, dt.millisecondsSinceEpoch);
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
dt = DateTime(
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
dt = new DateTime(
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.millisecond);
Expect.equals(-_MAX_MILLISECONDS, dt.millisecondsSinceEpoch);
Expect.throws(() => new DateTime.fromMillisecondsSinceEpoch(
_MAX_MILLISECONDS + 1,
isUtc: true));
Expect.throws(() => new DateTime.fromMillisecondsSinceEpoch(
-_MAX_MILLISECONDS - 1,
isUtc: true));
Expect.throws(
() => new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS + 1));
Expect.throws(
() => new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS - 1));
dt = new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
Expect.throws(
() => new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 1));
dt = new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
Expect.throws(() =>
DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS + 1, isUtc: true));
new DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 1));
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
Expect.throws(
() => new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, -1));
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
Expect.throws(() =>
DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS - 1, isUtc: true));
Expect.throws(
() => DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS + 1));
Expect.throws(
() => DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS - 1));
dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
Expect.throws(
() => DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 1));
dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
Expect.throws(
() => DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 1));
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
Expect.throws(
() => DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, -1));
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
Expect.throws(
() => DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, -1));
new DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, -1));
if (!supportsMicroseconds) return;
/// The nearest value to [base] in the direction [delta]. For native `int`s,
/// this is just `base + delta`. For web `int`s outside the safe range, the
/// next value might differ by some power of two.
int nearest(int base, int delta) {
for (int factor = 1;; factor *= 2) {
final next = base + delta * factor;
print(factor);
if (next != base) return next;
}
}
dt = DateTime.fromMicrosecondsSinceEpoch(_MAX_MILLISECONDS * 1000);
dt = DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
dt = new DateTime.fromMicrosecondsSinceEpoch(_MAX_MILLISECONDS * 1000);
dt = new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute);
Expect.equals(_MAX_MILLISECONDS * 1000, dt.microsecondsSinceEpoch);
print(-_MAX_MILLISECONDS * 1000);
dt = DateTime.fromMicrosecondsSinceEpoch(-_MAX_MILLISECONDS * 1000);
dt = DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
dt = new DateTime.fromMicrosecondsSinceEpoch(-_MAX_MILLISECONDS * 1000);
dt = new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute);
Expect.equals(-_MAX_MILLISECONDS * 1000, dt.microsecondsSinceEpoch);
Expect.throws(() => DateTime.fromMicrosecondsSinceEpoch(
nearest(_MAX_MILLISECONDS * 1000, 1),
Expect.throws(() => new DateTime.fromMicrosecondsSinceEpoch(
_MAX_MILLISECONDS * 1000 + 1,
isUtc: true));
Expect.throws(() => DateTime.fromMicrosecondsSinceEpoch(
nearest(-_MAX_MILLISECONDS * 1000, -1),
Expect.throws(() => new DateTime.fromMicrosecondsSinceEpoch(
-_MAX_MILLISECONDS * 1000 - 1,
isUtc: true));
Expect.throws(() => DateTime.fromMicrosecondsSinceEpoch(
nearest(_MAX_MILLISECONDS * 1000, 1)));
Expect.throws(() => DateTime.fromMicrosecondsSinceEpoch(
nearest(-_MAX_MILLISECONDS * 1000, -1)));
// These should all succeed - stepping into the valid range rather than out:
DateTime.fromMicrosecondsSinceEpoch(nearest(-_MAX_MILLISECONDS * 1000, 1),
isUtc: true);
DateTime.fromMicrosecondsSinceEpoch(nearest(_MAX_MILLISECONDS * 1000, -1),
isUtc: true);
DateTime.fromMicrosecondsSinceEpoch(nearest(-_MAX_MILLISECONDS * 1000, 1));
DateTime.fromMicrosecondsSinceEpoch(nearest(_MAX_MILLISECONDS * 1000, -1));
dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
Expect.throws(
() => DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, 1));
Expect.throws(() => dt.copyWith(microsecond: 1));
Expect.isTrue(dt.copyWith(microsecond: -1).toString().endsWith('.999999'));
dt = DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
Expect.throws(() =>
DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, 1));
Expect.throws(() => dt.copyWith(microsecond: 1));
Expect.isTrue(dt.copyWith(microsecond: -1).toString().endsWith('.999999Z'));
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
Expect.throws(
() => DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, -1));
Expect.throws(() => dt.copyWith(microsecond: -1));
Expect.isTrue(dt.copyWith(microsecond: 1).toString().endsWith('.000001'));
dt = DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
new DateTime.fromMicrosecondsSinceEpoch(_MAX_MILLISECONDS * 1000 + 1));
Expect.throws(() =>
DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, -1));
Expect.throws(() => dt.copyWith(microsecond: -1));
Expect.isTrue(dt.copyWith(microsecond: 1).toString().endsWith('.000001Z'));
new DateTime.fromMicrosecondsSinceEpoch(-_MAX_MILLISECONDS * 1000 - 1));
dt = new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS);
Expect.throws(() =>
new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, 1));
dt = new DateTime.fromMillisecondsSinceEpoch(_MAX_MILLISECONDS, isUtc: true);
Expect.throws(() =>
new DateTime.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, 1));
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS);
Expect.throws(() =>
new DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, -1));
dt = new DateTime.fromMillisecondsSinceEpoch(-_MAX_MILLISECONDS, isUtc: true);
Expect.throws(() => new DateTime.utc(
dt.year, dt.month, dt.day, dt.hour, dt.minute, 0, 0, -1));
// Regression test for https://dartbug.com/55438
dt = DateTime.utc(1969, 12, 31, 23, 59, 59, 999, 999);
Expect.equals(-1, dt.microsecondsSinceEpoch);
// The first fix confused millisecondsSinceEpoch and microsecondsSinceEpoch.
dt = DateTime.utc(1696, 3, 16, 23, 59, 59, 999, 999);
Expect.equals(-_MAX_MILLISECONDS - 1, dt.microsecondsSinceEpoch);
}
void main() {