Code Monkey home page Code Monkey logo

proposal-temporal's Introduction

Temporal

Provides standard objects and functions for working with dates and times.

Status

This proposal is currently Stage 3 and was reviewed for Stage 3 by Richard Gibson, Bradley Farias, and Daniel Ehrenberg.

This proposal is now in the hands of ECMAScript engine implementers, so the bar for making API changes is extremely high. Nonetheless, changes may occur as the result of feedback from implementation in JS engines. Editorial changes to the spec and bug fixes to the spec, tests, and docs are also ongoing, as is customary for Stage 3 proposals. Additional tests and documentation content are also being added during Stage 3.

Champions

Overview / Motivation

Date has been a long-standing pain point in ECMAScript. This proposes Temporal, a global Object that acts as a top-level namespace (like Math), that brings a modern date/time API to the ECMAScript language. For a detailed breakdown of motivations, see: Fixing JavaScript Date

Principles:

  • All Temporal objects are immutable.
  • Date values can be represented in local calendar systems (why?), but they should be convertable to and from the Proleptic Gregorian Calendar.
  • All time-of-day values are based on a standard 24-hour clock.
  • Leap seconds are not represented.

Specification Text

The specification text can be found here.

Documentation

Reference documentation and examples can be found below.

A cookbook to help you get started and learn the ins and outs of Temporal is available here

Polyfills

Polyfill Repo Status
@js-temporal/polyfill js-temporal/temporal-polyfill Alpha release available
temporal-polyfill fullcalendar/temporal-polyfill Beta release available

If you're working on a polyfill, please file an issue or PR so we can add yours here.

A non-production polyfill was built to validate this proposal. This polyfill continues to live in this repo, but only for the purposes of running tests and powering the documentation "playground" as described below.

DO NOT use this polyfill in your own projects! Instead, please use a polyfill from the table above.

Documentation Playground

When viewing the reference documentation, the non-production polyfill is automatically loaded in your browser, so you can try out Temporal by opening your browser's developer tools console.

proposal-temporal's People

Contributors

12wrigja avatar aditi-1400 avatar akshgpt7 avatar anba avatar arshaw avatar bakkot avatar cjtenny avatar frankyftang avatar gibson042 avatar gilmoreorless avatar h-h-h-h avatar hax avatar jasonwilliams avatar jessealama avatar jugglinmike avatar justingrant avatar linusg avatar littledan avatar ljharb avatar lubrsi avatar maggiepint avatar mattjohnsonpint avatar ms2ger avatar pdunkel avatar pipobscure avatar ptomato avatar redsquirrelious avatar ryzokuken avatar sffc avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

proposal-temporal's Issues

Calling types not tied to a specific location "local"

First of all, it is exciting that there is a proposal for improvements to date and time in ECMAScript. Thanks for working on that.

I know that some other APIs use "local" for types not tied to a specific location. One can speculate about why that is. My guess is that it comes from the idea of having either "UTC" or "not-UTC".

However "local" might not make the most sense. I looked up "local" in a dictionary:

local |ˈləʊk(ə)l|
adjective
1 relating or restricted to a particular area or one's neighbourhood: researching local history | the local post office.
• denoting a telephone call made to a nearby place and charged at a relatively low rate.
• denoting a train or bus serving a particular district, with frequent stops: the village has an excellent local bus service.
2 (in technical use) relating to a particular region or part, or to each of any number of these: a local infection | migration can regulate the local density of animals.
• Computing denoting a variable or other entity that is only available for use in one part of a program.
• Computing denoting a device that can be accessed without the use of a network. Compare with remote.

So local is talking about a particular region or part. In the proposal there is a description of two different types of datetimes:
"A date and a time without any time zone reference." and "A date and a time, at a specific instant in time, with a time zone."

Imagine you didn't know anything about previous datetime libraries. Which of those two descriptions fit best with "relating to a particular region or part" ? The one that is specific to "Europe/Paris" or the one that is "without any time zone reference"?

