#311: Temporal proposal

Visit on Github.

Opened Oct 27, 2018

Bonjour TAG,

I'm requesting a TAG review of:

Further details (optional):

  • Relevant time constraints or deadlines: Feedback is requested before this feature is proposed for Stage 3. We don't have a solid timeline for proposing for Stage 3, but the soonest possible time would be in the January 2019 meeting (and likely later).
  • I have read and filled out the Self-Review Questionnare on Security and Privacy. The answer to all questions is "no"--the only possible security-related concern was related to the possibility of adding more precise Date.now()-like functionality, which this proposal currently omits.
  • I have reviewed the TAG's API Design Principles

You should also know that...

It would be helpful to get feedback on which date/time units are useful/needed for web APIs (e.g., this proposal omits a duration type). The date/time types here may enable changes in the TAG's API Design Principles, c.f., https://github.com/w3ctag/design-principles/issues/101

We'd prefer the TAG provide feedback as (please select one):

  • open issues in our Github repo for each point of feedback
  • open a single issue in our Github repo for the entire review
  • leave review feedback as a comment in this issue and @-notify [github usernames]

Discussions

Comment by @slightlyoff Oct 31, 2018 (See Github)

Stream of consciousness before we take this up properly:

  • Is there a way to represent a time delta in this API?
  • What is the type relationship between Instant and DOMHighResTimeStamp/DOMTimeStamp? Do you envision those DOM types subclassing the new ones? Should they be convertable? Has there been a dicsussion about how IDL will translate them?
  • Has TC39 thought about HTMLTimeElement integration? Or how this can work with <input type="time">?
  • I don't think anyone is happy with the current processing model in HTML which is Gregorian-only (uggh) and which seems to include custom parsers. We'd love your thoughts on how to join these up.
  • Instant's constructor appears to rely on BigInt. Does DOM integration for Instant and related classes require WebIDL extensions?
Comment by @annevk Oct 31, 2018 (See Github)

What is wrong with Gregorian-only for non-user-facing date handling?

Comment by @cynthia Oct 31, 2018 (See Github)

Is there a way to represent a time delta in this API?

I was wondering the same.

Comment by @littledan Oct 31, 2018 (See Github)

Does DOM integration for Instant and related classes require WebIDL extensions?

I have a PR out for review at WebIDL to add BigInt support, but it's waiting for use cases to be articulated. I'm not sure if we'd need BigInt in WebIDL just to support Instant being used in DOM, though.

Is there a way to represent a time delta in this API?

Could you say more about use cases for a time delta representation?

Comment by @cynthia Oct 31, 2018 (See Github)

Example would be something like this:

if (date1 - date2 > date3 - date4) {
  // do something
}

