Temporal: The 9-year journey to fix time in JavaScript
689 points
19 hours ago
| 46 comments
| bloomberg.github.io
| HN
wesselbindt
11 hours ago
[-]
I'm very happy about this. The fact that Temporal forces you to actually deal with the inherent complexities of time management (primarily the distinction between an instant and a calendar datetime) makes it incredibly difficult to make the mistakes that Date almost seems designed to cause. It's a bit more verbose, but I'll take writing a handful of extra characters over being called at 3AM to fix a DST related bug any day of the week.
reply
anowell
5 hours ago
[-]
Agreed. We've almost eradicated our usage of JS Date - fixing plenty of bugs along the way, and then I extracted thousands of lines of conversions and formatting from our production app (scheduling focused) into a temporal-fun package to make it Temporal more ergonomic for lots of common cases.

npmjs.com/package/temporal-fun

reply
lightwords
17 minutes ago
[-]
Word of warning Temporal relies on the Intl API for formatting, and support in Chrome is very limited due to their binary size constraints. As a result, you'll need to polyfill unsupported languages using format.js
reply
rafram
4 minutes ago
[-]

  $ du -sh '/Applications/Google Chrome.app'
   1.3G    /Applications/Google Chrome.app
reply
698969
38 minutes ago
[-]
That looks neat although your package is missing a link to the source repository.
reply
throwaway_12629
10 hours ago
[-]
Technically, you're not likely to to have to fix a DST bug at 3AM any day but Sunday.
reply
dskloet
5 hours ago
[-]
That's a great example of the kind of wrong assumption that makes dealing with dates and times so challenging.

Some countries start on a Friday or Saturday and until 2022 Iran could start any day of the week although never at 3AM.

reply
usefulcat
10 hours ago
[-]

    // call foo() one day from now:
    sleep(86400); foo();
reply
hdjrudni
6 hours ago
[-]
Sorry, sleep returned a Promise and you didn't await it. You called foo() immediately.
reply
koakuma-chan
4 hours ago
[-]
What you want there is to stop saying "day" and instead say "24 hours." This way the code is correct and you don't need to deal with time weirdness.
reply
danbruc
1 hour ago
[-]
No, because if I want something to happen everyday at 12 o'clock, I have to wait for one day, if I wait for 24 hours, I will be off by an hour for half of the year.
reply
loloquwowndueo
10 minutes ago
[-]
Only if you live in one of the brain dead countries that observe the dst anachronism.
reply
mklepaczewski
39 minutes ago
[-]
You seem to assume that a day always has 24 hours. Common (but not only) non-24h day lengths are: - 23 hours - 25 hours - 24 hours 1 second - 23 hours 59 minutes 59 seconds

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.

reply
SOLAR_FIELDS
9 hours ago
[-]
In this day and age when a natural language query can produce the most AbstractBeanFactoryFactoryBeanFactory boilerplate at the same rate as a much more concise equivalent, does verbosity matter as much?
reply
Vinnl
13 hours ago
[-]
> Whilst Firefox was able to implement Temporal as it was being specced - thanks to the great work of André Bargull (known online as Anba)

It's worth highlighting that André is actually a volunteer contributor who managed to implement the whole thing by themselves.

reply
sfink
7 hours ago
[-]
Considering how prolific anba is, the only way we know he isn't an LLM is because he'd have to be several generations more advanced than the current SOTA. (It is possible that he might be an LLM from a few decades in the future, considering the connection to Temporal.)

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.

reply
Animats
13 hours ago
[-]
I went through a similar decade-long fire drill around ISO8601 date parsing in Python.[1] Issue started in 2012, and after about a decade a solution was in the standard library.

[1] https://groups.google.com/g/comp.lang.python/c/Q2w4R89Nq1w

reply
baliex
13 hours ago
[-]
Thank you thank you thank you.

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.

reply
plucas
18 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?

reply
apaprocki
18 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.
reply
MBCook
9 hours ago
[-]
Well said. As a Java programmer who hasn’t touched Temporal yet in JS it is extremely similar to the new Java types like… ZonedDateTime.

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.

