Top
Best
New

Posted by robpalmer 4 hours ago

Temporal: A nine-year journey to fix time in JavaScript(bloomberg.github.io)
329 points | 120 comments
alanning 4 minutes ago|
The Temporal Cookbook on TC39's site provides examples of how using the new API looks/feels:

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

VanCoding 2 hours ago||
A big step in the right direction, but I still don't like the API, here's why: Especially in JavaScript where I often share a lot of code between the client and the server and therefore also transfer data between them, I like to strictly separate data from logic. What i mean by this is that all my data is plain JSON and no class instances or objects that have function properties, so that I can serialize/deserialize it easily.

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.

jayflux 1 hour ago||
This was an intentional design decision. We wanted to make sure all the temporal types could be serialize/deserializable, but as you mentioned, you couldn't implicitly go back to the object you started with as JSON.parse doesn't support that.

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

perfmode 2 hours ago|||
This is a real pain point and I run into the same tension in systems where data crosses serialization boundaries constantly. The prototype-stripping problem you're describing with JSON.parse/stringify is a specific case of a more general issue: rich domain objects don't survive wire transfer without a reconstitution step.

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.

VanCoding 2 hours ago||
It's not that much about type safety. Since TypeScript uses duck typing, a DateTime could not be used as a ZonedDateTime because it'd lack the "timezone" property. The other way around, though, it would work. But I wouldn't even mind that, honestly.

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.

causal 1 hour ago|||
I'm with you on this. I worked on a big Temporal project briefly and I was really turned off by how much of the codebase was just rote mapping properties from one layer to the next.
qcoret 2 hours ago|||
All Temporal objects are easily (de)serializable, though. `.toString` and `Temporal.from` work great.
VanCoding 2 hours ago||
That's not what I mean. Even though it is serializable, it's still not the same when you serialize/deserialize it.

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.

flyingmeteor 2 hours ago|||
You would need to use the `reviver` parameter of `JSON.parse()` to revive your date strings to Temporal objects. As others have said, it's a simple `Temporal.from()`

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Bratmon 1 hour ago|||
Having to provide a complete schema of your json everywhere your json gets parsed negates the advantages of json.
cyral 2 hours ago|||
I've been doing this for so long and never knew there was a reviver param, thanks - that is super useful.
rimunroe 1 hour ago||||
> 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.

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.
gowld 2 hours ago|||
Would a plain data object be an instance of PlainYearMonth?

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.

Avamander 1 hour ago|||
> Especially in JavaScript where I often share a lot of code between the client and the server and therefore also transfer data between them, I like to strictly separate data from logic

Which makes me wonder how it'll look like when interfacing with WASM. Better than Date?

chrisweekly 2 hours ago|||
It should still be possible to continue using date-fns (or a similar lib) to suit your preference, right?
VanCoding 2 hours ago||
yes, sure. probably there will even pop up a functional wrapper around the temporal API occasionally. But would've been nice if it was like this from the start.
nekevss 3 hours ago||
Super happy to see Temporal accepted!

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

the__alchemist 47 minutes ago||
Maybe I will be able to move away from my custom/minimal DT lib, and ISO-8601 timestamp strings in UTC. JS datetime handling in both Date and Moment are disasters. Rust's Chrono is great. Python's builtin has things I don't like, but is useable. Date and Moment are traps. One of their biggest mistakes is not having dedicated Date and Time types; the accepted reason is "Dates and times don't exist on their own", which is bizarre. So, it's canon to use a datetime (e.g. JS "Date") with 00:00 time, which leads to subtle errors.

From the link, we can see Temporal does have separate Date/Time/Datetime types. ("PlainDate" etc)

julius_eth_dev 46 minutes ago||
Nine years is a long time, but honestly it tracks with how deeply broken Date has been since Brendan Eich cargo-culted java.util.Date in 1995. The real win with Temporal isn't just immutability or timezone support — it's that PlainDate and ZonedDateTime finally give us types that match how humans actually think about time. I've lost count of how many bugs I've shipped because Date silently coerces everything to UTC instants when half the time what you actually have is a "wall clock" value with no timezone attached.
plucas 3 hours ago||
Would have been interesting to connect back to Java's own journey to improve its time APIs, with Joda-Time leading into JSR 310, released with Java 8 in 2014. Immutable representations, instants, proper timezone support etc.

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?

apaprocki 3 hours ago||
I would characterize it more as Joda likely informed Moment.js, which better informed TC39 because it was within the JavaScript ecosystem. As we discussed in plenary today when achieving consensus, every programming language that implements or revamps its date time primitives has the benefit of all the prior art that exists at that instant. TC39 always casts a wide net to canvas what other ecosystems do, but isn't beholden to follow in their footsteps and achieves consensus on what is best for JavaScript. So my view is this more represents what the committee believes is the most complete implementation of such an API that an assembled group of JavaScript experts could design over 9 years and finalize in 2026.
mrkeen 2 hours ago||
Yep, JavaScript got the bad version from Java too!

https://news.ycombinator.com/item?id=42816135

bnb 4 hours ago||
Can't wait for it to land in the server-side runtimes, really the last thing preventing me from adopting it wholesale.
WorldMaker 1 hour ago||
Deno has had it behind the `--untable-temporal` flag for quite a few Minor versions now and the latest Minor update (because of TC-39's Stage 4 acceptance and V8 itself also marking the API as Stable) removed the requirement for the flag and it is out of the box.
apaprocki 3 hours ago|||
Node 26! Only a matter of time... :)
CharlesW 2 hours ago||
FWIW, I've been using it server-side via the js-temporal polyfill for some time, no issues.
bnb 2 hours ago||
ooh I'd not seen that yet, will have to take a look.
xp84 1 hour ago||
They travelled through time (forward, at 1X) by nine years to do this for us. I appreciate it.
zvqcMMV6Zcr 3 hours ago||
> Safari (Partial Support in Technology Preview)

Safari confirmed as IE Spiritual successor in 2020+.

WorldMaker 1 hour ago||
Slower to implement new features, but still implementing them, just makes it the new Firefox. IE's larger problem was how popular it had been before it stopped implementing new features. It was like if Google got bored with Chrome and decided to stop all funding on it. People would be stuck on Chrome for years after that investment stopped because of all the Chrome-specific things built around it (Electron, Puppeteer, Selenium, etc and so forth).

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.

nchmy 15 minutes ago||
> 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".

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/

cubefox 3 hours ago||
2026 A.D., still no support for native date pickers in mobile Safari.
CharlesW 2 hours ago||
Safari for iOS got native date pickers in 2012, and desktop Safari got them in 2021.
johncomposed 1 hour ago|
As a side note, huge fan of Promise.allSettled. When that dropped it cleaned up so much of the code I was writing at the time.
More comments...