Skip to content

Commit

Permalink
Revert "[js_runtime, js_dev_runtime] Implement microsecond field of…
Browse files Browse the repository at this point in the history
… `DataTime`"

This reverts commit fb057ea.

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 <[email protected]>
> Reviewed-by: Martin Kustermann <[email protected]>
> Reviewed-by: Lasse Nielsen <[email protected]>
> Commit-Queue: Stephen Adams <[email protected]>

Change-Id: I58572256a7710df4589bb5e41c7afee295c2388b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368103
Reviewed-by: Alexander Thomas <[email protected]>
Reviewed-by: Martin Kustermann <[email protected]>
Auto-Submit: Ivan Inozemtsev <[email protected]>
Bot-Commit: Rubber Stamper <[email protected]>
  • Loading branch information
iinozemtsev authored and athomas committed May 27, 2024
1 parent a3f83a9 commit 72b2883
Show file tree
Hide file tree
Showing 12 changed files with 472 additions and 464 deletions.
14 changes: 0 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
141 changes: 141 additions & 0 deletions sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 19 additions & 8 deletions sdk/lib/_internal/js_dev_runtime/private/js_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down
145 changes: 145 additions & 0 deletions sdk/lib/_internal/js_runtime/lib/core_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 72b2883

Please sign in to comment.