reply
mrkeen
17 hours ago
[-]
Yep, JavaScript got the bad version from Java too!

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

reply
VanCoding
17 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.

reply
jayflux
15 hours 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

reply
frde_me
10 hours ago
[-]
So it's intentional to make people pass down raw strings versus making the communication safe(er) by default?
reply
Hasnep
9 hours ago
[-]
There are no date, time or datetime types in JSON, so you'll have to serialise it to a string or an int anyway, and then when deserialising you'll need to identify explicitly which values should be parsed as dates.
reply
Manishearth
10 hours ago
[-]
.... we're talking about serialization here. "convert to a raw string" is sort of the name of the game.

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.

reply
hdjrudni
5 hours ago
[-]
You realize that JSON isn't just for JavaScript to JavaScript communication, right? Even if you had a magical format (which doesn't make sense and is a bad idea to attempt to auto-deserialize), it wouldn't work across languages.

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

reply
jitl
6 hours ago
[-]
its gotta become bytes somehow
reply
perfmode
17 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.

reply
TimTheTinker
13 hours ago
[-]
Sounds like we need an extended JSON with the express intent of conveying common extended values and rich objects: DateTime instants (with calendar system & timezone), Decimal, BigInt, etc.
reply
mrighele
27 minutes ago
[-]
I think a more practical and compatible approach is to keep json as it is, and use a side channel (e.g. an openapi spec) to convey metadata. Then it is up to the client to decide that a date returned as a string is a date or string, or to create a specific class instead of a generic object
reply
sheept
13 hours ago
[-]
I disagree: this is not unlike including the schema in the JSON itself. This should be handled by the apps themselves, since they would have to know what the keys mean regardless.

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.

reply
TimTheTinker
12 hours ago
[-]
I'm not talking about something arbitrarily extensible or compound values like vectors or lat/lon. Just a few more common data types -- primitive-like values that frequently need to be passed around.

This would probably best exist as a well-known wrapper around JSON itself.

reply
jitl
6 hours ago
[-]
there are a zillion of these "json pro" kind of things: superjson, devalue, capnweb, all with slightly different ideas about how to lower high-level semantics to json's available types. it's so easy to do this kind of thing, its a real https://xkcd.com/927/ situation.

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.

reply
akdev1l
10 hours ago
[-]
Amazon ION is kind of this?

Few people seem to use it outside of Amazon tho

reply
VanCoding
17 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.

reply
TimTheTinker
13 hours ago
[-]
Updating JSON.parse() to automatically create Temporal objects (from what shape of JSON value?) without a custom reviver would be a step too far, in my opinion.

This is effectively no different from Date:

  serialize: date.toJSON()
  deserialize: new Date(jsonDate)
in Temporal:

  serialize: instant.toJSON()
  deserialize: Temporal.Instant.from(jsonDate)
reply
nulltrace
7 hours ago
[-]
The serialization thing is real but I don't think OOP vs functional is the actual issue here. JSON has no date type, period. You JSON.stringify a Date, get an ISO string, and hope whoever's parsing remembers to reconstruct it. Temporal doesn't fix that part, but at least when you do reconstruct you're saying "this is a ZonedDateTime" vs "this is an Instant" instead of everything being one ambiguous Date object.
reply
tshaddox
12 hours ago
[-]
Don’t JavaScript Date instances have the same problem? Date implements toJSON, but when parsing JSON you’ll have to manually identify which string values represent Dates and convert them back to Date instances. The exact same is true of Temporal (e.g. Instant).

And as far as I know, date-fns deals with native Date instances, not “data-only objects.”

reply
qcoret
17 hours ago
[-]
All Temporal objects are easily (de)serializable, though. `.toString` and `Temporal.from` work great.
reply
VanCoding
17 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.

reply
flyingmeteor
17 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...

reply
foresterre
10 hours ago
[-]
Depending on your needs (i.e. how you would otherwise use your output jspn), using the reviver can have a significant impact on performance. JSON.parse itself is hyper-optimized. At the company I work we used the reviver for almost exactly this, but profiling showed that using the reviver had enormous impact on performance. We cut it out, and won in the seconds of performance for some large json's.
reply
cyral
17 hours ago
[-]
I've been doing this for so long and never knew there was a reviver param, thanks - that is super useful.
reply
Bratmon
16 hours ago
[-]
Having to provide a complete schema of your json everywhere your json gets parsed negates the advantages of json.
reply
true_religion
14 hours ago
[-]
The main advantage of json is that it’s human readable and writable. Beyond that, it has no notion of user created data types so anyone using it has to do custom unmarshalling to get a type apart from sting, number, dict and list.
reply
hdjrudni
5 hours ago
[-]
Even if you don't explicitly provide a schema, you implicitly still have one. The recipient needs to know what you're sending them. Unless maybe you want to start parsing JSON payloads with an LLM.
reply
hueho
14 hours ago
[-]
Most JSON libraries in typed languages require this for data binding to complex types though.
reply
Bratmon
11 hours ago
[-]
Which is why many developers only use JavaScript Object Notation for JavaScript objects, and only JavaScript objects that can losslessly be written as JSON. Which this proposal explicitly does not support.
reply
afiori
6 hours ago
[-]
{"$temporal_type":"PlainYearMonth","$data":"........"}
reply
rimunroe
16 hours 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.
reply
aubergene
11 hours ago
[-]
This seems more to do with how JSON works than Temporal. There are libraries such as Devalue which will handle this for you

`devalue.parse(devalue.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})`

https://www.npmjs.com/package/devalue

reply
tshaddox
12 hours ago
[-]
This is also true of JavaScript Date instances, so I’m curious what solution you had that did work with raw JSON stringify and parse.
reply
cyberrock
9 hours ago
[-]
Then you're talking about changing JSON.parse to start parsing some schema as a type instead of object, which would break compatibility.
reply
jonathrg
9 hours ago
[-]
The JSON types are string, number, boolean, null, object and array. So how could the suggested code possibly work? Do you want JSON.parse to do arbitrary code execution like Python's pickle?
reply
gowld
17 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.

reply
causal
16 hours 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.
reply
Avamander
16 hours 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?

reply
chrisweekly
17 hours ago
[-]
It should still be possible to continue using date-fns (or a similar lib) to suit your preference, right?
reply
VanCoding
17 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.
reply
avandecreme
3 hours ago
[-]
I didn't know about https://docs.rs/temporal_rs/latest/temporal_rs/

I wonder if it has a chance to replace chrono and jiff in the rust ecosystem.

reply
nekevss
18 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 :)

reply
ramon156
55 minutes ago
[-]
TIL temporal_rs was a thing. Not to be biased, but I think it's awesome how much Rust is used in the JS ecosystem. I saw Vite using oxc in some parts. Love it!
reply
QGQBGdeZREunxLe
9 hours ago
[-]
Noticed that converting between certain calendars is not supported. Was that choice intentional?

    const today = Temporal.PlainDate.from("2569-03-11[u-ca=buddhist]"); 
    today.toLocaleString("en", { calendar: "hebrew" });
    > Uncaught RangeError: calendars "buddhist" and "hebrew" aren't compatible
reply
rmunn
9 hours ago
[-]
Converting between solar-based and lunar-based calendars is fraught with potential for ambiguity. The Buddhist calendar is a solar calendar, while the Hebrew calendar is lunar-based. So converting between dates in the Buddhist calendar and the international-standard (ISO 8601) calendar is typically easy (give or take some subtleties I won't go into for reasons of length). But converting between the Hebrew calendar and the ISO 8601 calendar, or the Buddhist calendar, involves figuring out when the new moon will be — and since the lunar cycle is 29 or 30 days, 12 lunar months add up to 354 days. So the lunar calendars, including the Hebrew calendar, typically add a "leap month" every two or three years in order to track the sidereal year.

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

reply
fabon
3 hours ago
[-]
I think this is intentional design. Anyway we can convert `Temporal.PlainDate` to other calendars explicitly (I believe explicitness is good here).

  today.withCalendar('hebrew').toLocaleString("en", { calendar: "hebrew" });
  // "22 Adar 6329"
reply
paradox460
9 hours ago
[-]
Certainly surprising

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"

reply
zvqcMMV6Zcr
18 hours ago
[-]
> Safari (Partial Support in Technology Preview)

Safari confirmed as IE Spiritual successor in 2020+.

reply
WorldMaker
16 hours 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.

reply
supernes
2 hours ago
[-]
The biggest problem with IE from a developer standpoint wasn't the slow feature release cadence, it was that the features it did have worked differently from standards-based browsers. That's very much the position of Safari/WebKit today - code that works across all other engines throws errors in WebKit and often requires substantial changes to resolve.

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.

reply
nchmy
15 hours 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/

reply
WorldMaker
14 hours ago
[-]
> 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).

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.

reply
tacticus
8 hours ago
[-]
> There wouldn't be Chrome-only sites and tools if Safari wasn't holding the web back

Given the number of chrome-only sites that block firefox and not safari i think there are other issues in front end land

reply
cubefox
18 hours ago
[-]
2026 A.D., still no support for native date pickers in mobile Safari.
reply
CharlesW
17 hours ago
[-]
Safari for iOS got native date pickers in 2012, and desktop Safari got them in 2021.
reply
wpollock
17 hours ago
[-]
> "It was a straight port by Ken Smith (the only code in "Mocha" I didn't write) of Java's Date code from Java to C."

This is funny to me; Java's util.Date was almost certainly a port of C's time.h API!

reply
pbowyer
2 hours ago
[-]
If you were creating a new programming language in 2026, which DateTime/Temporal library would you copy and why?
reply
bnb
19 hours ago
[-]
Can't wait for it to land in the server-side runtimes, really the last thing preventing me from adopting it wholesale.
reply
WorldMaker
16 hours 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.
reply
apaprocki
18 hours ago
[-]
Node 26! Only a matter of time... :)
reply
CharlesW
17 hours ago
[-]
FWIW, I've been using it server-side via the js-temporal polyfill for some time, no issues.
reply
bnb
17 hours ago
[-]
ooh I'd not seen that yet, will have to take a look.
reply
thinkindie
1 hour ago
[-]
coming from PHP, it's incredible how many times I've been bitten by glitches with managing time with JS clientside.
reply
xp84
16 hours ago
[-]
They travelled through time (forward, at 1X) by nine years to do this for us. I appreciate it.
reply
sriramgonella
9 hours ago
[-]
Time handling has always been one of those areas where things look simple until you hit production edge cases.Time zones, DST transitions, leap seconds, and inconsistent Date APIs across environments have caused subtle bugs in many systems I've worked on. The promise of Temporal feels less about new features and more about making time handling explicit and predictable, which is probably what developers needed all along.
reply
tefkah
1 hour ago
[-]
ai;dr
reply
alanning
14 hours 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-...

reply
the__alchemist
15 hours 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)

