-
Notifications
You must be signed in to change notification settings - Fork 152
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
Instant vs ZonedInstant #42
Comments
var i = new Instant(1514764800000);
var zi = i.withZone('UTC');
var d = zi.toCivilDate(); // 2018-01-01 vs var i = new Instant(1514764800000);
var d = i.toCivilDate(); // 2018-01-01 Ok, that makes sense, but is that always what we want to do? var i = new Instant(1514764800000);
var zi = i.withZone('America/Los_Angeles');
var d = zi.toCivilDate(); // 2017-12-31 Without an interim object, we'd have two choices:
So assuming you meant the second option, the downside is that two var zi1 = new ZonedInstant(1514764800000, 'UTC'); // "2018-01-01T00:00:00Z"
var zi2 = new ZonedInstant(1514764800000, 'America/Los_Angeles'); // "2017-12-31T16:00:00-08:00" How does one equate or compare these two objects? With a separate |
Also, WRT option 1, I'd say that is not acceptable because it prohibits retaining time zone or offset information in the result of parsing a string that might contain such information. |
We could prohibit comparison operations on ZonedInstance whenever the zone isn't "UTC" just as easily, could we not? |
API-wise, instead of a I would prefer b below: // a)
let i = new Instant(1514764800000);
let zi = new ZonedInstant(1514764800000, 'America/Los_Angeles');
// b)
let i = new Instant(1514764800000);
let zi = new Instant(1514764800000, {timeZone: 'America/Los_Angeles'}); |
@mj1856 can you show me an example how to use temporal to check if date A in timezone X is equal to date B in timezone Y? For example, how to assert that 1:10PM in America/Los_Angeles means the same instant that 5:10PM in America/Sao_Paulo (both if we consider Jul 27, 2017 as date)? |
@rxaviers For that specific comparison, you'd want |
I would say that two |
@apaprocki to be clear, you wouldn't use |
Note that All below are true:
|
Let's not operator overload :-) |
@mj1856 how do I get a zoned date in the user's environment date please? (sorry if this was already discussed somewhere else) |
What two options did I give? |
Or did you mean that there would only be |
@mj1856 that seems much more reasonable to me than having two "types". |
@mj1856 yes, your last comment :); specially depending on your answer for #42 (comment) |
There's massive value to the code clarity you get in differentiating between a point on the global timeline with no context, and a point for which we have local context. I'll have to come up with some larger code samples to clarify. |
@maggiepint since timezone and "type" would only be present when constructing the instance (there's no type annotations elsewhere that would be relevant), I'd love to see the code examples for that. |
@maggiepint me too, thanks. |
As a strawman, you could replace const instant = new Instant(1514764800000);
const timeZone = new TimeZone('America/Los_Angeles');
// whichever you prefer, or both:
const civilDateFromTimeZone = timeZone.toCivilDate(instant); // 2017-12-31
const civilDateFromInstant = instant.toCivilDate(timeZone); // 2017-12-31 Of course, One problem with this would be than you can't directly I'm guessing const format = new Intl.DateTimeFormat('en-AU', {
hour: 'numeric', minute: 'numeric', second: 'numeric',
timeZone: 'Australia/Sydney',
timeZoneName: 'short'
});
new Instant(1514764800000).toString(format); '11:00:00 AM AEDT' I'm pretty sure you wouldn't want to combine the other format data into a I mention this largely because it's a bit of a weird concept for me to have a combined instant in time and a time zone: in my head time zones are what you use to convert between instants and local date and times! |
Just to keep track... On #84 (comment), @gibson042 said:
|
In terms of API, would it make sense to consider something like the below as an alternative to having two types? const instant = new Instant(0);
instant.toString(); // "1970-01-01T00:00:00.000000000Z"
instant.timeZone; // null
instant instanceof Instant; // true
// Equivalent to new Instant(0, {timeZone: "UTC"});
const utcInstant = instant.withZone('UTC');
utcInstant.toString(); // "1970-01-01T00:00:00.000000000+00:00"
utcInstant.timeZone; // "UTC"
utcInstant instanceof Instant; // true With respect to comparison, it could throw if comparing |
Why would ducktyping the value of a property be a better design than two distinct types, when "the zone doesn't matter" is a distinct thing from "it has a zone"? |
Because in practice both are so similar that is simpler to have one type? It would be good if @maggiepint could proceed with her examples #42 (comment) |
It may be simpler from a technical perspective, but I think it's more complex from a mental model perspective, because people don't tend to think that a fundamental semantic of an object is represented by the value of a single property on it (and when they do, bugs often abound). |
Time value is certainly fundamental to the model of an instant, but is time zone or even UTC offset? They're arguably more like decoration. Time zone can obviously influence comparison class methods, but in my opinion should have no effects on the operation of I'm open to persuasion on the merits of a separate class, but find the code samples currently in this issue to be unconvincing. What is an example that would be more difficult, complex, or confusing with zone-optional Instant as opposed to zoneless Instant and zone-mandatory ZonedInstant? |
const zi1 = ZonedInstant.fromString("2018‑01-01T00:00:00-08:00[America/Los_Angeles]");
const zi2 = ZonedInstant.fromString("2018‑01-01T02:00:00-05:00[America/New_York]"); By their instants, I tend to agree that if we have to choose one or the other, we chose the instant. That is one of the benefits of calling it a "zoned instant". However there are cases that are less clear: const zi3 = ZonedInstant.fromString("2018‑01-01T00:00:00-08:00[America/Los_Angeles]");
const zi4 = ZonedInstant.fromString("2018‑01-01T03:00:00-05:00[America/New_York]"); Both |
I think the main use case of What if we dropped the let chicagoZone = new Zone('America/Chicago')
let sydneyZone = new Zone('Australia/Sydney')
let dateTimeInChicago = new CivilDateTime(2000, 12, 31, 23, 59)
let instant = chicagoZone.instantAt(dateTimeInChicago)
let dateTimeInSydney = sydneyZone.dateTimeAt(instant)
dateTimeInChicago.toString() // 2000-12-31T23:59:00.000000000
dateTimeInSydney.toString() // 2001-01-01T16:59:00.000000000 |
"Simpler mental model perspective" - this is based on preference, what could be simpler for you, could be confusing for me. 🤷♂️ - @ljharb sorry, I won't debate preferences |
@timrwood, thanks for the example, but it's not clear to me how it helps define the need for both classes (instead of one). Using today's spec your example would be written as the below, right? let dateTime = new CivilDateTime(2000, 12, 31, 23, 59);
// Instant considering dateTime is based in Chicago:
let instant = dateTime.withZone('America/Chicago');
let dateTimeInSydney = instant.withZone('Australia/Sydney').toLocalDateTime();
// ... or alternatively:
// let dateTimeInSydney = new ZonedInstant(instant, 'Australia/Sydney').toCivilDateTime();
dateTime.toString(); // "2000-12-31T23:59:00.000000000"
dateTimeInSydney.toString(); // "2001-01-01T16:59:00.000000000" If instead we follow the single class proposal above, nothing changes... |
@timrwood one of the aims I have for these Objects is that they are neutral in terms of culture. (i.e. methods specific to a calendar do not belong on CivilDateTime (et al) are specific to a calendar and way of time-keeping. As such, I don't think The consequence is that:
That means it would mean for your proposal:
|
At a certain point, a lot of this becomes subjective. For example, the difference that @timrwood describes is similar to the difference between JSR310 and Time4J. Both models work, it's really just about which model we want to bake in to the language. We can make objective statements about pros and cons of each model, but we shouldn't be trying to figure out which one is more correct than the other. That would be futile. |
(More on this difference in the Time4J docs) |
@rxaviers, you seem to have missed the point of my comment, sorry. I don't care what the name is except for what it implies about the concept, and I think the concept of "instant + time zone" is wrong, which is what is causing the disagreement in this thread. My suggestion was to say "do I still want to compare by point in time if this type doesn't have instant in the name?" Given your example above, I would not expect Does that make it clearer? |
@simonbuchan thanks for the clarification. Therefore, I understand you're challenging where "timezone" should be: In |
@simonbuchan - Would you please restate your specific objection with "instant + time zone" is wrong? I think I'm following you, just want to be super clear. Thanks. |
@timrwood and @simonbuchan have pitched similar proposals here I'm not completely opposed, but the questions I have with that model are:
|
Passing things around as a string is harder to validate (both for correctness of the data, and correctness of "this is the right kind of thing"), harder to type for those using a type system, and harder to reason about. |
To my mind, the debate on whether we need At the end of the day, there is a lot of gravity toward a |
I will note tho; if we had a |
While a We're not talking about having a variety of "zoned" objects. There is only one, and it is currently |
A TimeZone object would be capable of normalizing input and/or providing validation in a way that strings would not, without also needing to create an arbitrary date and/or time to associate with it (and implicitly get the zone validation in the process). |
Sure. It would give abilities like: TimeZone.isValid('America/New_York') // true
TimeZone.isValid('America/Seattle') // false
TimeZone.isValid('-12:34') // true
TimeZone.validTimeZoneIds // ['SYSTEM', 'UTC', 'America/New_York', 'Asia/Tokyo', ...] However, the last one is interesting because we are also allowing arbitrary time zone offsets like shown above it, and those wouldn't be in the list. Either way, I think we can work on this for stage 3. It certainly could be a standalone object - it doesn't have to be passed as parameter. In other words, const zdt = new ZonedDateTime('America/New_York', 2018, 1, 2, 3, 4, 5);
vs
const zdt = new ZonedDateTime(new TimeZone('America/New_York'), 2018, 1, 2, 3, 4, 5); even if we have a |
val = 1538058600000000n;
unzoned = new Instant(val);
zoned1 = new ZonedInstant(val, 'Pacific/Auckland');
zoned2 = new ZonedInstant(val, 'America/New_York');
[
zoned1 > unzoned,
zoned1 < unzoned,
zoned2 > unzoned,
zoned2 < unzoned,
zoned1 > zoned2,
zoned1 < zoned2
];
// → [?, ?, ?, ?, ?, ?] |
We are removing |
@mj1856 since toString will still be there, (agreed that even with a TimeZone object, we'd want to accept strings or TimeZone objects anywhere in the API that took either) |
Except that a) string comparison is only useful for comparing temporal instants that use exactly the same time zone designator, and b) abstract relational comparison strongly prefers numbers anyway ( |
The hint isn't helpful unless we make |
@ljharb - Thanks for clarifying that point. So if |
|
I opened #92 for discussion around relational comparison, even though it is somewhat intertwined with this discussion. |
For better or worse, things seem to have stabilized around keeping ZonedInstant a distinct class from Instant (and also adding one for TimeZone). We can reopen or start a new issue if someone wants to raise this banner again. |
I asked in the meeting about this, and got an explanation, but wanted to discuss more offline.
Since ZonedInstant has a potential timezone of "UTC", it seems to be that anything you want to require "Instant" for, you can also require "ZonedInstance with a UTC timezone" for - it feels very "strongly typed language" to me to have disparate types for something that's otherwise equivalent.
Is there a reason we couldn't halve the number of globals, and condense Instant and ZonedInstant into the same name?
The text was updated successfully, but these errors were encountered: