Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DateTime class relies on private members of other objects #32051

Closed
srawlins opened this issue Feb 5, 2018 · 7 comments
Closed

DateTime class relies on private members of other objects #32051

srawlins opened this issue Feb 5, 2018 · 7 comments
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-core

Comments

@srawlins
Copy link
Member

srawlins commented Feb 5, 2018

DateTime has a few methods that rely on a private member of objects:

bool isAfter(DateTime other) {
  return _value > other._value;
}

other is not guaranteed to be of a class declared in dart:core, so it's _value may be inaccessible.

In practice, this means that DateTime is not implementable, a problem for the timezone package:

new DateTime.now().isAfter(new TZDateTime.now(detroit))

throws trying to call _value on the TZDateTime object. The docs for _value read:

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].

I think the best solution is to make a public getter, value for _value. Especially when TZDateTime is used to wrap a native DateTime, and should set its own _value to the native DateTime's _value, so that it doesn't have to use a hack like if (identical(1, 1.0) to make up a value.

@srawlins srawlins changed the title DateTime class relies on private methods of other objects DateTime class relies on private members of other objects Feb 5, 2018
@floitschG floitschG added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-core labels Feb 6, 2018
@floitschG
Copy link
Contributor

The publicly accessible members for _value are millisecondsSinceEpoch and microSecondsSinceEpoch. I vote against making value publicly accessible.

However, isAfter (et al) could use those getters instead of accessing _value directly.

@srawlins
Copy link
Member Author

srawlins commented Feb 6, 2018

I see. So all of the methods that access the _value of an other object now should become external in lib/core/date_time.dart, and their implementations will be moved to the VM/dart2js patches which will know to use either millisecondsSinceEpoch or microSecondsSinceEpoch. This amounts to:

  • operator ==
  • isBefore
  • isAfter
  • isAtSameMomentAs
  • compareTo

Any code that implements DateTime can use if (identical(1, 1.0) to implement their own _value, for example for the purposes of hashCode, toUtc, etc. (Perhaps an implementation doesn't need to use the same _value for these methods, and so doesn't need to use if (identical(1, 1.0), but the trick is available if they do.)

@lrhn
Copy link
Member

lrhn commented Feb 6, 2018

No need to go external, just change isAfter to be => microsecondsSinceEpoch > other.microsecondsSinceEpoch, and similarly for the rest. That would be correct and will work when combined with other implementations of the interface that do implement microseconds, even if the platform DateTime implementation uses only microsecond granularity.

Specifying hashCode is always problematic. If you want an interface to be implementable, you need to say how the hashCode is computed, but if you do so, you might pick a variant that is expensive or bad-at-hashing for some implementation (say, using microsecondsSinceEpoch & 0x3FFFFFFF might not be great if it only has millisecond precision, then the three lower bits will always be zero).
We haven't specified the hashCode yet, so that's still in the air.

@srawlins
Copy link
Member Author

srawlins commented Feb 6, 2018

Ah, good idea, @lrhn. Two questions before I send a patch:

  • microsecondsSinceEpoch is not slower for being a getter?

  • microsecondsSinceEpoch doc notes:

    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.

    Does this not get in the way?

@lrhn
Copy link
Member

lrhn commented Feb 6, 2018

It probably does get in the way, but if the compare must be ready to support non-platform DateTime methods, it needs to account for microseconds some way. Maybe it's better to do:

bool isAfter(DateTime other) { 
   var ms = this.millisecondsSinceEpoch;
   var oms = other.millisecondsSinceEpoch;
   return ms > oms || (ms == oms && this.microseconds > other.microseconds);
}

but the implementation must check the microseconds of the other.

And sure, if we can be more efficient by doing it differently on different platforms, we can go the external way and have independent implementations, but doubling the code to maintain should only be done when we can see that there is an actual need for the performance.

@lrhn
Copy link
Member

lrhn commented Sep 30, 2020

DateTime no longer depends on _value of other objects.

@lrhn lrhn closed this as completed Sep 30, 2020
@srawlins
Copy link
Member Author

Thanks much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-core
Projects
None yet
Development

No branches or pull requests

3 participants