reply
jitl
5 hours ago
[-]
I wouldn't be surprised to see the Rust ecosystem eventually move to Temporal's api, given v8 (Chrome) adopted Boa's rust implementation temporal_rs (https://docs.rs/temporal_rs/latest/temporal_rs/), see burntsushi's arguments for the need of a better datetime handling library in Rust (https://github.com/BurntSushi/jiff/blob/master/DESIGN.md#why...). I'm not sure his jiff create will be the one, i think temporal_rs has become the authoritative implementation.
reply
apaprocki
13 hours ago
[-]
Yes, please try! One of the main motivations for doing all this work is to slim down both the amount of code that has to be delivered and executed by providing everything that's needed by the platform. In addition, you're slimming the potential bug/attack surface as well, which is always nice.
reply
corentin88
3 hours ago
[-]
Temporal is a good idea, but the API is too complicated for broad adoption:

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

reply
klysm
9 hours ago
[-]
Bravo to the designers of this library. It’s well implemented and I’ve been using the poly fill for years now
reply
tmpfile
11 hours ago
[-]
I’d like to have interval types for example

   const D = new Temporal()
   const t = new Interval({minutes:5})
   const v = D.add(t)
reply
plastic041
10 hours ago
[-]
That's Duration!

    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...
reply
nikeee
11 hours ago
[-]
It is called Duration.
reply
jitl
6 hours ago
[-]
a much less ambiguous name than Interval
reply
kemayo
17 hours ago
[-]
> Developers would often write helper functions that accidently mutated the original Date object in place when they intended to return a new one

It's weird that they picked example code that is extremely non-accidentally doing this.

reply
Griffinsauce
3 hours ago
[-]
An example that is hard to follow defeats the point. It's just showing what pattern is possible and you can imagine the abstraction layers and indirection that would make it happen accidentally.
reply
Banou
6 hours ago
[-]
Temporal is nice but I've tried using it and had terrible performances. Hope the implementations get better in the future.
reply
redbell
18 hours ago
[-]
Oh, for a second, TeMPOraL (https://news.ycombinator.com/user?id=TeMPOraL) came to my mind!
reply
SoftTalker
16 hours ago
[-]
It's been a while since I worked in JS but dealing with dates/times, and the lack of real integer types were always two things that frustrated me.
reply
johncomposed
16 hours 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.
reply
philipallstar
17 hours ago
[-]
> have to agree on what "now" means, even when governments change DST rules with very little notice.

I didn't spot how Temporal fixes this. What happens when "now" changes? Does the library get updated and pushed out rapidly via browsers?

reply
WorldMaker
16 hours ago
[-]
Right, browsers own it instead of websites needing to rebuild Moment.js bundles. Additionally, most browsers pass the ownership further to the user's OS as the IANA timezone database is a useful system-level service and best updated at the cadence of OS "required" updates.
reply
nekevss
17 hours ago
[-]
Typically time zone data is updated in IANA's time zone database. That data would need to be updated in the implementation. In this case, the browser would need to update their time zone data.
reply
apaprocki
13 hours ago
[-]
Depending on the situation, the data lives either within the browser or within the OS. Chrome releases ship versions of tzdata that correspond to the version of tzdata shipped with the ICU it uses, and they do backport updates to prior Chrome releases within a certain window. Apple has a sideband way of deploying tzdata to all devices that doesn't appear via the normal Software Update mechanism. So it all depends on which particular OS/browser combo you're interested in and the decisions those owners made.
reply
stevefan1999
9 hours ago
[-]
I thought this Temporal is about the durable execution Temporal, well it is about time...
reply
tracker1
15 hours ago
[-]
Looking at the caniuse results... f*king Safari (and Opera)...

https://caniuse.com/temporal

reply
jitl
5 hours ago
[-]
They've been working on support for a while (years), eg this 2022 commit. https://github.com/WebKit/WebKit/commit/e6717cdeb6a841f4b1f6...

perhaps don't be hard on them, Chrome released this to stable 2 months ago.

reply
agos
15 hours ago
[-]
I usually am not too harsh on Safari on implementation of new features but this is a bummer, and reflects poorly on them
reply
beezlewax
15 hours ago
[-]
And I have to support safari while dealing with all the problems that are mentioned in this article. Maybe there is a polyfill.
reply
jitl
5 hours ago
[-]
reply
dmix
11 hours ago
[-]
there's plenty of polyfills for every new JS idea on the roadmap
reply
normie3000
18 hours ago
[-]
No mention of JodaTime?
reply
samwho
18 hours ago
[-]
Thanks for linking to my silly little quiz in the article! :)
reply
kemitchell
14 hours ago
[-]
> The first proposal I worked on was Promise.allSettled, which was fulfilling.

Har har.

reply
nehalem
2 hours ago
[-]
And yet another modernisation of the web platform sabotaged by Apple and their misguided (malicious?) refusal to update their devices continually.

https://caniuse.com/temporal

It will take years until this can be widely used as intended.

reply
halapro
11 minutes ago
[-]
The spec hasn't even reached stage 4 yet. Chrome only added support 58 days ago. Safari already added support in their alphas.
reply
darepublic
17 hours ago
[-]
My playbook for JavaScript dates is.. store in UTC.. exchange only in UTC.. convert to locale date time only in the presentation logic. This has worked well for me enough that Im skeptical of needing anything else
reply
ibejoeb
15 hours ago
[-]
For recording instantaneous events, that's usually sufficient. It's often not enough for scheduling. You can always present UTC or any other zone relative to some other zone, but you need to know that zone. Maybe you're going to a conference in another region and you want to know the time of a talk in that zone because that's more important than your zone. You either need to couple the zone with the time itself, or you need to refer to it. There are good reasons either way. Having an atomic time+zone type is basically trading space for time. When its embedded, you can just use it, which can be better than assuming UTC and then looking up the zone based on, say, the location of the venue.
reply
tshaddox
11 hours ago
[-]
That generally works for timestamps (Temporal Instant). But it doesn’t work for representing calendar dates with no time information (Temporal PlainDate) unless you add an additional strict convention like “calendar dates are always represented as midnight UTC”).
reply
WorldMaker
15 hours ago
[-]
Storing in UTC is lossy. You've lost information about the event's original UTC offset, at the very least, and probably also its original time zone. Most backends today have good ways to round-trip offset information, and still compare dates easily (as if they were normalized to UTC). Some backends can even round-trip timezone information in addition to offsets.

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.

reply
andrewl-hn
17 hours ago
[-]
The only time you need local dates is for scheduling. Stuff like “Report KPIs for each shift. Shifts start at 8:00 local time.”, or “send this report every day at 10:00 local time”, or “this recurring meeting was created by user X while they were in TimeZone Z, make sure meetings follow DST”.

Outside of scheduling UTC is the way.

reply
1-more
13 hours ago
[-]
The pathological case with scheduling is: It's 2015. You live in NYC. Your pal in Santiago, Chile says "hey next time you're here let's hang out." You say "great, I have a business trip there next April. Let's have dinner at 7pm on the 15th." They agree. You enter it into your calendar. If you store it as UTC, you're going to show up to dinner at the wrong time—the DST rules changed in between when you talked and when you expected dinner to happen. If you'd stored it as a local time with tzdb name America/Santiago you'd be there at the correct local time.
reply
masfuerte
15 hours ago
[-]
> Report KPIs for each shift. Shifts start at 8:00 local time.

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

reply
recursive
15 hours ago
[-]
It does work quite well. Sometimes you need a time zone to go with it. It might not be common, but sometimes you need to know the local time in a particular zone, which is not necessarily where the user is. I work on software that works with local times in arbitrary time zones. We submit data in a schema over which we have no control, which must include such local times that may or may not be in the time zone of the server or the current client machine.
reply
themafia
16 hours ago
[-]
I have a scheduling system that allows users to specify recurring events. "Every Monday at 2pm." Which needs to be understood in the native timezone of that user and needs to be capable of being displayed in that timezone for all viewers or optionally in the native timezone of the viewing user.

Temporal is a blessing.

reply
KingMob
4 hours ago
[-]
"Just use UTC" is another, albeit more subtle, falsehood programmers believe about date/time.

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.

reply
NooneAtAll3
17 hours ago
[-]
why UTC and not epoch then?
reply
SoftTalker
16 hours ago
[-]
Epoch (a/k/a "unix timestamps") are OK when you just need an incrementing relative time. When you start converting them back and forth to real calendar dates, times, with time zones, DST, leap seconds, etc. the dragons start to emerge.

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.

reply
lpa22
17 hours ago
[-]
Same here, this is the way
reply
sharktheone
18 hours ago
[-]
Very happy for it finally being there!
reply
bpiroman
12 hours ago
[-]
I wish JavaScript held onto the name Mocha :)
reply
FireBeyond
9 hours ago
[-]
> Higher-precision timestamps (nanoseconds, at a minimum)

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.

reply
jon_kuperman
19 hours ago
[-]
What a journey!
reply
hungryhobbit
17 hours ago
[-]
From the article:

    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();
