-
Notifications
You must be signed in to change notification settings - Fork 158
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
Draft of custom time zone API #498
Conversation
Codecov Report
@@ Coverage Diff @@
## main #498 +/- ##
==========================================
+ Coverage 96.83% 97.56% +0.72%
==========================================
Files 17 17
Lines 3955 3986 +31
Branches 582 583 +1
==========================================
+ Hits 3830 3889 +59
+ Misses 123 95 -28
Partials 2 2
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
> **FIXME:** This means we have to remove any checks for the _[[InitializedTemporalTimeZone]]_ slot in any APIs outside of `Temporal.TimeZone`. | ||
|
||
> **FIXME:** `Temporal.TimeZone` is supposed to be an iterable through all time zones known to the implementation, but what do we do about custom time zones there? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should decide how to do this in #294 for calendars and apply the same solution here.
fbf0b29
to
548402c
Compare
docs/timezone-draft.md
Outdated
|
||
/** Given an absolute instant returns this time zone's corresponding | ||
* UTC offset, in nanoseconds (signed). */ | ||
getOffsetAtInstant(absolute : Temporal.Absolute) : bigint; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should return a Temporal.Absolute, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it could, but this is conceptually more of a duration than an absolute point on the timeline. I only didn't use Temporal.Duration because it can't represent a sign, and then I'd have to return a pair of { sign, duration }
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, it's an offset from UTC, not nanos since epoch. Never mind then; we should use a numeric type. It's an interesting question about whether to use bigint or number. Maybe open a thread to answer that outside of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fairly minor and easy to change if we change our minds — I'll just change it to a number for now. The allowed offsets range from -24:00 to +24:00, not including endpoints, and 86400e9 is less than Number.MAX_SAFE_INTEGER
, so a bigint doesn't seem necessary.
7510f28
to
8bfd98d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it would be valuable to list some of the more compelling use cases here. I'm not entirely sure that "maybe the time zone database is out of date" is sufficient reason for the complexity.
/** Given the calendar/wall-clock time, returns an array of 0, 1, or | ||
* 2 absolute instants that are possible points on the timeline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to enforce the maximum of two elements?
Do we want to enforce this returns an array, rather than an iterable?
Should this comment say something about how we'd pick one of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to enforce the maximum of two elements?
I don't know, I was wondering if it would theoretically be possible for a time zone to decide to have two DST transitions back-to-back so that a DateTime was repeated three times. That wouldn't be handled by getAbsoluteFor, since the disambiguation parameter only takes "earlier" or "later" into account, but possibleInstants could be used by user code to deal with that case.
If we did want to enforce the maximum then it might make more sense to return an object { earlier: time1, later: time2 }
and just have getAbsoluteFor take the key that corresponds to the disambiguation parameter, or throw if the two are different in "reject" mode. That would move the responsibility for deciding what to do in the case of 0 absolutes found, from getAbsoluteFor into possibleInstants, and therefore into user code in the case of custom time zones.
Do we want to enforce this returns an array, rather than an iterable?
In the current form I'm not sure it makes sense to return an iterable, since the first thing we do is check its length.
Should this comment say something about how we'd pick one of them?
I'll add this.
docs/timezone-draft.md
Outdated
Alternatively, a custom time zone doesn't have to be a subclass of `Temporal.TimeZone`. | ||
In this case, it can be a plain object, which should (must?) implement all of the non-static methods in the interface. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't been following this particular discussion, so it might have been covered already – why is it desirable to allow this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I based that on the discussion in #300.
docs/timezone-draft.md
Outdated
Alternatively, a custom time zone doesn't have to be a subclass of `Temporal.TimeZone`. | ||
In this case, it can be a plain object, which should (must?) implement all of the non-static methods in the interface. | ||
|
||
> **FIXME:** This means we have to remove any checks for the _[[InitializedTemporalTimeZone]]_ slot in any APIs outside of `Temporal.TimeZone`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even the this checks in the methods on the prototype? That is, should we support Temporal.TimeZone.prototype.getOffsetFor.call({ getOffsetAtInstant() { ... } }, ...)
? Why or why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I wrote this I didn't intend to remove checks in the methods on Temporal.TimeZone.prototype
, but I see in your example how that could be useful. If we did Temporal.TimeZone.prototype.getOffsetFor.call(possiblyPlainObject, absolute)
internally then plain objects wouldn't have to implement all the methods. They'd only have to implement the methods that subclasses would have to implement. So I think we should support that.
8bfd98d
to
713b46a
Compare
ae419d6
to
3818fd9
Compare
docs/timezone-draft.md
Outdated
|
||
These functions gain an `options` parameter with an `idToTimeZone` option, whose value is a function returning the appropriate time zone object for the `id` passed in, or `null` to indicate that the time zone doesn't exist. | ||
|
||
If the option is not given, `Temporal.TimeZone.idToTimeZone()` is called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it wasn't for the fact that it makes sense to match the idToTimeZone
option, I'd say Temporal.TimeZone.fromId()
would make more sense. Now I don't know. On the other hand, do we need this given that Temporal.TimeZone.from()
exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can hash this out in #294.
> **FIXME:** These names are not very good. | ||
> Help is welcome in determining the color of this bike shed. | ||
|
||
Alternatively, a custom time zone doesn't have to be a subclass of `Temporal.TimeZone`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another question: what happens if you call Temporal.TimeZone.from()
with such a plain object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the plain object must implement the name
property, it would be the same result as calling Temporal.TimeZone.from(obj.name)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, can we require toString()
? Then from()
will call it by default. But then again, you'll still need the idToTimeZone
…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be fine, implementing name
would be optional then. I'll change this.
In #294 various ways of getting around the idToTimeZone
requirement are suggested.
3818fd9
to
6522247
Compare
6522247
to
3a6e65f
Compare
3a6e65f
to
c011110
Compare
Let's merge it as a draft, we can iterate from there. |
See also #300