To me ZonedDateTime seems more "local" than LocalDateTime. The type "without any time zone reference" is general, it does not know anything about a specific time.

An alternative is to use Naive instead of Local. NaiveDateTime, NaiveDate, NaiveTime. Elixir has a type called NaiveDateTime and Python also has a naive type.

ZonedDateTime constructor overloads?

I'm wondering -- Is there more explanation about the desired purpose of the additional overloads for the first parameter of the ZonedDateTime constructor?

  • The constructor overloads that take -HH:MM / HH:MM or number of minutes West of GMT -- how does this differ from the proposed OffsetDateTime?

  • What is the proposed behavior of the undefined or SYSTEM values? AFAIK there is no standards-defined way to determine the system IANA timezone string because it is not required that one be defined. If there is no way to get an IANA string from the system, then this would result in nothing more than an OffsetDateTime using the reported system offset. And undefined?

  • Overloading the first parameter will slow down the 99% case, which I assume would be the IANA string. Would this also affect optimization possibilities in current optimizing compilers?

  • Is it a throw-ing error if the IANA string is not available on the system (e.g. "sifhisfhs")?

Personally I would prefer to see this only take an IANA string and move OffsetDateTime from the future set into the initial set, since it is a much simpler type. Basically, implementing the two proposed overloads implements that type anyway. We currently universally use the exact proposed OffsetDateTime type alongside the built-in Date and it works very well. We provide IANA functionality via a static conversion function rather than a separate type, but either is acceptable.

Instant vs ZonedInstant

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?

TODO: Scenario-Based Examples

Previously we had two examples of common scenarios this API could solve. We should add those back, with the appropriate changes, and perhaps add some others.

Add "resolver" in a later proposal?

To keep the API surface area small for the initial proposal it might make sense to choose a reasonable default for the resolver option, but not make it configurable for now. Since it is part of the options object it looks reasonable to add it to the spec at a later point, the only downside would be that it's not clear how to figure out if your native implementation of the classes support the resolver or not, and it might be a little harder to polyfill.

Date/time validity

This API currently doesn't have the concept of 'invalid date' that is in the current Date spec. There are certainly some values that might be passed to the constructors that do not cleanly construct a date/time/instant.

  1. What are 'invalid' values for each type?
  2. What happens when they are passed? Invalid date? Thrown an error?

Differences from date-fns

Interested in what you're proposing here! Immutability is a big deal for us.

Can you share anything about what you plan to offer above a library like date-fns, which operates on Date objects in an immutable fashion? I suppose one advantage to the new lib is a 100% guarantee that no one will mutate the object.

TODO: Determine value of valueOf

We should decide what .toString() and .valueOf() return for each object type. We should also consider the output of console.log for each object type, and whether it is the same or different than the toString output.

Time forward testing

Libraries like Moment provide a way to override the "current time" with a function of one's choice for the purpose of time-forward testing.

Do we provide a way to do this or just let people monkey patch the instant object?

TODO: Create APIs for parsing and formatting

How do we want to handle date parsing? Is it part of this proposal? 402? Some of both depending on if the date is ISO8601/RFC2822 or a local format?

Should there be a separate parsing object that returns a 'parsed date' object?

Consistent method prefixes

Since date-time libraries API's like threeten or moment have a relatively large number of methods, i would like to propose the use of consistent method prefixes for this API in order to make it more manageable and readable.

I would like to propose the following method prefixes from the threeten API.

  • is - checks if something is true
  • with - the immutable equivalent of a setter
  • plus - adds an amount to an object
  • minus - subtracts an amount from an object
  • to - converts this object to another type, such as zonedDateTime.toLocalDateTime()
  • at - combines this object with another, such as date.atTime(time)

The threeten get method prefix should be omitted, because its quite uncommon for javascript. More common is to simply get the value by name like date.year().

The prefixes of and parse for static factory methods might be better replaced by constructors, among others, because of the lack of private constructors in javascript.

TODO: Difference functions

We should have difference function(s), such as determining the number of days between two dates, or someone's age.

TODO: Functions for getting the current time

We'll want a way to get the current time. Possibilities:

  • temporal.getCurrentInstant()
  • Instant.now
  • Instant.getCurrentTime()

And maybe shorthand ways to get the now of other object types.

Converting from one calendar (e.g., gregorian) to another (e.g., chinese)

I see an example converting from various timezones:

// Convert between various time zones
var eastUS = new ZonedDateTime('America/New_York', 2017, 1, 1, 0, 0);
var france = eastUS.withZone('Europe/Paris');
var utc = eastUS.withZone('UTC');
var localSystem = eastUS.withZone('SYSTEM');

I miss a similar example converting from various calendars.

TODO: Simple properties

Need to add getter-only properties for all objects for simple things like .year, .month, .day, etc.

Consistent use of singular/plural form

Currently, these objects use singular form for fields, but accept plural forms in arguments for e.g. plus. So we cannot even use that new type as a date range and add it to existing.

Use case:

// constructors are used with different API for clarity
const delta = new CivilDate({ month: 1 });
let current = new CivilDate({ year: 2017, month: 1 });
while (date.year < 2018) {
  layoutCalendarMonth(date);
  date = date.plus(delta);
}

Define Instant before ZonedDateTime?

Apologies if this has been discussed elsewhere; I did a brief search on es-discuss but couldn't find anything.

Anyway, my thought is that ZonedDateTime, Instant and OffsetDateTime all seem to be representations of basically the same thing (a single instant in time), with ZoneDateTime and OffsetDateTime containing some extra info (the zone or offset) that's not stored in the Instant case. To capture this, it seems like it might be good to define Instant first, because then the instant could actually be a property on the ZonedDateTime and OffsetDateTime objects.

Object.keys(new ZonedDateTime()) // ["instant", "zone"]
Object.keys(new OffsetDateTime()) // ["instant", "offset"]

Thoughts? I know the realm of dates and timezones is full of subtle distinctions and caveats, so I'm probably overlooking something here...

Do not use pluggable calendars

The proposed API uses an "options" field to allow for pluggable calendars. As I wrote about Joda-Time, making the calendar system a pluggable element of the object is a Bad Idea. Developers will call getMonth() and expect it to return 1 to 12, and then be baffled when it returns 13. Or when the length of a month is 5. The only sane approach is to have the main classes only implement the ISO-8601 civil calendar system (no Julian-Gregorian cutover).

See also here and the architectural issues here. In general, giving developers power over calendar systems is almost always a Bad Idea - they simply won't consider the edge cases properly.

For this API, I would strongly recommend keeping the calendar system completely out of the state of the classes. Instead, pass it in to the formatter, or as a parameter, when needed.

Construction from scalar arguments vs. aggregate objects

In this proposal, constructors take positional parameters, but methods like add take an options bag with named parameters. I was wondering why this option was chosen. Why do constructors not also take such an options bag?

Global objects vs own namespace

I'm a bit concerned about the pollution of the global space with a number of new global objects from this proposal.

Historically, it seems to me that we tried to collect single global per functional area, like Intl, SIMD or WebAssembly and while all "raw" globals like Array, Int8Array, Number etc. are of generic use.

Date&Time operations as presented in the proposal feel to me much more like a specialized functional area that is likely to grow, rather than a generic data type. In particular, it adds multiple objects, and multiple operations and in the description aims to be open for future extension to handle more calendars, operations and types.

Having both, Date and a number of new globals proposed here, feels like confusing design decision that will over time creep up as a cognitive load on the users.
With no chance to deprecate Date anytime soon, I'm wondering if it would make sense to create a single global namespace, say Temporal or DateTime and aim to fill it with methods and objects that would provide the functionality this proposal aims for.

I feel like it makes the global namespace cleaner, including as trivial things as helping with autocomplete.

Any chance the champions would be willing to consider this idea?

Documented API differences between Date and CivilDate?

Can we document somewhere what are the deliberate differences between Date and CivilDate and their rationale?

@dherman brought up a good point about the departure from getters/setters now that we have immutables which seems like a very reasonable reason to me.

can we document somewhere where else it differs? Here are a few that I caught looking at this:

  • string constructors (e.g. CivilDate("2017-07-25"))
  • static .now() and .parse()
  • toJSON()
  • toISOString()

Please use constructors instead of factories

It's generally an antipattern in JavaScript to have classes that are not constructible, but instead somehow the browser magically creates them when you use special factory functions. I don't understand the motivation for deviating from the normal pattern and doing temporal.createTime() instead of new PlainTime().

Set of value types

While I understand the desire to start small, the reality is that the benefits of having separate types accrue once you have enough of them to be meaningful. You need to teach people to think about what type their data really is, and to do that you need enough types to express it, My experience is that the following are the important types:

  • Instant
  • LocalDate
  • LocalTime
  • ZonedDateTime

The next tier includes:

  • LocalDateTime
  • OffsetDateTime

The final tier includes:

  • OffsetTime
  • Year
  • YearMonth
  • MonthDay

The amount classes Duration and Period are separate to this discussion and can certainly be added later.

As such, I think choosing LocalDateTime for the first proposal is an odd choice - LocalDate, LocalTime and Instant are more important.

Motivations

As moment.js and js-joda demonstrate this proposal could be implemented as a library too. I think we should make the actual motivations a little more clear why this should be built into the browser/platform, and what the advantages are compared to being implemented as a third-party lib.

  • Date is built-in too, but the general consensus seems to be that it's not a great design and has a lot of flaws. These flaws can not be fixed without breaking API unfortunately so to fix it, the whole class would need to be deprecated and replaced with something else. (Alternative is to keep the broken API forever and only rely on third-party libs for sane date/time code)

  • The timezone database is large, and shipping it to the browser is costly. It would be nice if the browser/platform provided at least the timezone database natively so that libraries like moment.js or js-joda could take advantage of it. (Alternative is shipping it with the application code or figuring out what timezone info is needed and loading it async from the server, which requires a lot more complexity though)

The first point would probably favor the current proposal, while the second point would be resolved if only some low-level primitives would be offered by the platform. I personally haven't yet decided what road I'd choose, but it is clear that any solution would be better than the current state. :)

TODO: Explain overflow (or lack thereof)

Since the Date object's behavior is to overflow / bubble (i.e. Jan 32 => Feb 1), and the objects in this proposal will not have that behavior, we should determine what happens (throw, etc.) and document this.

porting conversation from personal repo

I set up a private repo a while ago, with @Nevraeka, in which we attempted to flesh out a spec for datetimes. In the interests of keeping to one solid proposal, I thought it might be useful to shutter that repo and bring the conversation here to get some discussion points for this proposal.

@keithamus commented on 6 Feb
I'd like to use this issue to discuss & discover what the high level goals of this spec should be. What we are attempting to do, and what rules & guides we should stick to.
I'll kick this off with the following points, which I feel is important to a better date lib than what we currently have. In no particular order:

  • Constructors should not accept a range of different arguments and formats. Pick one good format (ISO8601), provide supplementary methods for parsing with minimal "magic".
  • Dates should be immutable. Any methods which alter a date should return a new one.
  • Dates should provide some mechanism for safely adding durations of one factor without changing another. For example it should not be possible to do add one month and get back a date two months in the future, or increment the date (0-31) and get back a date that has rolled over to the next month.
  • I would also like to put some thought into how we can provide a decent enough abstraction to support multiple calendars, but this could be impossible to reasonably do.
  • We should provide facilities - where possible - to abstract over common operations which are prone to errors. A fairly concrete example: provide a mechanism for iterating over days in a month.

@Nevraeka commented on 8 Feb
👍 I totally agree with almost all of what you are saying @keithamus - especially on Dates being immutable. Constructors, maybe, could take a few different args & formats (a limited set though that accommodates the major cases).

One item that I have not seen done well in the few languages I looked at are Time Zones. Any thoughts on how Dates 'should' best handle this?

Also - Date objects could be created without using 'new' instances similar (superficially) to Date.now or Ruby Dates

For example: Date.parse or Date.strfTime would implicitly return the new object with the applied transformation.

this is opposed to ...

const dateInst = new Date();
const parsedDate = DATE.parse('12-25-2016');

@keithamus commented on 8 Feb

Constructors, maybe, could take a few different args & formats (a limited set though that accommodates the major cases).

My (probably strong) opinion is that constructors should be single arity. (other than Array (probably wrong here, but off the top of my head)), Date is the only constructor which is A) not single arity, B) variadic, C) has different outputs based on the number of arguments.

This is not ideal because for every new possible instantiation of a constructor increases the complexity (both code and mental overhead) exponentially. Right now, off the top of my head, I cannot tell you how many different instantiations of Date are possible.

However Date has another problem. The other problem is that the "stringly-typed" constructor version, where you parse in a Date string is rife with cross-browser compat issues. Some browsers parse some date string formats, while others don't.

Even worse than all of that, is that if you actually manage to get a working instantiation, there's still a possibility you'll end up with Invalid Date.

By specifying a Date constructor which has a fixed and small arity, with a well specified definition of what those arguments should be, and throwing errors when the arguments do not meet these requirements, we can do so much to alleviate the hassle of using Dates. As you suggest, if we want to have some additional mechanisms for parsing arbitrary values into Dates, then we can with static methods, to wit (not formal here, but some ideas):

Date('2017-12-31') // works
Date(2017, 12, 31) // throws TypeError
Date.fromNumbers(2017, 12, 31) // works
Date('12-31-2017', '%m-%d-%Y') // throws TypeError
Date.strptime('12-31-2017', '%m-%d-%Y') // works

One item that I have not seen done well in the few languages I looked at are Time Zones. Any thoughts on how Dates 'should' best handle this?

Timezones are especially difficult, both to implement reasonably, but also to calculate on top of - as they slip and slide and can cause unexpected changes to a Date. Upon some iteration of my original work around this I pretty much settled on the idea that a Date constructor should strip any timezone information upon parse time. It's not ideal because it is a destructive operation, it means Dates become lossy, but in actuality if you want to do any date calculation - you probably want to do it in UTC. In fact ES6 introduced Date.UTC to provide this (even if it did include the dreaded words When the UTC function is called with fewer than two arguments, the behaviour is implementation-dependent).

I think, given an opportunity for a new Date constructor, I would make the default ctor strip tz information, and perhaps have an opt-in with a static Date.tz which carries timezone information with it. Perhaps passing a tz to the main ctor would throw?

Date('2017-01-01T00:00:00+0400') // throws (Range?)Error
Date.tz('2017-01-01T00:00:00+0400') // works, you're on your own from here

Also - Date objects could be created without using 'new' instances similar (superficially) to Date.now or Ruby Dates

I agree with this. Current behaviour is weird - most stdlib ctors work without new, or throw. We should do one or the other, preferably work without new.

@Nevraeka commented on 9 Feb
Great points on 'single arity'. I can agree.

On the note of Date.UTC & timezones check out Intl.DateTimeFormat. I still have to look further into this but it seems but since the examples seem to rely on the current Date constructor we would need compat for this as well

@Nevraeka commented on 9 Feb
Also - Once we have a good high level scoping, I would think we should prob consolidate with the other implementers of this to get a holistic approach going to avoid flame warring. Thoughts?

@keithamus commented on 9 Feb
Yeah I've had a look at Intl.DateTimeFormat. I think this might be the biggest cause of pain - having both interop with, and extending to that.

Also - Once we have a good high level scoping, I would think we should prob consolidate with the other implementers of this to get a holistic approach going to avoid flame warring. Thoughts?

Completely agree. Kept this repo private between us until we can flesh out a very loose spec, then we can discuss opening this repo up and getting a wider audience. Want to keep it focussed with us now to avoid getting a bucket of wishes from wider community and ending up with design-by-committee.

TODO: math functions

We need to decide on how we want our math functions to look. Options are basically addMonths(3) or add(3, 'months'). We could have subtract functions also, or just allow negative numbers, or both.

Decide what to do about other calendar systems

The types in this proposal currently support only the ISO8601 (proleptic Gregorian) calendar system.

We need to decided if we should extend to additional types to support other calendar systems, or if we'll just handle that during parsing/formatting via ECMA-402.

The only advantage I can see by supporting them in this proposal is that one could then do things like "add one Hijiri month" to a date. It may not be worth the overhead to deal with that here, when such functionality could easily be provided by a library.

Timezone and DST

There are a ton of problems with the current Date object and I'm really thankful to see this project get going.

Just to pick one of the problems... can we avoid the misleading method .getTimezoneOffset() which conflates both the TZ and DST offsets. These are very different things and It's often useful to have both values. Getting the difference between UTC and wall-clock time is a common use case, but let's call it something like .getLocalOffset() instead.

Functions for understanding clock precision

Per discussion at TC39, we should consider whether we can expose metadata that describes the clock precision of the host environment.

We should also include some documentation in the spec that talks to accuracy !== precision, and that not all environments can provide the same level of precision.

This is related to #28.

"temporal" the built-in module and the concept Temporal Dead Zone

I'm not sure I have an opinion either way, but I couldn't help thinking of this when I read the proposal. Obviously there is no technical issue here, but I'm thinking more in terms of those learning language concepts and potential for confusion. Maybe I'm over thinking it?

Integer nanoseconds vs. fractional nanoseconds

In most web APIs nanoseconds are represented as fractions of milliseconds. Why was a pair of integers chosen here? Is it possible to make floating-point milliseconds the only representation instead, or does that lose too much precision? If the latter, it'd be great to have a section explaining.

Relatedly, I'm wondering what new Instant(123456.7890, 1234.532) does.

Comparison functions

Should comparison be part of the proposal? I realize it's more awkward without operator overloading. And for instants it's easy to convert to numbers and compare. But it seems important to be able to compare PlainDate/PlainTime/PlainDateTime. (Is this day equal to X's birthday? Is the time between 9am and 5pm?)

I think it'd be good if comparison between unlike types threw, at least in the beginning, instead of trying to do some clever conversion.

Bikeshed thread for civil time prefix

Currently the "civil time" types are prefixed with Plain. This is a bit confusing and unclear to me, so I thought it would be worthwhile to start a thread exploring alternatives.

To be clear, this kind of bikeshedding is not a big issue or blocker, and hopefully will not be too frustrating for the proposal authors. But I think it's a discussion worth having.

Some alternatives:

  • Plain
  • Civil
  • Local (I think this is what they were originally called, and I agree it's probably bad)
  • Floating ?
  • Unzoned ?

TODO: Define valid time zone options

We will probably need to support time zone constants of 'UTC' and 'SYSTEM', to work with UTC and the local system time zone, respectively. We also will want to support IANA time zone IDs, and fixed offsets from UTC in ISO-8601 format (±HH:MM)

Questions about built in modules

just a bit out of scope
How built in modules will work? They will be namespaced? If this questions are in discussion yet, can you guys point out some resources?

import { LocalDateTime } from 'temporal'; // All classes in the same module?
import LocalDateTime  from 'temporal/LocalDateTime'; // Splitted Modules?
import { LocalDateTime } from '@standard-lib/temporal'; // Namespaces?

Options make the state of the object unclear

While I understand the desire for variation, I'm not conviced that options are the right approach. A key part of having speific types for parts of the date/time problem space is that the types are values. Specifically, this means that given two LocalDateTime instances, if they are equal then they are also substitutable.

Consider this:

var options1 = {
    resolver: (mapping) => ({
        skipped: mapping.forwardShifted(),
        ambiguous: mapping.firstOccurrence()
    })
};
var options2 = {
    resolver: (mapping) => ({
        skipped: mapping.nextValid(),
        ambiguous: mapping.firstOccurrence()
    })
};
var zdt1 = new ZonedDateTime('America/Los_Angeles', 2017, 3, 10, 8, 0, options1);
var zdt2 = new ZonedDateTime('America/Los_Angeles', 2017, 3, 10, 8, 0, options2);
// but is zdt1 equal to zdt2?

My assumption is that zdt1 is not equal to zdt2. Yet this is plainly silly - both represent the same point in time, with the same time-zone. The only thing that differs is that they have been setup to react differently in certain circumstances, but that information is not really part of the state of the date-time, instead it is part of the business logic of the application. As such, the resolver must not be part of the state of ZonedDateTime, but should be passed in as a parameter when needed.

As further evidence of this, the date-time objects should have be able to be compared with a natural order, and in Java that order should be compatible with equals(). See the definition of equals() and compareTo() on ZonedDateTime in Java 8 to undertand how this works. It is also worth considering serialization, ie. how you can't really send a ZonedDateTime across a network as a string very easily if it contains additional information like a resolver.

In summary, the state of a date-time object has to be carefully controlled to just be the date, time, offset and time-zone. Resolvers don't make sense to be part of the state.

Parsing ISO 8601 strings

The discussion in #34 about maintaining precision between server and client got me thinking about transport formats. I added a comment on #25 about serialising temporal objects to JSON output, but what about the reverse? When the server outputs a datetime in ISO 8601 format, we'll need a way to convert that string into a temporal object.

The different temporal objects will have different requirements (e.g. PlainDate aka CivilDate will have no use for any of the time parts). Additionally, ISO 8601 strings can be variable length, omitting various parts of precision.
This is mostly thinking out loud (or the text equivalent), so I don't have a solid proposal, just more questions:

  • Do we allow each temporal object type to parse an ISO 8601 string and throw away the parts that are not needed? Would this just cause more confusion than it's worth when CivilTime ignores a time zone present in the input?
  • Do we define a top-level temporal.parseISOString() method (name is up for debate) that returns the appropriate object depending on the string format?
temporal.parseISOString('2017-07-14');
// == CivilDate(2017, 7, 14)

temporal.parseISOString('2017-07-14T07:54');
// == CivilDateTime(2017, 7, 14, 7, 54)

temporal.parseISOString('2017-07-14T07:54:00+10:00');
// == CivilDateTime(2017, 7, 14, 7, 54, 0)  TBC
  • Do we allow parsing more information than provided by ISO 8601, in order to accurately map the time zone? The current proposal shows an example of a ZonedInstant being represented as 2017‑12‑31T09:00:00+09:00[Asia/Tokyo] — the extra zone information is very useful here, and it would be ideal to be able to parse the string output of any temporal object back into an identical object, thus reinforcing the immutability.

(As a side note/rant, I find the lack of named zone support in ISO 8601 really frustrating. Although it's understandable, given the lack of official standard (IANA zones notwithstanding, but they're still just a defacto standard rather than an official one). For example, a timestamp for where I live can only define the zone as +1000 / +10:00, which could be for Sydney, Brisbane or even Vladivostok.)

There are complications and edge cases no matter which way it's done, but I think that defining a 2-way serialisation is important. Without it, the usefulness of the temporal module will be greatly diminished.

(Of course, this is the generally the point where someone smarter than me comments with a solution that seems perfectly obvious in hindsight. 😉 )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.