reply
rmunn
9 hours ago
[-]
I'd argue that `new Date()` returning the current time is a design mistake, and it should at least have been something like `DateTime.now()`. (Especially because it's called a date but it actually returns a timestamp: the footgun potential is large). C#'s date API isn't the best design (otherwise [NodaTime](https://www.nodatime.org/) wouldn't have been necessary) but it at least got some things right: you don't get the current time by doing `new DateTime()`, you get it by referencing `DateTime.UtcNow` for UTC (almost always what you want), or `DateTime.Now` for local time (which is sometimes what you want, but you should always stop and think about whether you really want UTC).

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.

reply
tshaddox
11 hours ago
[-]
That’s uglier because, if you were previously doing new Date(), you almost certainly don’t want a zonedDateTime. You almost certainly want an Instant.
reply
evolve2k
14 hours ago
[-]
Firstly, I really want this also and am supportive of an opinionated decision to put something at say Temporal.DateTime() that would be logical for developers to use ‘most of the time’.

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.

reply
sheept
13 hours ago
[-]
I think that it's nice it's explicit that the method returns the current instant, rather than some other zero value.

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.

reply
Bratmon
12 hours ago
[-]
What would have been wrong with Temporal.now() returning a sensible value?
reply
ejplatzer
9 hours ago
[-]
What counts as a sensible value? The whole point of the library is to be explicitly about what kind of date/time data you're working with, because different kinds of data have to be handled in very different ways.
reply
greatgib
1 hour ago
[-]
I also find it super more complicated and messy than what you can find in another language without proper justification.

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

reply
tmpfile
11 hours ago
[-]
Or const now = new Temporal();
reply
sourcegrift
16 hours ago
[-]
If you give me your background I'll explain in longer terms but in short it's about making the intent clear and anyone who understands s modicum of PL theory understands why what's a constant is so and what's a function is so.
reply
themafia
16 hours ago
[-]
I'm a programmer. I'm a human. Perhaps we should also allow for some "human theory" inside our understanding.
reply
Bratmon
16 hours ago
[-]
I'm excited for this conversation. If you see someone respond to a developer ergonomics complaint with "If you give me your background I'll explain in longer terms... anyone who understands s modicum of PL theory" you're about to see some legendary bullshit.

It's like witnessing a meteor shower!

reply
ChrisArchitect
18 hours ago
[-]
Aside: Bloomberg JS blog? ok.
reply
robpalmer
16 hours ago
[-]
Yep. You can learn more about why we created this new blog here:

  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.

reply
deepsun
17 hours ago
[-]
Bloomberg has a pretty large software engineering department, including a lot of offshore contractors. Similar to Walmart Labs that does cool stuff as well, despite being part of a retail chain (retail industry typically sees SWEs a cost, not asset).
reply
ChrisArchitect
17 hours ago
[-]
oh, just meant it was a new tech blog from them.
reply
jon_kuperman
15 hours ago
[-]
Yes! Brand new!
reply
wiseowise
17 hours ago
[-]
What surprises you? Terminal UI is written in JS using Chromium. It’s not just plain Chromium, but it’s still funny that it’s pretty much same approach as universally (according to HN and Reddit) hated Electron.

https://youtu.be/uqehwCWKVVw?is=wBijGwdD2k2jIOu7

reply
virgil_disgr4ce
18 hours ago
[-]
Pretty big fan of Temporal. Been using the polyfill for a while. Very nice to use a modern, extremely well thought-through API!
reply
NooneAtAll3
17 hours ago
[-]
so Temporal is copying cpp's std::chrono?
reply
andrewl-hn
17 hours ago
[-]
More like a copy of Java’s JSR310, which in turn took many years to get right.
reply
ventuss_ovo
15 hours ago
[-]
interesting point about immutability
reply
ChrisArchitect
18 hours ago
[-]
A good article and discussion from January:

Date is out, Temporal is in

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

reply
shevy-java
4 hours ago
[-]
Good. Now someone has to fix JavaScript.
reply
newzino
17 hours ago
[-]
The buried lede in this article is temporal_rs. Competing browser engines have never shared an implementation library like this before. V8, Boa, and others all running the same Rust core for a major language feature -- that's a real departure from how standards get implemented.

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.

reply