(Although this proposal doesn't include a operator overloaded syntax, this is just an example of a use case.)

Comment by @pipobscure Oct 31, 2018 (See Github)

Re: time-delta

This is actually a fair bit more complicated than that. For one thing, the delta depends on TimeZones which may not be available.

So the approach we have chosen for temporal is to be explicit. So for Instant you'd do

if (instant1.milliseconds - instant2.milliseconds > instant3.milliseconds - instant4.milliseconds) {
  // do something
}

If you assume CivilDate then date1 - date2 will never give you a result that can in any way be relied upon to be correct. So being more explicit ensures that the result will actually be what you'd expect.

Comment by @pipobscure Oct 31, 2018 (See Github)

Re: DOMHighResTimeStamp/DOMTimeStamp

Both of these are defined as floating point values representing milliseconds. That in turn means that while the resolution may be sufficient, the precision may be off.

Again we have opted to be more explicit.

  • Instant.prototype.seconds - the number seconds since epoch
  • Instant.prototype.milliseconds - the number milliseconds since epoch
  • Instant.prototype.microseconds - the bigint microseconds since epoch
  • Instant.prototype.nanoseconds - the bigint nanoseconds since epoch

Being explicit and using BigInt where relevant ensures that we have both resolution and precision to be correct for calculations.

Using

  • Instant.fromSeconds(seconds : number)
  • Instant.fromMilliseconds(milliseconds : number)
  • Instant.fromMicroseconds(microseconds : bigint)
  • Instant.fromNanoseconds(nanoseconds : bigint)

allows for interoperation.

Comment by @pipobscure Oct 31, 2018 (See Github)

What is wrong with Gregorian-only for non-user-facing date handling?

Because the Gregorian calendar makes a lot of assumptions that isn't really the case. Think along the lines of days that don't start at midnight. So if you want to do date-calculations, then you have to work in the same calendar as is user-facing, or you are almost guaranteed to get sporadically wrong results.

Comment by @annevk Oct 31, 2018 (See Github)

That question was in the context of HTML doing something bad, but HTML doesn't do date calculations that I know about.

Comment by @mattjohnsonpint Oct 31, 2018 (See Github)

Has TC39 thought about HTMLTimeElement integration? Or how this can work with <input type="time">?

Our CivilTime object should map nicely to <input type="time">. Likewise, our CivilDate object should map nicely to <input type="date"> in a way that the current Date object does not.

I previously hadn't looked much at HTMLTimeElement, but on cursory scan, not every data type listed there would have a distinct object in Temporal - at least not with the initial implementation. Using the terminology listed there:

  • ❌ Valid month string
  • ✔️ Valid date string (Temporal CivilDate)
  • ❌ Valid yearless date string
  • ✔️ Valid time string (Temporal CivilTime)
  • ✔️ Valid local date and time string (Temporal CivilDateTime)
  • ❌ Valid time-zone offset string
  • ✔️ Valid global date and time string (Temporal ZonedInstant)
  • ❌ Valid week string
  • ❌ Four or more ASCII digits
  • ❌ Valid duration string
Comment by @plinss Oct 31, 2018 (See Github)

One thing that would be extremely useful to web apps would be the ability to access the list of IANA time zone names known by the system, possibly as a map with their offsets.

Comment by @hober Feb 5, 2019 (See Github)

@plinss since the Olson database changes over time, I'm concerned about the fingerprinting implications of exposing the client's known time zone names.

Comment by @littledan Feb 5, 2019 (See Github)

About the privacy concern, I believe this information can already be inferred through the Intl.DateTimeFormat API.

Comment by @littledan Feb 6, 2019 (See Github)

About @plinss 's feature request, there was an alternative proposal writeup which would just expose the tz database, rather than including it in a higher level API, and some web authors expressed support for this alternative. We had some discussion in TC39 about a lower-level API in TC39, but decided it was important to unify and provide good tools for high-level usage as well.

Comment by @travisleithead Feb 7, 2019 (See Github)

Took up at Tokyo f2f.

Thanks for the responses so far. As noted by @plinss above, we would really like to see the named time zones provided (if not in this proposal, than in some other venue) to complete the web developer experience of understanding the offsets.

Comment by @littledan Feb 7, 2019 (See Github)

Thanks for the feedback. I'll investigate whether we can provide this in the TC39 Temporal proposal.

Comment by @dbaron May 21, 2019 (See Github)

From way earlier:

What is the type relationship between Instant and DOMHighResTimeStamp/DOMTimeStamp? Do you envision those DOM types subclassing the new ones? Should they be convertable? Has there been a dicsussion about how IDL will translate them?

I think there probably shouldn't be a relationship, since DOMHighResTimeStamp and maybe also DOMTimeStamp are (I think!?) mononically-increasing, even in the face of user-initiated or ntp-initiated adjustments to the system clock. (Though maybe DOMTimeStamp isn't... and if so, perhaps there should be a way to convert from Instant.)

Comment by @dbaron May 21, 2019 (See Github)

One other comment is that "IANA time zone names" should probably be clearer about which ones. I'd guess it's something like the zones in africa, antarctica, asia, australasia, backward, europe, northamerica, southamerica ... maybe those in etcetera, and not those in backzone or pacific-new or systemv.

Comment by @cynthia May 21, 2019 (See Github)

Taken up during the Iceland F2F. We have one hard requirement for this to ship, in particular the WebIDL dependency should be solved. Additionally, timezone lookup seems like a missing piece in the infrastructure, and that would be a useful addition. The fingerprinting concerns we believe are not significant, as it would be bound to a browser version which is already detectable in the user agent string.

Additional feedback we have are the following points:

Regarding delta, as generating one from two Civil temporal types is hard to define, so we think the current .plus() operation should suffice. If there is wide usage of this, and the community needs syntactic sugar this can be revisited later.

As a developer, it is unclear what "Civil" implies; we do not have better suggestions, but it would be useful to either disambiguate that in the spec (or through MDN) what the intent of that name is, and when that name should be used for other types.

When there is wide implementation and usage, we would like to have HTML integration treated as a bug, and revisited.

