npmjs.com/package/temporal-fun
$ du -sh '/Applications/Google Chrome.app'
1.3G /Applications/Google Chrome.appSome countries start on a Friday or Saturday and until 2022 Iran could start any day of the week although never at 3AM.
// call foo() one day from now:
sleep(86400); foo();You could assume that a day isn't exactly 24 hours, but it's close-ish to 24 hours. Nope, not even close.
And that assumes that we can treat an hour as a precise measure of time (we can't). On some systems, even a second is not a precise measure of time (second smearing).
To make things worse, those are "simple" edge cases.
Time is hard. I'm not sure if I can make any statement about time that is true.
It's worth highlighting that André is actually a volunteer contributor who managed to implement the whole thing by themselves.
anba implemented all of Temporal single-handedly, plus fixed up numerous places in the spec, plus migrated the implementation over some massive changes after other implementers discovered what a monster it all is. The original version of the spec kind of forced two separate internal implementation paths for everything, one for custom calendars and one for the built-in stuff, just to make the built-in one reasonably performant. That was a lot of work to implement, and a lot of work to remove. (I think ptomato shepherded the spec side of that?)
Fortunately, anba knows how to take a break, relaxing occasionally with minor tasks like rewriting large swathes of the JIT code generator to optimize the support on various platforms. He also gets plenty of nutrition, by ingesting entire specs and mind-melding with them.
[1] https://groups.google.com/g/comp.lang.python/c/Q2w4R89Nq1w
Parsing dates with anything other than fromisoformat feels totally backwards in comparison. We were using ciso8601 until fromisoformat was in the standard library. And now things are incredibly simple and reliable.
Given that the article refers to the "radical proposal" to bring these features to JavaScript came in 2018, surely Java's own solutions had some influence?
It’s not identical. The names of the “Plain” objects make a bit more sense to me than the “Local” names Java chose.
But overall easy to use and a fantastic improvement. I can’t wait to get to use it.
This is not the case for Temporal objects. Also, the temporal objects have functions on them, which, granted, makes it convenient to use, but a pain to pass it over the wire.
I'd clearly prefer a set of pure functions, into which I can pass data-only temporal objects, quite a bit like date-fns did it.
Instead the onus is on the developer to re-create the correct object they need on the other side. I don't believe this is problematic because if you know you're sending a Date, DateTime, MonthDay, YearMonth type from one side, then you know what type to rebuild from the ISO string on the other. Having it be automatic could be an issue if you receive unexpected values and are now dealing with the wrong types.
There is an example here in the docs of a reviver being used for Temporal.Instant https://tc39.es/proposal-temporal/docs/instant.html#toJSON
It's a string in a well specified string format. That's typically what you want for serialization.
Temporal is typed; but its serialization helpers aren't, because there's no single way to talk about types across serialization. That's functionality a serialization library may choose to provide, but can't really be designed into the language.
If you really want that, it's not very hard to design a pair of functions `mySerialize()`, `myDeserialize()` that's a thin wrapper over `JSON.parse`.
That said, I think the Temporal team made the right call here. Date-time logic is one of those domains where the "bag of data plus free functions" approach leads to subtle bugs because callers forget to pass the right context (calendar system, timezone) to the right function. Binding the operations to the object means the type system can enforce that a PlainDate never accidentally gets treated as a ZonedDateTime. date-fns is great but it can't give you that.
The serialization issue is solvable at the boundary. If you're using tRPC or similar, a thin transform layer that calls Temporal.Whatever.from() on the way in and .toString() on the way out is pretty minimal overhead. Same pattern people use with Decimal types or any value object that doesn't roundtrip through JSON natively. Annoying, sure, but the alternative is giving up the type safety that makes the API worth having in the first place.
If you do want the interchange format to be the one deserializing into specific runtime data structures, use YAML. YAML's tag syntax allows you to run arbitrary code inside YAML, which can be used for what you want.
This would probably best exist as a well-known wrapper around JSON itself.
CBOR (Concise Binary Object Representation) has JSON-like semantics with type extension support; with built in type extensions its much easier to get some agreement about registering certain magic type IDs to mean certain things. for example from a random google search for "cbor datetime" https://j-richter.github.io/CBOR/date.html; there's an IANA registry of type IDs: https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
however, it is binary.
Few people seem to use it outside of Amazon tho
The real drawback of the functional approach is UX, because it's harder to code and you don't get nice auto-complete.
But I'd easily pay that price.
This is effectively no different from Date:
serialize: date.toJSON()
deserialize: new Date(jsonDate)
in Temporal: serialize: instant.toJSON()
deserialize: Temporal.Instant.from(jsonDate)And as far as I know, date-fns deals with native Date instances, not “data-only objects.”
For example `JSON.parse(JSON.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})` won't work, because it misses the prototype and is no longer an instance of Temporal.PlainYearMonth.
This is problematic if you use tRPC for example.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I don't know if I'm missing something, but that's exactly how I'd expect it to compose. Does the following do what you wanted your snippet to do?
Temporal.PlainYearMonth.from(JSON.parse(JSON.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))))
JSON.stringify and JSON.parse should not be viewed as strict inverses of each other. `JSON.parse(JSON.stringify(x)) = x` is only true for a for a small category of values. That category is even smaller if parsing is happening in a different place than stringification because JSON doesn't specify runtime characteristics. This can lead to things like JSON parsing incorrect in JS because they're too large for JS to represent as a number.`devalue.parse(devalue.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})`
If not, that regardless of being plain data or a serialized object with functions, you'd still need to convert it to the type you want.
Which makes me wonder how it'll look like when interfacing with WASM. Better than Date?
I wonder if it has a chance to replace chrono and jiff in the rust ecosystem.
Congrats to all the champions who worked super hard on this for so long! It's been fun working on temporal_rs for the last couple years :)
const today = Temporal.PlainDate.from("2569-03-11[u-ca=buddhist]");
today.toLocaleString("en", { calendar: "hebrew" });
> Uncaught RangeError: calendars "buddhist" and "hebrew" aren't compatibleAll of which means there are many potential ambiguities in converting between calendars, and the combinatorial explosion possible means they probably only want you to convert between non-ISO8601 calendars and ISO8601. It would be too easy to get corner cases wrong otherwise and not notice, I'm sure. So to convert a date from Buddhist calender to Hebrew calender, you'd probably have to do Buddhist -> ISO8601, then ISO8601 -> Hebrew. (I haven't had time to test that for myself yet, I'll post a correction if that turns out to be wrong).
today.withCalendar('hebrew').toLocaleString("en", { calendar: "hebrew" });
// "22 Adar 6329"One of my favorite interview questions is asking a candidate to, piece meal, build a calendar. They start with Julian, and then write converters to and from other calendars. Any calendar can be converted to any other, by going through Julian
I got the idea from the book "calendrical calculations"
Safari confirmed as IE Spiritual successor in 2020+.
Right now the world needs a lot more Safari and Firefox users complaining about Chrome-only sites and tools than it does people complaining about Safari "holding the web back". Safari's problems are temporary. Chrome is the new Emperor and IE wasn't bad because it stopped, it was bad because it stopped after being the Emperor for some time. People remember how bad the time was after the Empire crumbled, but it's how IE took so many other things down with it that it is easier to remember the interregnum after IE crumbled than to remember the heyday when "IE-only websites are good enough for business" sounded like a good idea and not a cautionary tale.
Safari is also pretty popular on iPhones, in fact it has a full 100% market share. With browser updates tied to the OS, that means millions of devices have those "temporary" problems baked in forever.
There wouldn't be Chrome-only sites and tools if Safari wasn't holding the web back (no "quotes" needed, as that's precisely what they're doing).
> Safari's problems are temporary.
What are you talking about? They've been woefully behind for like a decade. Here's an excellent article on the topic: https://infrequently.org/2023/02/safari-16-4-is-an-admission...
And an entire series: https://infrequently.org/series/browser-choice-must-matter/
It's a matter of perspective. The safer perspective is: Safari isn't holding the web back, Chrome is moving too fast. Developers making Chrome-only sites and tools are moving too fast for the safety of web standards/web platform. Where one of the safety factors is "widely available in multiple implementations, not just a single browser".
> > Safari's problems are temporary.
> What are you talking about?
The point is that Safari may be moving slow, but it is still moving. It doesn't have enough users to hold the web back. It isn't "always a decade behind", it 's "a couple years to a couple months behind", depending on which caniuse or MDN Baseline approach you want to take.
There are some things Safari doesn't want to implement, but has registered safety or privacy or coupling reasons behind such things. Firefox is doing the same.
Safari isn't trapping website developers in "old standards forever", it is encouraging developers to use safe, private, stable choices. Chrome is "move fast and sometimes break things". Safari doesn't want to be that. That's useful for the web as a platform to have one or two browsers considering their implementations. It's a good reason to point out "Chrome-only" developers as being "too bleeding edge" (sometimes emphasis on the bleeding) and out of touch with standards and standards processes.
Given the number of chrome-only sites that block firefox and not safari i think there are other issues in front end land
This is funny to me; Java's util.Date was almost certainly a port of C's time.h API!
https://tc39.es/proposal-temporal/docs/cookbook.html
For example, calc days until a future date: https://tc39.es/proposal-temporal/docs/cookbook.html#how-man...
...or, compare meeting times across timezones: https://tc39.es/proposal-temporal/docs/cookbook.html#book-a-...
From the link, we can see Temporal does have separate Date/Time/Datetime types. ("PlainDate" etc)
- new Date() equivalent in Temporal is `const now = Temporal.Now.zonedDateTimeISO();`.
- Date.now() equivalent is `Temporal.Now.instant().epochMilliseconds`
- It’s PascalCase, where JS is mostly snakeCase.
- nanoseconds per default. who needs that except Bloomberg? It should have been an option
It’s definitely great all the efforts put in place, but it’s not going to be a replacement to Date which such a complicated design.
const D = new Temporal()
const t = new Interval({minutes:5})
const v = D.add(t) const D = Temporal.PlainDate.from("2020-06-16");
const t = Temporal.Duration.from({ day: 1 });
const v = D.add(t) // 2020-06-17
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...It's weird that they picked example code that is extremely non-accidentally doing this.
I didn't spot how Temporal fixes this. What happens when "now" changes? Does the library get updated and pushed out rapidly via browsers?
perhaps don't be hard on them, Chrome released this to stable 2 months ago.
Har har.
It will take years until this can be widely used as intended.
It's easy not to feel that loss as a big deal, but captured offsets can be very helpful for exactly debugging things like "what time did this user think this was?" versus time zone math (and DST lookups) from UTC. It can help debug cases where the user's own machine had missed a DST jump or was briefly on a different calendar or was traveling.
But a lot of the biggest gains in Temporal are the "Plain" family for "wall clock times"/"wall calendar dates" and breaking them apart as very separate data types. Does a UTC timestamp of "2026-02-01 00:00:00Z" mean midnight specifically and exactly or where you trying to mark "2026-02-01" without a time or timezone. Similarly I've seen data like "0001-01-01 12:10:00Z" mean "12:10" on a clock without the date or timezone being meaningful, but Temporal has a PlainTime for that. You can convert a PlainDate + a PlainTime + a Time Zone to build a ZonedDateTime, but that becomes an explicit process that directly explains what you are trying to do, versus accidentally casting a `Date` intended to be just a wall-clock time and getting a garbage wall-clock date.
Outside of scheduling UTC is the way.
To represent this you probably don't want a local date. Plain times [1] and plain date/times [2] are a better fit.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Temporal is a blessing.
It's fine for distributed logging and computer-only usage, but fails in obscure ways once humans, time zones, travel, laws, and/or daylight saving time get involved.
If you're scheduling events for humans, and can't immediately list the reasons your app is an exception to the above, store the time zone to be safe. You probably don't have big data, and nobody will notice the minuscule overhead.
A lesson I learned pretty early on is always use the date-time datatypes and libraries your language or platform gives you. Think very carefully before you roll your own with integer timestamps.
I get HFT, but I have a hard time comprehending a need for a Bloomberg Terminal to be talking in picoseconds, as in fractions of a billionth of a second.
const now = new Date();
The Temporal equivalent is: const now = Temporal.Now.zonedDateTimeISO();
Dear god, that's so much uglier!I mean, I guess it's two steps forward and one step back ... but couldn't they have come up with something that was just two steps forward, and none back ... instead of making us write this nightmare all over the place?
Why not?
const now = DateTime();And even with C#'s date API, I've seen errors. For example, a library that formatted datetime strings by merely writing them out and adding a "Z" to the end, assuming that they would always be receiving UTC datetimes — and elsewhere in the code, someone passing `DateTime.Now` to that library. (I'm guessing the dev who wrote that was in the UK and wrote it during winter time, otherwise he would have noticed that the timestamps were coming out wrong. If he was in the US they'd be 4-7 or 5-8 hours wrong depending on whether DST was in effect. But in the UK during winter, local time equals UTC and you might not notice that mistake).
This is another reason why Temporal's API making clear distinctions between the different types, and requiring you to call conversion functions to switch between them, is a good idea. That C# mistake would have been harder (not impossible, people can always misunderstand an API, but harder) if the library had been using Nodatime. And Temporal is based on the same design principles (not identical APIs, just simmilar principles) as Nodatime.
However my guess is that the spec designers saw this lack of specivity as part of the problem.
A key issue of dates and times is that we use them culturally in day to day use in very imprecise ways and much is inferred from the context of use.
The concepts of zoned time and “wall clock” time are irreducable and it’s likely much code will be improved by forcing the developer to be explicit with the form of time they want to use and need for their particular use case.
I think this is why it’s so explicitly specified right now.
But I agree; I’ve often struggled with how verbose js can be.
Maybe with time (pun intended), more syntactic sugar and shorter conventions can be added to expand what has been an incredible effort to fix deep rooted issues.
There's also other methods that return other types, like
const now = Temporal.Now.instant()
which isn't as bad.One could argue that the ugliness of the API intentionally reveals the ugliness of datetime. It forces you to really think about what you mean when you want "the current date time," which I think is one of the goals of the API.
Like the Temporal.Instant with the only difference that is now but in nanosecond. Would have been better to be Now with a suffix to indicate that it is more precise. Or even better, it is just the function you use or parameter that give the precision.
And why Now as the name space? I would expect the opposite, like python, you have something like Temporal.Date, and from there you get a date of now or a specific time, with or without timezone info, ...
It's like witnessing a meteor shower!
https://bloomberg.github.io/js-blog/post/intro/
I hope you like it ;-)And if it seems like a surprise, you can blame me for not publicising this kind of content earlier given how long we've been working in this area. Thankfully Jon Kuperman and Thomas Chetwin (plus others) found the time and energy to put this platform together.
Date is out, Temporal is in
Normally each engine writes its own implementation from the spec text, bugs and all. Then Test262 slowly flushes out the differences. With temporal_rs, the interop story is baked in from day one because there's literally one implementation. The tradeoff is that a bug in temporal_rs hits every engine simultaneously, but given that Temporal has 4,500 Test262 tests (more than String, Date, Function, BigInt, and Boolean combined), that risk seems manageable.
I'm curious whether this becomes a template for future large proposals. The spec size problem isn't going away -- Temporal is bigger than the entire Intl spec. If every engine has to independently implement proposals this large, the pipeline will keep bottlenecking on engine team bandwidth.