From 72b2883c6fa78d32776b753abd22e60cd25132b1 Mon Sep 17 00:00:00 2001 From: Ivan Inozemtsev Date: Mon, 27 May 2024 07:54:14 +0000 Subject: [PATCH] Revert "[js_runtime, js_dev_runtime] Implement `microsecond` field of `DataTime`" This reverts commit fb057ea4e07de2af624688455925c0ca07afe04f. 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 > Reviewed-by: Martin Kustermann > Reviewed-by: Lasse Nielsen > Commit-Queue: Stephen Adams Change-Id: I58572256a7710df4589bb5e41c7afee295c2388b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368103 Reviewed-by: Alexander Thomas Reviewed-by: Martin Kustermann Auto-Submit: Ivan Inozemtsev Bot-Commit: Rubber Stamper --- CHANGELOG.md | 14 -- .../deferred_loading/data/lazy_types/lib.dart | 2 +- .../js_dev_runtime/patch/core_patch.dart | 141 +++++++++++++ .../js_dev_runtime/private/js_helper.dart | 27 ++- .../_internal/js_runtime/lib/core_patch.dart | 145 +++++++++++++ .../_internal/js_runtime/lib/js_helper.dart | 44 ++-- .../js_shared/lib/date_time_patch.dart | 195 ------------------ .../_internal/vm_shared/lib/date_patch.dart | 143 ++++++------- sdk/lib/core/date_time.dart | 80 ++++--- sdk/lib/libraries.json | 10 +- sdk/lib/libraries.yaml | 8 +- tests/corelib/date_time_extremes_test.dart | 131 +++++------- 12 files changed, 474 insertions(+), 466 deletions(-) delete mode 100644 sdk/lib/_internal/js_shared/lib/date_time_patch.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc5cfc4632..78ba51e36a7 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart index f028c43522e..751b18c5b0e 100644 --- a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart +++ b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart @@ -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') diff --git a/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart index 65cc146b9a5..7c391788d81 100644 --- a/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart +++ b/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart @@ -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 { diff --git a/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart index 6e7c3634b8f..2c1c1a0489c 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart @@ -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('!', 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('!', r'new Date(#)', value); + if (isUtc) { + JS('!', r'#.setUTCFullYear(#)', date, years); + } else { + JS('!', r'#.setFullYear(#)', date, years); + } + return JS('!', r'#.valueOf()', date); + } + // Lazily keep a JS Date stored in the JS object. static lazyAsJsDate(DateTime receiver) { if (JS('!', 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); diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart index d7aa5f82bbd..db471fb225e 100644 --- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart @@ -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 { diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index 2359738a289..1a04a339594 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -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); diff --git a/sdk/lib/_internal/js_shared/lib/date_time_patch.dart b/sdk/lib/_internal/js_shared/lib/date_time_patch.dart deleted file mode 100644 index ecba7409489..00000000000 --- a/sdk/lib/_internal/js_shared/lib/date_time_patch.dart +++ /dev/null @@ -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); - } -} diff --git a/sdk/lib/_internal/vm_shared/lib/date_patch.dart b/sdk/lib/_internal/vm_shared/lib/date_patch.dart index 1f0686724cc..b1fd03db8f3 100644 --- a/sdk/lib/_internal/vm_shared/lib/date_patch.dart +++ b/sdk/lib/_internal/vm_shared/lib/date_patch.dart @@ -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? __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> _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> _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 _computeUpperPart(int localMicros) { @@ -207,7 +180,7 @@ class DateTime { DateTime.daysPerWeek) + DateTime.monday; - List list = List.filled(_YEAR_INDEX + 1, 0); + List list = new List.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; diff --git a/sdk/lib/core/date_time.dart b/sdk/lib/core/date_time.dart index 6c53f7f4e99..366995cf813 100644 --- a/sdk/lib/core/date_time.dart +++ b/sdk/lib/core/date_time.dart @@ -159,6 +159,13 @@ class DateTime implements Comparable { 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 { 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 { } 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 { 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 { /// 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 { /// ``` 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 toLocal() { if (isUtc) { - return _withUtc(isUtc: false); + return DateTime._withValue(_value, isUtc: false); } return this; } @@ -555,11 +545,9 @@ class DateTime implements Comparable { /// ``` 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 { 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 { /// 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. diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json index a00925f6058..56265a24143 100644 --- a/sdk/lib/libraries.json +++ b/sdk/lib/libraries.json @@ -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", diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml index e843722cc1a..068e1b287f4 100644 --- a/sdk/lib/libraries.yaml +++ b/sdk/lib/libraries.yaml @@ -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" diff --git a/tests/corelib/date_time_extremes_test.dart b/tests/corelib/date_time_extremes_test.dart index ad5316fc30b..57facb12402 100644 --- a/tests/corelib/date_time_extremes_test.dart +++ b/tests/corelib/date_time_extremes_test.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() {