As for the Gregorian only limitation, turns out that calendars are hard! @dbaron had a bunch of useful feedback on the complexity of this problem, and we are okay with the current approach.

Aside from the concerns above, we are happy to see this move forward and would like you to re-open if there are any significant design changes that we should look at.

Comment by @littledan May 21, 2019 (See Github)

Note, we appreciated your past feedback, and there is ongoing work to provide duration support, as well as two additional types for the HTML integration, and a function to get the current timezone, in progress at https://github.com/tc39/proposal-temporal/pull/130 .

We have one hard requirement for this to ship, in particular the WebIDL dependency should be solved.

What do you mean by this?

Comment by @littledan Nov 27, 2020 (See Github)

The Temporal champion group has made several revisions to the proposal based on feedback it's received from the TAG and others. The changes to the proposal are now complete, and now is a good time for final feedback on the documentation and specification before Stage 3 and implementation in browsers.

Some specific comments about the current proposal with respect to the TAG's past feedback:

  • To represent the delta between two Temporal types, we added an explicit Temporal.Duration type, which can also be used in the argument to .plus(). To calculate the difference between two Temporal objects, resulting in a Duration object, use the .since() method that exists on each type.
  • The Civil prefix has been renamed to Plain. Still likely a bit unclear, but it's the best we have. We determined that it's important to have some prefix, to indicate clearly the lack of timezone (as opposed to ZonedDateTime). We plan to edit the documentation to tersely explain this meaning.
  • For the Gregorian-only limitation, we have created a generic calendar API to avoiding being too Gregorian-specific. All dates have calendars attached to them, which govern date arithmetic on them.
  • To get the current timezone, use Temporal.now.timeZone. An API to get the list of all timezones was drafted within the Temporal proposal, but broken out into the Intl Enumeration API, which is currently blocked on investigating this fingerprinting question. Your feedback would be very welcome there.
  • To represent the data model for year-month and month-day date pickers, as well as other use cases, the types Temporal.PlainYearMonth and Temporal.PlainMonthDay were added.

Specifically, about integration with WebIDL and other web specs:

  • WebIDL support for the new bigint type has landed. However, the use in Temporal does not involve WebIDL, as TC39's specifications do not currently use WebIDL.
  • I'm happy to work towards HTML integration whenever it is seen as appropriate by the browsers/HTML editors. Some things already "just work" by coercion to strings (example from the docs).
  • Specifically, for the relationship between DOMHighResTimeStamp and Temporal.Instant: In my opinion, it would be best for future APIs to accept Temporal.Instant, due to its ability to describe higher precision and lack of rounding errors. (Remember not to use it for applications needing a monotonic timer!)
  • I get the feeling that the design philosophy used in web specifications like these is more "demand pull" than "supply push"--even if we have a mechanism which may be useful, we should maybe wait for other specification authors to ask for this integration where it's useful for them. So it seems like the most effective thing would be to publish good documentation about the proposal, rather than push specific integrations on people. This seems to be the philosophy taken about integration about Promise and BigInt integration in Web specifications. (I proposed several BigInt integrations that received a lack of interest for a long time; I don't want to waste people's time with something similar for Temporal.) It seems that the bar for proving that the integration is useful will be high, even amid TAG encouragement.

As an introduction to the current API shape of Temporal, see this presentation which walks through the basic types and operations. Our plan is to integrate this sort of discussion into the Temporal documentation.

Comment by @LeaVerou Jan 28, 2021 (See Github)

@cynthia, @kenchris, and I discussed this in our VF2F today.

Since there is no explainer but only documentation & the spec, we missed a list of user needs, which would facilitate review.

However, based on the documentation, we are concerned that while powerful, this API is very difficult to use for simpler use cases (see principle of Least Power).

One source of cognitive overhead is the deep hierarchy of objects, the distinction between which is based on what information is available (e.g. day+month vs day+month+year vs month+year or types without a timezone and types with a timezone). Not only does this mean that when objects are constructed based on incremental user input, the developer will need to continuously convert between types, but it also forces the API to promote factory methods for object creation which need to be learned separately, as opposed to constructors. We were wondering about the reasoning behind this decision, compared to a single object, with some of its information undefined or null.

While reviewing, we also spotted a typo in your documentation: in the example here the unit is probably "seconds", not "minutes".

Discussed Mar 15, 2021 (See Github)

