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

Draft of custom time zone API #498

Merged
merged 1 commit into from
Apr 28, 2020
Merged

Draft of custom time zone API #498

merged 1 commit into from
Apr 28, 2020

Conversation

ptomato
Copy link
Collaborator

@ptomato ptomato commented Apr 6, 2020

See also #300

@codecov
Copy link

codecov bot commented Apr 6, 2020

Codecov Report

Merging #498 into main will increase coverage by 0.72%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            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              
Flag Coverage Δ
#test262 51.99% <60.19%> (-0.42%) ⬇️
#tests 93.21% <97.35%> (+0.98%) ⬆️
Impacted Files Coverage Δ
polyfill/lib/absolute.mjs 100.00% <100.00%> (ø)
polyfill/lib/date.mjs 93.03% <100.00%> (+0.52%) ⬆️
polyfill/lib/datetime.mjs 95.10% <100.00%> (+3.41%) ⬆️
polyfill/lib/duration.mjs 98.18% <100.00%> (+4.49%) ⬆️
polyfill/lib/ecmascript.mjs 97.91% <100.00%> (-0.16%) ⬇️
polyfill/lib/monthday.mjs 98.83% <100.00%> (+0.20%) ⬆️
polyfill/lib/now.mjs 100.00% <100.00%> (ø)
polyfill/lib/time.mjs 99.28% <100.00%> (+2.81%) ⬆️
polyfill/lib/timezone.mjs 87.91% <100.00%> (+1.20%) ⬆️
polyfill/lib/yearmonth.mjs 96.89% <100.00%> (+0.27%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b251071...c011110. Read the comment docs.

Copy link
Collaborator

@sffc sffc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I think it is nicer to pass real Temporal objects where possible, in line with what we decided last week for #354. So instead of taking or returning BigInt epoch nanos, just use an Absolute instead.

The idToTimeZone thing should follow what we come up with for calendars in #294.

docs/timezone-draft.md Outdated Show resolved Hide resolved
docs/timezone-draft.md Outdated Show resolved Hide resolved
docs/timezone-draft.md Outdated Show resolved Hide resolved

> **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?
Copy link
Collaborator

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.


/** Given an absolute instant returns this time zone's corresponding
* UTC offset, in nanoseconds (signed). */
getOffsetAtInstant(absolute : Temporal.Absolute) : bigint;
Copy link
Collaborator

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?

Copy link
Collaborator Author

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

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

docs/timezone-draft.md Outdated Show resolved Hide resolved
@ptomato ptomato force-pushed the custom-time-zone-draft branch 2 times, most recently from 7510f28 to 8bfd98d Compare April 6, 2020 21:52
Copy link
Collaborator

@Ms2ger Ms2ger left a 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.

docs/timezone-draft.md Outdated Show resolved Hide resolved
docs/timezone-draft.md Outdated Show resolved Hide resolved
docs/timezone-draft.md Outdated Show resolved Hide resolved
docs/timezone-draft.md Outdated Show resolved Hide resolved
Comment on lines +86 to +99
/** Given the calendar/wall-clock time, returns an array of 0, 1, or
* 2 absolute instants that are possible points on the timeline
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Comment on lines 119 to 120
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.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

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`.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

docs/timezone-draft.md Show resolved Hide resolved
@ryzokuken
Copy link
Member

@ptomato could you please rebase this on top of #506 or master if it gets merged by the time you read this? Thanks.

@ptomato ptomato force-pushed the custom-time-zone-draft branch 2 times, most recently from ae419d6 to 3818fd9 Compare April 13, 2020 16:47

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.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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`.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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

Copy link
Collaborator

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

Copy link
Collaborator Author

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.

@ptomato
Copy link
Collaborator Author

ptomato commented Apr 27, 2020

@Ms2ger Although #294 is still under discussion, I've changed this to use the fromId() method that we had suggested. I believe the comments are resolved as much as possible without #294, are there any blockers left in your opinion?

@Ms2ger
Copy link
Collaborator

Ms2ger commented Apr 28, 2020

Let's merge it as a draft, we can iterate from there.

@Ms2ger Ms2ger merged commit e3d232e into main Apr 28, 2020
@Ms2ger Ms2ger deleted the custom-time-zone-draft branch April 28, 2020 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants