Temporal: The 9-year journey to fix time in JavaScript(bloomberg.github.io) |
Temporal: The 9-year journey to fix time in JavaScript(bloomberg.github.io) |
I didn't spot how Temporal fixes this. What happens when "now" changes? Does the library get updated and pushed out rapidly via browsers?
Date is out, Temporal is in
perhaps don't be hard on them, Chrome released this to stable 2 months ago.
Not as bad as IE of old, but still seems to similarly be the odd one out always lagging half a year or more behind.
Har har.
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.
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();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.
Temporal is a blessing.
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...
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.
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!
You raise a very important question: why is `Temporal.Now.zonedDateTimeISO()` so verbose? After 10 years of work on Temporal, you can assume that every API design and naming decision has been argued about, often many times. I'll try to summarize the reasoning and history below to explain how we got there.
There's really five questions here. There's no perfect order to answer them because the answers overlap somewhat, so you may want to read to the end until disagreeing. :-) I will have to split this response up into two posts because it's long.
1. Why do we need `Temporal.Now.`? Why not just `Temporal.`?
The TC39 members working on security required us to have a forward-compatible way to override and/or prevent access to information about the local machine. With the current design, a hosting environment can monkey-patch or remove `Temporal.Now`, and regardless of what Temporal changes in the future that local-machine info will still be blocked or overridden. But if we'd spread methods across each Temporal type (e.g. `Temporal.PlainDate.today()`, `Temporal.Instant.now()`) then a new Temporal type added in the future could allow jailbreaking to get local machine info.
This isn't just a security concern. Environments like jest may want to control the local time that tests see. Hosters of JS code like https://convex.dev/ might want to override the local time so that transactions can be re-run deterministically on another node. And so on.
2. Why a `Temporal.*` namespace? Why not just `ZonedDateTime`, `PlainTime`, etc. in the global namespace?
If `Date` didn't exist, we'd probably have done exactly this, and argued harder about (1) above, and we maybe could have won that argument!
But we were worried about the confusion between `Date` and another type named `PlainDate` (or whatever we could have called the date-only type). By putting all Temporal types in a namespace, it was much harder to be confused.
A secondary benefit of a namespace was to expose developers (via autocomplete in editors and browser dev tools) to the full set of types available, so that developers would pick the right type for their use case. But the `Date` conflict was the main reason.
We could have made the types TemporalDate, TemporalTime, etc. but that shaves only one character and violates (1) so there was no chance that would move forward. So we went with a namespace.
3. Why is it `Temporal.Now.zonedDateTimeISO()` not `Temporal.now()`?
As @rmunn and others discuss below, one of the core tenets of Temporal (following in the footsteps of Java, Noda time, Rust, and many others) was that developers must figure out up-front what kind of date/time data they want, and then pick the appropriate Temporal type for that data.
For a canonical example of why this is needed, just think of the thousands (millions?) of times that JS developers or end users have ended up with off-by-one-day errors because `new Date()` reports a different date depending on which time zone you're in, even if all you care about is displaying a date where the time zone is not relevant.
It's a reasonable argument that because `Temporal.ZonedDateTime` represents the superset of all Temporal types, it should occupy a privileged position and should be the default Temporal type for something like `Temporal.now()`. Given that I proposed the initial design of the ZonedDateTime type (https://github.com/tc39/proposal-temporal/pull/700) I'm understandably sympathetic to that point of view!
But there are performance concerns. Accessing the system time zone and calendar not free, nor is the extra 2+ bytes to store them in a `Temporal.ZonedDateTime` implementation compared to a cheaper type like `Temporal.Instant`. This also would have popularized a pattern of calling `Temporal.now().toPlainTime()` which creates two objects vs. just creating the desired type in one call via `Temporal.Now.plainTime()`.
(cont'd in next post)
This is a fun story. Originally there was no `Temporal.ZonedDateTime`, and the type now called `Temporal.PlainDateTime` was called `Temporal.DateTime`. When we added a Temporal type with a datetime + time zone (its placeholder name was LocalDateTime), we needed to figure out naming for the date+time types so that developers would pick the right one: if they knew the time zone then they should pick one, and if they didn't know the time zone then they should pick the other.
The biggest danger was this: if the zoneless type had been named `Temporal.DateTime`, then developers new to JS would almost certainly use it because it sounds like it should be the default. This exposes programs to subtle time zone bugs that only manifest 2x per year when time zones switch from DST/"Summer Time" to standard time and back again.
I'd seen my previous company take two years (!!!) and thousands of hours of developer time to fix exactly this bug because many years before that our 23-year-old founders didn't realize that `DATETIME` in SQL Server was a zoneless type that would skip or lose an hour twice per year.
I was determined to reduce the chance of the same mistake with Temporal by ensuring that the zoneless type would not (like in SQL Server) look like the default type that everyone should use for date/time data.
Naming is hard! My preference was to use "DateTime" for the zoned type, and an something obviously non-default like "ZonelessDateTime" for the other one. Other champions felt the opposite: that because we were adding a time zone then its name should be longer. Eventually we settled on "Plain*" for all zoneless types and "ZonedDateTime" for the DST-safe one.
You can read 150 comments in https://github.com/tc39/proposal-temporal/issues/707 if you want to understand the arguments made on all sides of this issue.
5. Why is it `Temporal.Now.zonedDateTimeISO()` not `Temporal.Now.zonedDateTime()`?
I tried. The presence of "ISO" in method names (of `Temporal.Now` and a few other Temporal methods) is my biggest regret in the 6 years I spent working on this proposal. That said, it could have been even worse. Here's some history.
"ISO" in this context refers to the ISO 8601 calendar, as opposed to the Chinese calendar, the Hebrew Calendar, the Coptic calendar, the Persian calendar, one of several Islamic calendars, and of course the Gregorian calendar which is effectively the same as ISO 8601 except the former uses BC/AD eras.
One of the features of Temporal is to support both Gregorian and non-Gregorian calendars. Most of the world's population uses a non-Gregorian calendar for some purposes, like determining holidays or for official government documents. So it's convenient that user can write code like this:
// What's the date of the next Chinese new year? Temporal.Now.zonedDateTimeISO() .withCalendar('chinese') .add({ years: 1 }) .with({ month: 1, day: 1}) .toPlainDate() .withCalendar('gregory') .toLocaleString() // => '2/6/2027'
In 10 years of working on Temporal, the argument that took more hours and frustration was whether we should default to the ISO calendar in Temporal APIs.
One side of this argument was this: in localization, you never want a default locale because then developers won't write or test their code to work in other locales. For languages this is exactly the right approach because there is no default language worldwide that everyone knows. Therefore, we should not have a default calendar in Temporal.
The other side of that argument was this: the Gregorian calendar is used in almost every country in the world for almost all use cases that software will need to handle. Exceptions prove the rule, because the most common use of non-Gregorian calendars in software is building apps that correlate Gregorian with another calendar so that multi-calendar users can see both dates side-by-side. Therefore, we should make era-less Gregorian (called the ISO 8601 calendar in JS) the default calendar in Temporal.
After many hours of discussion, it was clear that neither side could convince the other. So we made a painful compromise: we'd have two variations of APIs. Methods like `Temporal.Now.zonedDateTimeISO()` would use the ISO 8601 calendar by default, and methods like `Temporal.Now.zonedDateTime()` would require the user to provide a calendar or would throw a `RangeError`.
This compromise was not ideal because it would confuse developers who'd call the shorter ISO-less methods and end up with exceptions at runtime. In 2023 we had an opportunity to improve it somewhat, because Temporal had to trim about a third of the surface area of the the proposal (IIRC, about 100 properties and methods!) to address concerns from browser implementers about the RAM and download size impact of so much new code on devices like Apple Watch and low-end Android phones.
As part of that trimming exercise, we decided to remove one of the ISO-default vs. calendar-required method pairs. Thankfully, the ISO-default variant was retained. I lobbied to remove "ISO" from the method names, now that differentiating them from their calendar-required counterparts was not required anymore. This lobbying didn't succeed. See https://github.com/tc39/proposal-temporal/issues/2846 for details.
I'm not happy with the extra `ISO` that millions of developers and AI agents will have to be typing in the decades to come. But in the grand scheme of things this is a small price to pay for a dramatically improved date/time/timezone/calendar API. Building standards is inherently a team sport, and if you're winning every argument then it's probably an unhealthy team. Overall I'm quite happy where we ended up with Temporal, even if some of the decisions didn't match what I thought would be best.
Anyway, I hope this background is useful context.
2018+ Macs/iPhones still receive Safari updates, with security updates going further back.
Chrome is Android 10+, which requires a device not older than 2017/2018 as well.
Chrome is Windows 10+, which I'd say requires 2015+ hardware.
The difference is not that large, how many devices older than 9 years do you use?
npmjs.com/package/temporal-fun
Some 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();Ramadan is observed from one visual sighting of a crescent moon to the next.
Cloud conditions may prevent sighting and thereby alter the official start of Ramadan for an individual location, and from time-to-time, the start of a country's change in timezone.
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.
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.
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.
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 :)
Safari confirmed as IE Spiritual successor in 2020+.
This is funny to me; Java's util.Date was almost certainly a port of C's time.h API!
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"
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-...
I wonder if it has a chance to replace chrono and jiff in the rust ecosystem.
That being said, the library is designed to be specification conformant and with EcmaScript implementations in mind. There are some specific API choices made specifically for those clients.
That being said, we are always looking for feedback regarding the native Rust API. So feel free to try temporal_rs out and provide whatever feedback you'd like :)
From the link, we can see Temporal does have separate Date/Time/Datetime types. ("PlainDate" etc)
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 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...Period (https://docs.oracle.com/en/java/javase/21/docs/api/java.base...) is a date-based measure of time. 2 years, 3 months, and 4 days.
Interval (https://www.threeten.org/threeten-extra/apidocs/org.threeten...) isn't built into java.time, however, it is in the popular threeten-extra library. The docs say "An interval represents the time on the time-line between two Instants." The main difference being that Interval is anchored to the timeline while Duration and Period are not.
What I keep thinking about with Temporal is the adoption timeline question isn't really 'is it specced?' anymore, it's 'what minimum runtime version do I need?' Node.js, Deno, Bun all need to ship it stably, and then the practical floor for usage is wherever most prod environments are. The polyfill situation (@js-temporal/polyfill and others) doesn't really collapse until that happens.
So the speccing is done but I think we're still a couple of LTS cycles away from it being genuinely boring to reach for Temporal.
It's weird that they picked example code that is extremely non-accidentally doing this.
- 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.
But, still, let's look at your first couple of complaints.
To make #1 more explicit: If you want the equivalent of "new Date()", then as you observe you need to say something that's longer because it's more specific about what it's giving you. Why can't it just do the obvious simple thing, like Date does?
To make #2 more explicit: If you want the equivalent of "Date.now()", then as you observe you again need to say something that's longer because it's more specific about what it's giving you. Why can't it just do the obvious simple thing, like Date does?
Well, because as those two examples show there isn't actually an obvious simple thing. Two operations both of which one might expect to do the obvious simple thing do different things, and if there's some obvious way for someone who doesn't already happen to have the specs of Date memorized to know which one is "new Date()" and which one is "Date.now()", I don't know what it is.
So, to me, those first two examples look like pretty convincing evidence that Temporal is a better design and one that's less likely to lead non-experts to make serious mistakes.
... And then your other two complaints aren't actually about the API being "too complicated" at all! PascalCase isn't more complicated than snakeCase. Nanoseconds aren't more complicated than milliseconds.
(Also: "zonedDateTimeISO" and "epochMilliseconds" are in fact both snakeCase, and a quick look at the Temporal documentation suggests that this is the norm. Method names are snakeCase, class names are PascalCase. I am not a Javascript expert but isn't that pretty normal?)
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
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.
No-one seems to like this style, but I find it much simpler than converting on db read/write and passing datetime objects around.
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.
This is known as the "primitive obsession" anti-pattern.
Which makes me wonder how it'll look like when interfacing with WASM. Better than Date?
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/
github.com/howie-code/temporal-fun
Only Morocco does this, I believe, and it's not even clear that that's actually official time at this point. In 2018, Morocco abolished DST, but it seems unclear what that means in practice.
I'd love it if someone from Morocco could weigh in on what the actual situation is on the ground. Do people still change their clocks for Ramadan? Would they be annoyed if a website kept Moroccan users on standard time during Ramadan?
Both of those things (and others) became "standards" when IE was moving quickly and breaking things. It took a while for the actual multi-browser standards to catch up. XHR took a few years to show up in non-IE browsers. CSS `box-sizing` wasn't added to the CSS standards until 2012 (11 years after IE6 was released, the "last" version of IE for a long time; five years without a new version). A lot of the web was built easier on those things or better with those things which lead to so many people using IE up to IE6 as their primary browser and so many developers building IE-only websites up to IE6.
Again, as a developer it can be easy to remember the pain of still supporting IE6 in 2005 (five years before tools like `box-sizing` made it a lot easier to support similar CSS for both IE and non-IE browsers, and a year before IE7 finally broke the "IE6 is the last IE" problem). It seems a lot harder to remember why we were still supporting a "dead"/"final" IE6 in 2005 or still supporting a "dead"/"final" IE6 in 2012 when IE10 was fresh and new and very standards compliant (including supporting both `box-sizing` modes) but not yet winning over the crowds of legacy sites: everyone was using IE6 until Microsoft killed it. A lot of things were built for its version of "standards" (many of which were better/easier to develop for versus their contemporary real standards) and couldn't be easily upgraded until the real standards also caught up to how fast IE had been innovating/changing/upgrading the standards.
The risk to the web platform that I think IE represents the most cautionary tale about is relying too much on the browser rushing ahead of the standards, because it could stop at any moment and may take a decade or more for the standards to truly catch back up. Because they did.
If Google decided today to do a "The Browser Company-style pivot" because the Age of AI means that browsers are dead, everything a browser can do should be done through agentic automation, and asked all of the Chrome team to switch to some new agentic harness or accept a soft layoff, how much work would there be to move websites out of being "Chrome-only" or built on top of Chromium? (Which to be further unfair is also sort of what feels like is already left of Microsoft's Edge team working in Chromium today.) It's real easy to imagine that hypothetical, I already named two companies working with Chromium that have just about done exactly that. The hypothetical is not that far from the inside baseball of what happened to IE6 where Microsoft thought browsers were "done" and pivoted the IE team to new roles on "higher priority" Windows work and/or soft layoffs.
We remember the pain of having to support older versions of IE pretty well, but not enough of us seem to remember the pain of how we got to that point and how easy it feels like companies could do that to the web again. Safari lagging current standards is a relatively smaller problem compared to if Chrome gets burnt we suffer another "internet dark age" of supporting ancient browsers for a decade or two due to legacy apps and in turn legacy users that don't or won't upgrade.
(Some would argue that can't happen in the same way that IE did because Chromium is open source and already has many forks. I can't help up but bring up examples like the word "diaspora" and the tale of "the Tower of Babel" that a messy soup of forks that no one can agree on as the new "standard" can be its own slow train wreck disaster.)
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`.
$ du -sh '/Applications/Google Chrome.app'
1.3G /Applications/Google Chrome.appIs there any way to force it to only use (and bundle) the polyfills that are needed assuming 2025+ era browsers?
And the LLM doesn’t care. You could hand it a pile of the best code ever and a pile of brainfuck and probably the difference between comprehending one over the other is in the seconds if not milliseconds of compute time.
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
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.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
const myObject = JSON.parse(text);
myObject.date = new Date(myObject.date);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.
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.
I was just thinking about this this week because I have a webpage I built with offline capabilities and an excuse coming up where many of the webpage's users will be offline for a week but might have use for the webpage, but I can't easily turn it into a PWA because it was built as an MPA and there's no great high level tools for writing an MPA's ServiceWorker because most of the high level libraries are so (overly) focused on SPAs. I wish I could just put a manifest.json or some sort of zip archive users could download and have it share Local Storage.
I do pin a lot of this on Google engineers. The side effect is Safari is having a hard time implementing these "standards", but the real cause is the "standards" are over-complicated trash that are also hard to develop for. Everyone including the links you sent can see how Apple's App Store moat gives them an incentive to drag their feet on implementing these "standards" and yet no one is giving Google enough gruff for the conflict of interest with Google Play's moats and making over-complicated standards that are hard for anyone to use and harder for anyone else to implement is just another way to drag your feet and keep the whole web platform behind, without looking like you are dragging your feet.
It would feel different, too, if the fully declarative manifest.json approach hadn't briefly worked (well) in Edge (Spartan) and Firefox before Google derailed that standards train with "Chrome-first" ServiceWorker complications. Always seemed like one of the reasons that Microsoft just gave up on the web platform because they couldn't keep up with Google's machinations (and conflicts of interest) in Chrome.
Given the number of chrome-only sites that block firefox and not safari i think there are other issues in front end land
our personal convictions don't change that fact.
There’s no single time unit that works for all situations.
The safari feet dragging/obstruction goes far beyond PWAs. The chart on this page is one of many examples showing how consistently far behind Safari is - they've been enormously behind chrome and firefox in coverage of tests for 7+ years. https://wpt.fyi/. And here's an extremely comprehensive article on the topic https://webventures.rejh.nl/blog/2024/history-of-safari-show...
As for standards, here's another detailed series to learn from https://infrequently.org/series/effective-standards-work/. Once again, you have it all backwards. Saying "no one is giving Google enough gruff for the conflict of interest with Google Play's moats and making over-complicated standards" is not only laughable, but just dumb - Google doesn't and, in fact, can't "make standards". Standards are something that comes about through the painful diplomatic process described in those links.
Moreover, it is quite clearly an institutional decision to hold back the web, or else they would allow for other browser engines to run on iOS rather than focing them all to be skins on webkit. Again, this is all documented in extreme detail in the articles on that site. If you find it to be still somehow lacking, the author is very open to discussion on bluesky or mastodon (I'd prepare far better though, because what you've said thus far would get eviscerated).
Also bizarre that you are saying that Google Play is somehow at the root of this supposed scheme to make web standards impossible for others to implement. Android is similarly against the web flourishing, but evidently not nearly as powerful in the greater Google enterprise as iphone/app store is in Apple.
As for MPA PWAs, there's nothing at all stopping you from serving pages from a service worker. There's plenty of valid and accessible ways to precache all the pages that a user might need while offline. Workbox (from Google!) makes it easy, but its also easy to hand-roll.
And, Microsoft most definitely has not given up on the web platform - they literally adopted and make contributions to chromium. The author of that site literally works at Microsoft now, coaching both internal and external teams on improving their use of the web, as well as contributing to standards.
I dont see any point in continuing this discussion, as you haven't shown even the slightest interest in considering how you're living in some bizarro world.
If you are actually attempting to communicate in good faith, i can't recommend strongly enough that you read that entire site. And, likewise, read and support the work of Open Web Advocacy. https://open-web-advocacy.org/
I'm saying this is exactly the problem. If the perception is that only one browser is "moving forward" and the rest are just chasing that moving target, that's not healthy and it is not a standards process. WHATWG has always been at risk of "regulatory capture" by Google or at least Chromium interests. More so than ever there are standards that seems like WHATWG rubber stamped whatever Chrome decided to do without larger consensus work with Safari and Firefox. That's really dangerous for the web platform. (And W3C lost to WHATWG and seems increasingly irrelevant as a standards body for HTML.)
I think we are all very lucky that ECMA hasn't so far shown the same risk and TC-39 (JS) continues to look overall diverse and healthy.
> Google doesn't and, in fact, can't "make standards". Standards are something that comes about through the painful diplomatic process described in those links.
This is why I put standards in quotes in most of that comment. I do think WHATWG has already signed off on Chrome-first things as "standards" that aren't in the sense of multiple robust implementations and a diverse enough number of stakeholders that aren't just using Chromium-derived codebases. I worry WHATWG is at risk of getting worse in this.
> As for MPA PWAs, there's nothing at all stopping you from serving pages from a service worker. There's plenty of valid and accessible ways to precache all the pages that a user might need while offline. Workbox (from Google!) makes it easy, but its also easy to hand-roll.
As very personal experience from building PWAs (and failing to build many more of them): Workbox is bloated and awful to work with and is bad enough at SPAs that trying to feed it an MPA makes me want to scream just thinking about it. Hand-rolling a Service Worker remains a nightmare because the API is awful to work with by hand, which is the whole reason Workbox exists. There's something very wrong with the APIs that right now the only answer seems to be "just use Workbox". That's not healthy for the web platform to be so dependent on a single vendor's tool to get over the hump of using a web API. (Even if that tool is open source. CVEs affect open source like everything else.)
The last time I was serious about PWA development I broke down in tears and switched to Ionic's Capacitor and Electron because browser wrappers are still too much easier than writing a PWA.
I know that isn't just me also anecdotally by the number of Electron apps running on my machine even right now (a bunch) and the number of PWA apps running on my machine (none).
Statistically Service Workers and Workbox are massive failures, and it isn't Apple's fault and it is weird to me claiming that it is entirely Apple's fault. If you don't want to blame Google or at least Chromium engineers, that's fine, we don't have to agree on that. But show me the app with a working PWA ServiceWorker that has a good reliable caching strategy, good offline-first support, and people use that offline-first capability regularly and I'll show you a unicorn. The APIs are terrible, the standards should be better. If we don't want to point fingers at why the current APIs and standards are so awful, can we at least find someone to point a finger at who is actively working to make them better? It doesn't seem to be "Just Use Workbox" Chromium. Who is actually trying to move the offline-first web forward towards pragmatic reality and not just "we support it in theory, with this one JS library, but very few are using it in practice and almost none successfully"?
> And, Microsoft most definitely has not given up on the web platform - they literally adopted and make contributions to chromium. The author of that site literally works at Microsoft now, coaching both internal and external teams on improving their use of the web, as well as contributing to standards.
When Microsoft switched to Chromium they soft laid off a lot of their web platform staff. Chromium Edge's outward development focus seems to be AI and First-Party Coupon Cutting Extensions.
Spartan Edge had ideals and seemed to really believe in the PWA as a first class application platform. For a time, I had a bunch of PWAs as daily use applications in Windows 8 and early 10 (not all of which I built myself, either). That era is certainly gone now. WebView2 is making some inroads in reduce the reliance on Electron by certain types of apps, but WebView2 isn't a PWA platform, it is another end run around it/away from it.
> I dont see any point in continuing this discussion, as you haven't shown even the slightest interest in considering how you're living in some bizarro world.
> If you are actually attempting to communicate in good faith
You've strayed close enough to the realm of ad hominem attacks that I'm going to stop here. It doesn't sound like we are going to ever agree, but certainly not because I'm not debating in "good faith" or living in some "bizarro world". It seems rude to me to imply such accusations. Just because I have a different perspective doesn't make me a bad actor nor prove I have some sort of mental health issues. I may have experienced a different world than you have in my career, but there was nothing "bizarro" or worse about it. Different perspectives should be a joy to engage with, not an affront to ridicule. I'm sorry I couldn't find help you find common ground.
as for service worker, I literally said you dont need workbox. I have done lots of hand-rolled MPA caching. Its dead-simple, so i dont know what complexity you're referring to.
As for the fact that there arent many good pwas out there - people dont bother because iphone is a mess. Your arguments would hold water if apple allowed other browser engines and then pwas still languished.
Even still, there's all sorts of efforts towards offline/local-first. Its a hard problem to solve. But, again, simple MPA caching is not hard. If its a dynamic backend, then that would be much more difficult