Can we close?

Ken: already moved to stage 3 or 4

Dan: taking a look at an issue.. they're being responsive to our feedback. It's already on proposed closed. Close at plenary?

Discussed Mar 15, 2021 (See Github)

Reviewed, set to propose close pending Sangwhan's input

Comment by @cynthia Mar 17, 2021 (See Github)

We discussed what to do about this in today's call - while we have some concerns (as noted above) if the feedback is not actionable we don't think we should block this work. Hopefully there will be some libraries that make this API easier to use for average users. Thanks for bringing this to your attention!

(As for the pattern discussion, we should probably make that a separate discussion on design-principles.)

Discussed Mar 22, 2021 (See Github)

Reviewed recent feedback.

Comment by @ptomato Mar 23, 2021 (See Github)

Hi TAG! First of all I apologize for not responding sooner. Through my own fault I wasn't subscribed to this thread until two weeks ago, and at that point all my time was taken up with preparing for the TC39 plenary, and later recovering from it. Thanks for taking the time to review the proposal. I have some responses below to Lea's and Sangwhan's messages.

Since there is no explainer but only documentation & the spec, we missed a list of user needs, which would facilitate review.

I've been looking over the requirements for an explainer and while it's true that we don't have a document that follows this format, we did intend our documentation and cookbook to fulfill roughly the same function, of describing the rationale for the proposal, the user needs motivating it, and showing samples of how users would solve problems using it. I'd be interested in hearing more about what was missing from these resources which would have helped with your review, so we can improve this going forward.

One source of cognitive overhead is the deep hierarchy of objects, the distinction between which is based on what information is available (e.g. day+month vs day+month+year vs month+year or types without a timezone and types with a timezone). [...] We were wondering about the reasoning behind this decision, compared to a single object, with some of its information undefined or null.

I can give the rationale for having multiple classes in the proposal. (There is more background in https://github.com/tc39/proposal-temporal/issues/927 and https://github.com/tc39/proposal-temporal/issues/51 if you want to read more about it.) Having strong types is a design decision that was made to avoid bugs that might occur if developers had only one class to work with and had to fill in arbitrary values for missing data, and then decipher what that meant.

For example, "00:00:00" might be a common interpretation of null hour/minute/second fields, but until recently Brazil had their DST transitions at midnight, so for one day every year, that time would not exist, leading to unexpected and hard-to-track-down bugs. The "correct" workaround, to use noon rather than midnight as the missing-time value, is not well-known to most web developers.

In fact the question about HTMLTimeElement integration from the previous review that the TAG did a couple of years ago was part of the motivation for adding the PlainYearMonth and PlainMonthDay types.

I do want to acknowledge that this is a matter of taste. While we've gotten some feedback that many developers seem to understand and agree with the idea of representing your data more precisely with strong types, certainly plenty of developers would be happier with a single, more generic class as you suggested. If they wanted to, these developers could use Temporal.ZonedDateTime as an "everything" class, but in the documentation and cookbook we don't currently encourage it to be used that way, since it could lead to the type of bugs mentioned above.

In any case, the decision to have multiple classes is foundational to the proposal and I don't believe it would be possible to change it at this point, unless we started over with a different proposal altogether. But I hope that this response helps mitigate the concerns you mentioned, certainly legitimate ones, about cognitive overhead.

Hopefully there will be some libraries that make this API easier to use for average users.

I'm hopeful that using Temporal.ZonedDateTime for everything would be at least as good of a solution as relying on a wrapper library. That said, if someone does make some sort of a "simplified Temporal" library, I wouldn't be overly sad, as Temporal gives better tools for doing this than the Date object ever did, and avoids the need for that wrapper library to contain a copy of the time zone database which users need to download over and over again.

it also forces the API to promote factory methods for object creation which need to be learned separately, as opposed to constructors.

I'm not certain of the link that you mentioned between multiple classes and factory methods; that is also a design decision that's a matter of taste, but I do think it's a separate one from the decision to have multiple classes. Speaking personally, I also prefer constructors above factory methods, but it's my understanding that that's not a universal opinion, and I think the current split between low-level constructors related directly to the data model, and high-level factory methods accepting more kinds of input, is a good compromise. We did have an earlier thread with Lea about this.

While reviewing, we also spotted a typo in your documentation: in the example here the unit is probably "seconds", not "minutes".

I think this has been fixed in the meantime, thanks!