mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:49:17 +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:
parent
a3f83a9e44
commit
72b2883c6f
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue