Dates and Times in JavaScript – A New API for Dates from TC39
blogs.igalia.com
blogs.igalia.com
Then all of them should implement that in their standard library, so that going forward we've got one sane conceptual time handling system everywhere.
I tire of dealing with the quirks of everyone having their own approach.
For example, lets say I have a store that opens from 8:00-17:00 every day, and it's currently 22:00. How long is it till it opens next?
And last year someone else's code failed on 30 December 2019, so I had a pretty good idea where to look.
Still need to fix a bug in code written only yesteryear because the 1st of July wasn't on a Monday this year, but I decided to snooze that test failure ... it'll work fine again in 9 years.
like in your search results for let's say some M-F 8-5 business... it would say "Hours: 8:00-17:00, Closed right now, in 72% of the cases, the next opening time is tomorrow morning at 8"
About your question it’s impossible to answer without further context. There may be DST tonight, or a leap second, or the enforcement of some strange treaty that will change the time zone..
http://www.creativedeletion.com/2015/03/19/persisting_future...
… which the proposal doesn't use. (It uses POSIX.)
https://codeblog.jonskeet.uk/2010/12/01/the-joys-of-date-tim...
https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a...
Or look at the usual falsehood list:
https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b...
Some interesting ones:
A week (or a month) always begins and ends in the same year.
Months have either 28, 29, 30, or 31 days.
There is a leap year every year divisible by 4.
The day before Saturday is always Friday.
These are similar problems: getting a computer-amenable model of something that's fundamentally a human phenomenon, and carries multiple centuries and multiple continents of accumulated context, ambiguity, and edge-cases. This is an extremely difficult class of problems, but in the case of text & characters, we managed to finally get a pretty well-functioning and broadly-supported solution after a few decades of gratuitously-incompatible half-solutions. There's hope.
ISO8601 without an offset is semantically different to one with an offset, it represents a time in local timezone (context-dependent). It isn't an "optional" offset in the sense that you can just omit it, it is a fundamentally different data type.
Without this distinction, there is no way to specify a local time in ISO8601, which would be highly inconvenient for certain applications. For example, how do you represent an event that occurs at 9am every day regardless of location? After all, dates and times are used for more than just storing absolute timestamps.
You are absolutely correct that offsets are also not timezones, which makes the ability to specify local "floating" times even more important (i.e. you can't just denormalize the above concept into a list of timestamps with offsets for each timezone you care about, as the offsets will change over time [edit: and tz->offset conversion is lossy and not reversible]).
Maybe there should be a "float" offset marker. Another thing I was reminded of is that +0000 offset should be Z (UTC), but it is often application dependent if -0000 offset is also Z as some applications use -0000 for "user local time, regardless of user". Which is related to "floating", but yet another semantic difference.
I suspect a side-effect of libraries lacking support for the full range of ISO8601 (for example, refusing to parse a value without an offset, forcing people to use such hacks). Wouldn't surprise me; iOS doesn't even have a consistent way to parse ISO8601 values with and without milliseconds.
[edit:
Apparently this is a RFC3339 thing, did not know that: https://tools.ietf.org/html/rfc3339#section-4.3
Negative zero offset is invalid in ISO8601, but valid and carries the meaning you describe in RFC3339. I misread what that meaning actually is from your comment - it is different to a "floating" or unqualified local time, it always represents a UTC time but with an unknown local time offset. ]
Is the distinction in representation too subtle?
We accept this subtlety elsewhere; we are used to 0 and "0" being different things, and expect them to behave differently under operators. Few would find the following surprising:
0 + 0 == 0
"0" + "0" == "00"
Then why would we expect these to behave the same? 20200710T010000Z + `3 hours`
20200710T010000 + `3 hours`
The first is a fixed timestamp in UTC, the result of the second depends on timezone.ECMAScript specifies "simplified extended ISO 8601", which is just that string above.
It took me quite a while staring at Stack Overflow answers and my code, wondering why the SO answers supposedly worked and my code did not, before I realized I had time.strptime and they were using datetime.strptime.
That also reminds me that .NET documentation likes to pedantically remind me that what I think of as ISO 8601 is probably more specifically IETF RFC 3339, which defines a formal BNF grammar as ISO didn't think to do that in the 80s. (Yay, standards.)
python-dateutil for incomplete but stdlibbish ISO 8601:2004.
python-edtf for ISO 8601-2:2019.
However, the ISO 8601-2:2019 spec is something of a clusterfuck born out of EDTF.
Most public discourse about it can be found in various confused crossover threads from people who need open-ended dates for their projects.[2-9]
Here's a haskell implementation of ISO 8601-1:2019 and ISO 8601-2:2019, for reference.[10]
[1]: https://github.com/metomi/isodatetime/issues/138
[2]: https://www.loc.gov/standards/datetime/
[3]: https://www.loc.gov/standards/datetime/background.html
[4]: https://www.loc.gov/standards/datetime/implementations.html
[5]: https://github.com/plk/biblatex/issues/656
[6]: https://github.com/ixc/python-edtf/issues/24
[7]: https://github.com/saw-leipzig/csv2cmi/issues/18
[8]: https://github.com/schemaorg/schemaorg/issues/242
[9]: https://github.com/JohnLukeBentley/open-datetime-standard-bo...
Why is it so hard to configure a Linux system (its locale settings) to get RFC 3339 formatted dates everywhere? Sure, I can `ls -l --time-style=full-iso`, `git log --date=iso`, `date --iso-8601=s`, `date --rfc-3339=s` and change the configuration of every single application by hand. Am I missing something?
Often, people recommend the hackish `LC_TIME=en_DK.UTF-8`. This however, doesn't work for Java applications and caused various other issues.
In spite of Unicode we also have a bunch of programs and libraries that mangle text.
The T-form is still useful for filenames and the like
From https://tools.ietf.org/html/rfc3339#page-8:
> NOTE: ISO 8601 defines date and time separated by "T". Applications using this syntax may choose, for the sake of readability, to specify a full-date and full-time separated by (say) a space character.
See https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_tim... for more references.
Compare 2020-7-29T19:45 to Chinese style:
2020年7月29日19時45分
Easier to read and you can pick any subset of components unambiguously.
In contrast, for dates and times, everyone really could have used the same design. Maybe you'd need a slightly different design for dynamic languages, but I'm not even sure about that.
Most of the problems I have seen in dealing with time, across various languages, arise from allowing types to be way too ambiguous. A lot of code uses interchangeably things like "UTC -0700" and "America/Los_Angeles" which are actually very different (one incorporates daylight savings and one doesn't). The important fix is that languages start using reasonable abstractions and are stricter about requiring explicit conversions between types rather than trying to implicitly guess an interpretation.
The first library I've seen get this right was Joda in Java, and it later got included into the standard library for Java. It's been copied other languages, and I've used it on the frontend a bit as JSJoda, but the literal translation makes it a bit awkward in JS and I found it was hard to get other JS developers to prefer it over the more popular moment or date-fns. Temporal looks great, it actually seems to borrow heavily from Joda but doesn't literally copy all of the names or methods so hopefully it will be the best of both worlds.
https://news.ycombinator.com/item?id=23783603 called for Unicode but for Time, which I think is perfect. In that framing, ISO8601 is UTF-8, but it is Unicode that is needed for everyone to agree on what is "code point" and what is a "character" (equivalently, what is a "calendar date", what is an "absolute timestamp", etc).
For example, if the standard was that a datetime would be represented as separate year, month, day, hour, minute, second, time zone some languages might implement that as a struct with names fields for each of those 7 elements.
Some languages might implement it as an object with properties for each of those elements.
Some might implement it as an array with one entry for each of those elements.
If the standard called for a function add_seconds that adds a given number of seconds to a datetime, modifying that datetime in place, languages that use a datetime struct might provide a standalone function that takes a pointer to a datetime struct and the number of seconds to add.
Languages that represent datetime as an object might have add_seconds as a member function.
So if I'm a Perl programmer and know how this standard works in Perl that would not necessarily mean it is obvious to me how it works in Python or C. But it would mean I could think about time the same way in all of them. I could expect to have corresponding data structures in all of them, with the same core set of operations available in all of them.
Names might be slightly different (e.g., what the standard calls add_seconds might become addSeconds in a language that by convention uses camel case for member function names), but the point is what these functions and data structures actually do would be standardized so once I know how to do all my date and time stuff following this standard it is easy to map that to the language specific implementations in JavaScript, Java, PHP, C, and the rest.
ISO 8601 defines a number of terms of art to represent a thorough mental model of the problem space, and then goes on to define string representations on how to express them. But it's paywalled, so people reach for RFC 3339 and the Wikipedia article instead. These focus on representations, and the definition of the concepts isn't captured clearly, so sometimes they're rediscovered independently. Other times, they're not.
Serious attempts to figure this out usually coalesce to something that looks and behaves like Java 8 time. Java 8 time may be verbose, but its concepts are clearly mapped out, and the API bears some marks of a misuse-resistant design. Compared to that, there's efforts like the standard libs for Python, Ruby, Go, or most third-party libs for JS, Rust, that don't bring clarity to the table, and don't present any solution for dealing with calendrical or clock-face objects, and do little to discourage misuse of their point-in-time objects in abstract contexts.
This proposal falls much, much closer to the Java 8 way than to the "everything is an instant, go make your own classes" way.
One shortcoming of ISO 8601 is that it has no provisions for abstract timezones. Instead, people go outside of the standard to represent ISO datetimes alongside IANA timezones.
But the most glaring problem with ISO 8601 is that it doesn't define partials where a more-significant term is unknown or missing: there's no valid, unambiguous way to refer to the 15th day of March, while leaving the year deliberately unspecified. This means that anniversary dates (e.g birthdays, holidays) aren't possible to represent in the abstract.
Likewise, there's no valid, unambiguous way to refer to the 29th second of the fourth minute of every hour. This is much less important than the birthday shortcoming.
The Library of Congress has a standard which extends ISO 8601 with a number of useful constructs for their purposes [2], like uncertainty qualifiers, but even that standard has no mechanism to represent a month+day partial in the abstract.
[1] https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_i... [2] https://www.loc.gov/standards/datetime/
Quoting from https://en.wikipedia.org/wiki/ISO_8601#Calendar_dates –
> The 2000 [of ISO 8601] version allowed writing "--04-05" to mean "April 5" but the 2004 version does not allow omitting the year when a month is present.
Probably the only thing perhaps not the most standard are the names they use for actual time zones? So perhaps that could be improved.
But seriously I don't know how the fuck javascript people can work with dates. It's ATROCIOUS. I think my favorite one is 0 indexes on months? like if you do date(2020,07,09).. is that July 9th 2020? nope, fucking June! Not June 8th.. but June 9th.
I don't know what monster did that but there should be a special place in coding hell for him/her!
The native API's are a disaster and using them is just asking for trouble!
If you think built-in .NET DateTime is any good, give NodaTime a try. Standard DateTime is barely usable, compared to it.
For JavaScript, there is excellent js-joda library (same design as NodaTime and Joda-Time), but it's 43 kB minified and compressed, which is... not terrible but also not great, really depends on your use case if it's worth it.
Well that was kind of my point- needing an external library to manage datetime is ludicrous. That's why I'm talking about what C# has builtin. No matter what new project you start, whoevers code you are working in- you can just use builtin.
>give NodaTime a try. Standard DateTime is barely usable, compared to it.
Went to the website to see some example code.. looks basically the same? Anyway thanks for the heads up but I work with datetime stuff constantly and I am not missing anything. It's easy to create datetimes, convert between time zones, add timespans etc.
The main difference is that there are separate types for the following concepts:
1) A point in time in global timeline
2) A specific date and time in specific time zone
3) Specific date and time but without any information about time zone (and two more related types that contain only date information or only time information).
There are a few more types, but these illustrate the main difference from .NET DateTime. In case you missed it, the basic concepts are explained here: https://nodatime.org/3.0.x/userguide/concepts (which I should have linked to in the first place).
This blog post by Jon Skeet explains the troubles with DateTime: https://blog.nodatime.org/2011/08/what-wrong-with-datetime-a... (he's also one of the authors of NodaTime).
Then I'll care / complain about the solution ;)
However for “absolute” chronological records you would be looking at using Barycentric Coordinate Time, which is what we get when we do a bunch of corrections to subtract the effects of the sun and earth’s (and the other planets) mass and motion, leaving us with a virtual “clock” that is suitably common for anything in the solar system.
To quote one definition of Barycentric Coordinate Time - ”It is equivalent to the proper time experienced by a clock at rest in a coordinate frame co-moving with the barycentre of the Solar System: that is, a clock that performs exactly the same movements as the Solar system but is outside the system’s gravity well.”
The ELI5 I normally use is roughly: It’s what we get if we take the Solar System in its normal orbit around the Milky Way, with some fancy magic we subtract away all the stuff, sun, planets, moons and all the asteroids and comets we can count, and then with a little more fancy magic, we put a super accurate clock that doesn’t weigh anything at all back at the center of where all the solar system stuff used to be.”
1) Latitude on another planet is straightforward: take the rotational equator, divide into 90 degrees north and south, done. But what about Longitude? Here on Earth, the prime meridian is Greenwich, because reasons. On Mars, it's indexed to a certain crater. But on the gas giants? Yeah, we still don't have a good plan about that.
2) Paul Krugman's thesis (yes, that Paul Krugman) is on relativistic economies. As in, my planet is in a deep gravity well, your's isn't. My clocks pas a lot slower. Now, how do I calculate interest? What about letters of credit for ships traveling between places to ship goods? His conclusion: let's hope there isn't money by then, because interest can't work between relativistic time frames.
There is the tz database that is correct for many use cases. It should be available in all languages that you listed https://en.wikipedia.org/wiki/Tz_database
I recently had to parse a bunch of dates in python, writing them to postgres, and then handle the same dates in javascript. And boy was it a PITA. The worst problem was the handling of "BC" dates. I had to hack my way around the boundary between python and sql. Javascript is no better, it simply doesn't compute BC dates without heavy hackery.
Is it too Java-y that it wouldn't make sense to port to JS? Are there copyright implications?
The JS Temporal proposal _does_ as far as I can tell, share many of the underlying fundamental concepts, which is great, but then confusingly has some types, such as `LocalDateTime`, which mean the exact opposite of what they do in the well-known Java API [3].
There is still discussion going on about these details, but from my perspective it seems like the best thing would be to just copy the Java naming conventions exactly.
[0]: https://www.joda.org/joda-time/
[1]: https://docs.oracle.com/javase/8/docs/api/java/time/package-...
I can't find a mention of LocalDateTime in the Temporal docs, did you mean something else?
Figuring out a name is still part of the ongoing discussion, so this specific case of `LocalDateTime` isn't a huge deal, and I might have misrepresented things slightly in my original comment, sorry! But I do think the overall point still stands - that it might be best to just use the same names and terminology as Java does.
[1] https://docs.oracle.com/javase/8/docs/api/java/time/package-...
It would probably be more correct to say that java.time is Joda version 2. Colebourne, the original author of Joda, was also one of the leads on JSR-310, and very much intended that 310 learn from the mistakes on Joda.
For example, `ReadableInstant` [1] in Joda implements 3 interfaces and has 7 subclasses. And really, what is the difference between `AbstractDateTime` and `BaseDateTime`? Whereas `Instant` from java.time [2] is an immutable value type and I haven't found it lacking in any respect.
On the whole java.time has struck me as extremely well designed (after coming from Python and previous date and time libraries in Java) and I think it would behoove other languages to liberally copy its design.
[1] https://www.joda.org/joda-time/apidocs/org/joda/time/Readabl...
[2] https://docs.oracle.com/javase/8/docs/api/java/time/Instant....
They're definitely an influence, just not the only one.
> LocalDateTime
That's a strawman proposal that's essentially user feedback and not a part of the API. Not yet atleast.
https://github.com/tc39/proposal-temporal/issues/7
https://github.com/tc39/proposal-temporal/issues/84#issuecom...
>JavaScript Date is broken in ways that cannot be fixed without breaking the web.
I really dislike the arrogant tone that many "new" people employ when talking about some "old" thing they are trying to "improve". (Quotes are there to indicate sarcasm).
Date is not 'broken', it may just not do whatever the author wishes it did; it is not 'broken' in the sense of failing to perform its intended function adequately.
I hope people could change their ways and be a bit more decent in general.
Aside from that, I welcome the new API as I think it would be an improvement over what we have now.
The fact that a vanishingly small percentage of JavaScript developers use Date for non-trivial applications is a great indicator of these failures.
import Temporal from "js:temporal";
1: https://github.com/tc39/proposal-built-in-modulesJava really did a good job here: Instant, LocalDate, LocalDateTime, ZonedDateTime. (If they hadn't already had java.util.Date, they probably could've replaced LocalDate/LocalDateTime with Date/DateTime, but that ship sailed).
Actual spec: https://tc39.es/proposal-temporal/
Less formal docs: https://tc39.es/proposal-temporal/docs/
Examples: https://tc39.es/proposal-temporal/docs/cookbook.html
You can try Temporal in the JS console on a doc page.
Regarding JavaScript temporals - better late than never I guess.
* If you're reading UTC dates from the backend and displaying them in local time, new Date(year + 1900, month, day, hour, minute, second) works great.
* It's also easy to calculate repeating intervals in local time by using that constructor with getYear(), getMonth(), etc.
* Send it back to the server with .toISOString(). Add a .replace('Z', '') on that so .NET binds a DateTime with the right Kind.
I pulled in moment about 5 years ago into our group’s stack, but we mostly use it for parsing and formatting date (only) strings for date arithmetic. The government vital records forms we are working with seldom deal with time of day, and when they do, it’s an optional string down to minutes resolution only.
I guess all the converting to and from strings coincidentally protected me from mutability. Good to know.
[0]: https://moment.github.io/luxon/index.html [1]: https://moment.github.io/luxon/docs/manual/moment.html
[0] Current user's current locale is easy. If for some reason you need to show some other locale than what the browser is using you'll likely still need Moment or Globalize.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Also, if you find you only really need one or maybe two date time utilities, date-fns [1] is an alternative that that takes an even more modular approach for tinier imports.
1. https://venam.nixers.net/blog/unix/2020/05/02/time-on-unix.h...
Regrettably Safari is—yet again—lacking with support, but in the meantime there is a polyfill.
1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Twice I’ve started with it, twice I’ve tossed it out because in the end it didn’t help my code at all. (Both times I only cared about English, so its replacement which did a better job of formatting values for my situation took pretty much no extra code—for exact equivalence, formatter.format(-n, 'day') for known positive n becomes a simple ternary, `n == 1 ? '1 day ago' : '${n} days ago'` (or if the formatter has the {numeric: 'auto'} option, change '1 day ago' to 'yesterday'); but even with full localisation, I’d still be unlikely to use RelativeTimeFormat.)
const now = Temporal.now.absolute();
const then = Temporal.Absolute.from(
'1989-11-09T22:45:00+0100[Europe/Berlin]',
);
const sinceThen = now.difference(then);
sinceThen.toLocaleString('en');
1: https://tc39.es/proposal-temporal/docs/duration.html#toLocal...If you want those formatted values to be consistent with the rest of your app, you will need this anyway.
You app does support localisation right?
Also: there’s no guarantee a browser would have the same localisation as your app (for instance German-language browser, English website).
You really have to do this 100% yourself in your app if you want the result to look right.
I attempted to test your library's behavior in this regard, but I do not think you are correctly implementing the proposal's TimeZone. TimeZone.getDateTimeFor() is supposed to take an Absolute and return, in your case, the time that absolute represents in TAI (that time, in the TimeZone). But this:
var one_before = new Temporal.Absolute(915148799n * 1000000000n);
console.log(one_before.toString());
console.log((new Temporal.TimeZone('UTC')).getDateTimeFor(one_before).toString());
console.log((new TAI()).getDateTimeFor(one_before).toString());
emits, 1998-12-31T23:59:59Z
1998-12-31T23:59:59
1970-01-01T00:15:15.148768
That last timestamp being the output supposedly in TAI; but that POSIX timestamp, 915148799 represents 1999-01-01T00:00:30 in TAI. That is, the second line, 1998-12-31T23:59:59 in UTC == 1999-01-01T00:00:30 TAI.The other direction (getPossibleAbsolutes) is similarly effected.