Hacker News new | past | comments | ask | show | jobs | submit login
JavaScript dates are about to be fixed (docs.timetime.in)
413 points by thunderbong 25 days ago | hide | past | favorite | 277 comments



Thank god. Javascript is the only language where I... wait for it... roll my own datetimes. Moment's central failure, beyond the surprise mutability, is the conflation of Dates and Times with Datetimes. They are not the same, and this causes so many problems. Python's Arrow library made the same mistake, likely inspired by Moment. I've heard JS devs insist that Dates and Times have no place as standalone constructs outside of a Datetime, but this is incompatible with practical use cases.

Rust's Chrono? Outstanding. You can find flaws, but it's generally predictable, and most importantly, has fewer flaws than those in other languages. Python's? Kind of messy, but usable. JS's Date and Moment? Unusable.


> I've heard JS devs insist that Dates and Times have no place as standalone constructs outside of a Datetime

As somebody having both caused and fixed many time-related bugs in a financial/payments context, this sentiment is creating an almost physiological reaction at this point.

If your real-world process is concerned with things happening on particular days, don't represent these as midnight, i.e. 00:00, of that day. You'll thank me the day your company starts hosting servers or interacting with a customer/vendor in a different timezone.

Essentially, this is just a corollary of "don't imply more precision in your internal data representation than was present in your data source", but it bears repeating when it comes to date/time processing too.

(And please, please don't ask me about the difference between "Y" and "y" in Java's SimpleDateFormat, but look it up before you ever use it, thinking they're equivalent... Because on some days of some years, they are not.)


My favorite Date v Datetime bug is that Chile changes to DLS at midnight and not 1am. So it’s goes 11:59>1am. Many systems that conflate dates and date times take the existence of midnight as an invariant, which it is not.


Oh, that's another beautiful counterexample, thank you!

I was wrecking my mind trying to come up with a scenario where what we did could go wrong with DST shifts alone (i.e. without a timezone conversion/relocation), but fortunately, most (all?) of Europe shifts DST on a weekend, never at the last day of the month, and with ample safety buffer from midnight, for which I am very thankful.


Another fun thing is that some countries that celebrate Ramadan may change back to normal time during Ramadan. So you can 2-4 DLS changes per year depending on when Ramadan falls. Luckily most time systems don’t really care, but it’s still a fun edge case. You really can’t hard code anything.


DST being on a weekend doesn't mean things can't go wrong. If it's Friday at 1pm and you want to schedule something for 1 week later (next Friday at 1pm) and there's a DST transition over the weekend, are you correctly getting next Friday at 1pm? Or is it 2pm because you didn't account for the jump forward in time, thus making this particular week 1 hour shorter than more?

DST problems can manifest in a lot of interesting ways. That's why it's important to use a datetime library that does DST safe arithmetic (like Temporal).


Case in point. On the iphone in 2010, if you had a repeating alarm set before a DST transition it would go off an hour wrong.

Sizable portion of society were late that Monday (most probably don't have repeating alarms on the weekend so I guess it went unnoticed for most).

Best part. After making headlines all around, apple managed to have the same/similar bug next year.


I didn’t mean to imply nothing could go wrong (and a lot does go wrong; I once watched a Windows PC freeze completely exactly at the time of DST shift!), but it does reduce the number of ways things can go wrong with non-defensive programming slightly.

Not doing the shift on a weekday or the last of the month alone has probably prevented countless bank batch jobs from exploding.


This is an interesting question because it’s the difference between clocks and calendars. If you add 7 calendar days it’s 1pm. If you add 168 hours then it’s 2pm. Generally calendar systems will try to encode reasonable semantics for this on what a human would expect it to do. In those case it’s add 7 days. Calendars and clocks are not equivalent.


Yes, that's why I said "1 week." With time zones, units of days or higher are non-uniform.


What kind of new projects will this new feature in ECMAScript enable? Just spitballing here.


Oh god your comment brought back so many horrible memories. At my last company we had a reporting system in NodeJS that ran daily reports for clients around the globe, and whoever set it up decided that the reports should be set to 00:00.

The amount of hell that ensued was never-ending. I'm not sure we ever truly fixed the issue, it's so hard.


Same!

There's still apps at my company that use the server's local time for everything, built in a pre-cloud world. Now we "cloud host" many of these, by running sets of VMs with their clocks set to different timezones and setting up each tenant on one matching their zone. Unfortunately, it's a product that makes a fair bit of money but isn't our focus (not much growth or new sales), so it's never been worth it to actually do something better.

It's not just the timezone/DST issues, either. Another product I've worked on did the "reports at midnight" thing, but as it grew it became impossible, as it took hours to complete all reports for all customers. So some "scheduled at midnight reports" would happen at 1:28am. This alone isn't a huge deal, but many reports were time-dependent (eg: you get different results running at 1:28 vs 0:00), and for a subset of those it was actually a big problem. So some got fixed by removing the time dependency (basically making the queries in the report have end times vs using now()). Another workaround was to staggering times manually on other reports - run at 2:30am - but still many customers would still pick 1:00 or 2:00 (which is of course someone elses' midnight), and there were still big I/O bursts on :00, :30, :15 and :45, especially overnight.

My personal takeaway was to avoid building report systems like that, and try to do things like let the user pick "Daily" without picking the time-of-day. For time-sensitive "reports" it's been my experience that if you actually dig into the user needs, they're really looking for an "operational view", not a report. It requires specific dev work to build of course, but the end result is better both technically and for the customer, and is less overall work than all the fixes/workarounds needed when you try to shoehorn it in with all the other reports, especially as the usage grows.


> don't imply more precision in your internal data representation than was present in your data source

Matrix's e2ee message formats have a form of this issue. The AES IVs generated are only a subset of the possible IV space than the cryptography actually uses. This gets expressed in the JSON base64 string storing the IV always having an AAAAA prefix for the zero-padding.

I'm not sure if this is still true and I don't believe it was responsible for any security vulnerability due to how the IVs are used, but it's still a sloppy design.


In defense of Java's SimpleDateFormat, it's purposefully designed to be compatible with the ICU formatting.

The choice of 'Y' for the rarely used "week year" is unfortunate. For those unaware, in ISO-8601 every year has either 52 or 53 full weeks. If you want to format a date like "2023-W52-6" (the 6th day of the 52nd week of 2024) you would use "YYYY-'W'ww-u".

https://unicode-org.github.io/icu/userguide/format_parse/dat...


Unfortunately ICU's mistake here has spread to a lot of places because deferring to what ICU does is nearly always the correct decision even if you aren't actually using ICU.


Don't wish to be a java apologist but SimpleDateFormat lives in the java.text package, far from where date/time stuff is.


How is a function that converts an internal date/time representation to an external/string-based one not "date/time stuff", regardless of the package it happens to be in?


You may want to check out the contents of java.time [1] and it's subpackages to see what I mean by stuff.

[1] https://docs.oracle.com/en/java/javase/17/docs/api/java.base...


java.time.format.DateTimeFormatter seems to still point the exact same loaded footgun (i.e. the format patterns "Y" and "y") at every junior developer that dares to use it, so I'm not sure that would have helped junior-me much.

The important information I was missing was "the week-based year and the year-of-era representations of one and the same date differ on some days, but not all days, and especially not those days you're currently using in your unit tests".

And even if it wouldn't – SimpleDateTime was the non-deprecated standard library method I found at the time that seemed to do my job, so that was the one I used.


Here's a nice modern Python library in this space that claims to take influence from Temporal and Chrono - https://github.com/ariebovenberg/whenever


Have you ever used date-fns or Luxon? although Moment is still popular, I thought that was it succeeded by other libraries. If my memory is completely wrong I thought that Luxon might have been made by the creators of Moment?


Date-fns uses the native Date object which makes the same mistake, but then has a bunch of additional mistakes like not accounting for timezones and being painful to use. Date-fns can resolve some of these issues, but it's generally too simple to do a good job. It's great for manipulating timestamps in the user's local time zone, but beyond that it starts becoming difficult to get right.


Luxon is great! You’re correct about its origin. I use Luxon both for exiftool-vendored and PhotoStructure.

The new temporal api discussed in TFA includes more robust calendar support and more flexible zoned DateTime than Luxon afaict.


Dayjs works good either.


Where did the overlap of JS and Rust knowledge come from? Is one a day job and the other tinkering? Are both somehow "work"?


Different tools for different jobs. Sometimes you need a hammer; sometimes a drill. So, you have both in the toolbox.


Ruby on Rails (ActiveSupport::TimeWithZone) is a solid API--have never had a problem with it. Also looks like Elixir is good too.

One of the biggest problems with Javascript's Date is that there is no way to change the timezone context--you are stuck with whatever the system timezone is.


Beyond timezone and DLS which I don't use much, and mutability which is already solved with dayjs, is there any problem with moment? Really curious because I use that in production for years without problem.


"About to be fixed" is potentially inaccurate.

There has been no meaningful change in this proposal for some time now.

Perhaps in 6 months, hopefully I'll be proven wrong.

But there's no reason to suspect that now is the time.


Thank god!

Presumably this new API will fix the fact that JS does in fact know about some time zones, but not most.

Shield your eyes from this monstrosity that will successfully parse some dates using `new Date(<string>)` in some select special time zones, but assume UTC in the other cases:

https://github.com/v8/v8/blob/781c20568240a1e59edcf0cb5d713a...

This bit me hard in a previous role. :'(


That's the Chrome V8 standard library? The one "official" thing that everybody uses?

It knows about 8 time zones in 4 different offsets, and that's it?

I ask because yes, everything on the site points that way. But it's still so hard to believe that some confirmation would be good.


It's more about the javascript spec. If the spec says "thou shalt support this and that" and one of the browsers implements more than that, we are now back in the 2000s where one browser may be supported by some websites but others aren't.


Canvas by Instructure, one of the largest LMS (learning management system) in the world officially supports all major browsers.

Unofficially schools tell students to use Chrome because some tasks fail often outside of Chrome. In particular Safari's default settings for cookies/privacy often causes the system to fail - particularly with integrations that use iframes/popups and other antiquated tricks.

The year is 2024.


I'm actually less surprised about this in 2024 than I would have been for a webapp in 2014.

I feel like we peaked somewhere around the mid 2010s with regards to application compatibility: No more Flash, but also more than just 1.5 rendering engines.

Before that, it was all "oh, sorry, Windows .exe only"; since then, "sorry, Chrome visitors only".


It was nice. As long as you weren't doing something totally inadvisable like using IE 7 or something, things generally worked fine. Blink, Gecko, WebKit, whatever. Even random tiny FOSS browser projects wrapping somewhat outdated builds of WebKit or Gecko worked ok the majority of the time as long as you spoofed the user agent string of one of the big guys.


People put serious effort into even IE compat for a while. AFAICT the beginning of the end came with trying to do battle with mobile browsers; you simply couldn’t debug all the potential problems well enough. I remember burning a month on a regression that affected only iOS before Apple fixed it. Of course people were going to eventually pick an easy standards based target and tell everyone else to pray.


Idk 2015 was when I had to use Firefox for Dyanmics CRM, IExplorer for several specific things, and Chrome because it was faster for the rest.


After using Canvas for 2 years, I haven't had any problems with Firefox (except for the Lockdown Browser feature our school used to require, which I think is being gradually phased out).


The JS spec requires that std-libraries decode 8 US timezones and no further?


> It's more about the javascript spec.

I don't think this is correct. I think the JavaScript spec here defines a string format which is extremely narrow, but then leaves it open to browsers implementing whatever they want.

https://262.ecma-international.org/5.1/#sec-15.9.4.2

> The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.

https://262.ecma-international.org/5.1/#sec-15.9.1.15

> ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

> Z is the time zone offset specified as “Z” (for UTC) or either “+” or “-” followed by a time expression HH:mm

> Note2: There exists no international standard that specifies abbreviations for civil time zones like CET, EST, etc. and sometimes the same abbreviation is even used for two very different time zones. For this reason, ISO 8601 and this format specifies numeric representations of date and time.

The hint as to what is going on then comes from MDN, which more documents the ground truth than the goal.

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

> There are many ways to format a date as a string. The JavaScript specification only specifies one format to be universally supported: the date time string format, a simplification of the ISO 8601 calendar date extended format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

> Note: You are encouraged to make sure your input conforms to the date time string format above for maximum compatibility, because support for other formats is not guaranteed. However, there are some formats that are supported in all major implementations — like RFC 2822 format — in which case their usage can be acceptable. Always conduct cross-browser tests to ensure your code works in all target browsers. A library can help if many different formats are to be accommodated.

That table then comes from RFC2822, which also doesn't want you to use string named time zones, but as RFC822 has such it needs to still support it a bit--I imagine this probably came from back when this was used by ARPA-- and so has a section on "obsolete" date and time format extensions, which only explicitly requires support for US time zones and (notably) military formats.

https://datatracker.ietf.org/doc/html/rfc2822#section-4.3

    "UT" / "GMT" /          ; Universal Time
                            ; North American UT
                            ; offsets
    "EST" / "EDT" /         ; Eastern:  - 5/ - 4
    "CST" / "CDT" /         ; Central:  - 6/ - 5
    "MST" / "MDT" /         ; Mountain: - 7/ - 6
    "PST" / "PDT" /         ; Pacific:  - 8/ - 7
    %d65-73 /               ; Military zones - "A"
    %d75-90 /               ; through "I" and "K"
    %d97-105 /              ; through "Z", both
    %d107-122               ; upper and lower case
> Other multi-character (usually between 3 and 5) alphabetic time zones have been used in Internet messages. Any such time zone whose meaning is not known SHOULD be considered equivalent to "-0000" unless there is out-of-band information confirming their meaning.

So, a spec, sure, but not the JavaScript spec, and not because if Chrome supported something weird then JS would get mad (as JS nigh unto suggests going above and beyond), and even RFC2822 (itself already outside JS) leaves open the door for more zones... in practice, I actually think JavaScriptCore might support even more formats (but could be totally wrong on that)?


Honestly, it sounds like the most reasonable behavior here would be to _only_ support "Z" (optionally with "+<offset>") and none of the 3-letter codes. Unfortunately it would probably a breaking change to remove support for the existing codes, so it wouldn't happen.


Just to note, that table is from the "Obsolete Date and Time" section. AFAIK, RFC 2822 only really allows the number offsets and Z.

Anyway, yeah, that does explain the situation we are in. And yeah, it looks like if you are extremely literal-minded and insist on supporting obsolete notations on your new thing, that would make your code be exactly like the one the OP pointed.

It's a bad decision all around, because the RFC 2822 isn't concerned about general purpose time handling. A programing language shouldn't use something like this.


Yes, this is the standard library that Node JS and the browser uses. Wacky isn't it?


Yes, incredibly wacky.

Somebody posted the spec as a reply, and looks like the standard can be read in a way that mandates exactly that.


> In other words, the function responsible for transforming a timestamp into a human-readable date is not injective, as each element of the set of timestamps corresponds to more than one element of the "human dates" set.

The author confuses the concept of injectivity and well-definedness. For an injective function f, it has to be true that given distinct a and b, f(a) != f(b). That's not the problem here; rather the problem is that for a timestamp t there's no unique human-readable date x such that f(t) = x. That's well-definedness, not injectivity.

https://en.wikipedia.org/wiki/Well-defined_expression

You could talk about the inverse function, from human-readable dates to timestamps, and correctly point out that that function is not injective, because more than one human-readable date maps to the same timestamp...


But how does either problem apply to timestamps?

if t is 17943000... whatever

which translates to say

03:50 AM on Mar 16, 2020 in Greenwich (i.e. UTC+00.00)

then it can be converted to any other human readable zoned time.

Where is the issue?


The ability to convert it to any human-readable time is actually the problem: without additional information you don't know which one to pick, and are forced to make certain assumptions, such as using the user's local time zone or defaulting to UTC. That's the point of the section of the article I quoted above.

For example, given just a timestamp, you don't even know what day it was. Was the user in New York? That 3:50am was actually 11:50pm on March 15th.


I genuinely still don't get it, but why do you need to fix the reference point once and for all?

I would assume in 99% of cases the user is simply just interested in his local time anyway.

If you really need the location info, then you would obv have to make some other tradeoffs like either

1) always store the location alongside the timestamp or

2) complicate (probably massively so) the time format, storing and parsing/processing logic

But I don't see how that's an issue of UTC timestamps per se rather than being an issue of requiring more information than just time (i.e. time AND location at the time).

Again, really would appreciate an explanation of where I am mistaken here because thus far the whole point of OP seems like a trivial non-issue to me.


I have the exact same confusion as you.


I think this might be one of those “difficulty curve” memes. On the left, the Padawan: “just use UTC timestamps”. In the center, the journeyman: “no!!! we have to store time zones and translate between them! Time zone awareness!” On the right, the Jedi master: “just use UTC timestamps”.


No - the master's answer is that you use one or the other depending on whether you are representing absolute time or clock/calendar time. The expertise lies in understanding the difference between those two things, which one your use case falls into, and how to ensure other developers and the very APIs provided by your platform do not make silent assumptions about that decision.

Often it feels like you can "just use UTC" because it's hard to imagine the fail states, and APIs often make tz assumptions for you so you may never notice those fail states (occasionally your software will just be buggy and annoy a user, decreasing the feeling of quality and trust - it may never result in a disaster).


And even then it's difficult. Time zone definitions change. Summer time does not start and end on the same date everywhere. So you also might need to know the UTC offset corresponding to the time zone at that time not just the UTC offset of that time zone today (assuming it still exists)

Edit to add: still, this sounds like a great improvement. A common mistake that naive programmers make is to believe that they can hand-roll their own date/time functions. And JavaScript sort of forces them to do that.

See also Falsehoods programmers believe about time at https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b...


It’s not difficult if you understand what you are trying to do conceptually.

For example, if you are trying to say “run this task at this specific time in Los Angeles time” and you put a specific UTC offset, it just doesn’t make any sense. Los Angeles time consists of two offsets, and yet here you openly put down something wrong. You need to put into your system literally Los Angeles time (like America/Los_Angeles) because that’s what you want. You didn’t want an offset. Dear god why are you putting an offset then?

Or you want to define a specific date but then you put into your system a date with a time like 12/12/2024 00:00. Like you started off with “I want to define a specific date” and here you openly just put a time. You had to make up 00:00. Why are you just doing random things without thinking?


I wasn't literally suggesting that you store offsets. Or timestamps. There are way too many weird cases around doing that. But "America/Los_Angeles" is a reference to a file in tzfile(5) format, which contains, among other things, the UTC offset. And a function that produces the correct date and time in Los Angeles at any point in the past has to know (among other things) if that offset was ever anything different, and if it was, when it changed. And many other things.

There are date/time libraries that can't get all of this right, but you'll come a lot closer using them than you will rolling your own.

Don't store raw timestamps. Don't store offsets. Use date/time data types.



There is no such thing as absolute time so the distinction you're trying to make is not clear.


Most use cases don't care about relativity


While UTC timestamps look like they solve all of the problems in one passing, they have plenty of edge cases that can blow up spectacularly. A good example of this is daylight saving time and countries changing their rules around it. You would think this is a rare occurrence, but it actually happens pretty often [0]! In the last ten years, fourteen countries have abolished daylight saving time.

[0] https://en.wikipedia.org/wiki/Daylight_saving_time_by_countr...


Like, what if you only need the date but not the time? You can use timestamp to store a birthday, but which in which timezone should you transform it back to date?

Or what if it is specifically about time in timezone independent way? You want to wake up ate 06:00 no matter in any timezone. Just something like alert.

You could use part of ISO8601 like 2020-02-02 or 06:00:00 to store exactly what you specify. But with timestamp, not so much.


We hit this with the US timezone change in 2005. The front-end devs were using moment.js which didn't know about the previous start/end dates.

These days I try to encode everything that is logically a date as yyyy-mm-dd. And if I need to do math with it, I often work with an offset julian date as an integer. But that only works if you stay on this side of September 1752:

    % cal 09 1752
       September 1752     
    Su Mo Tu We Th Fr Sa  
           1  2 14 15 16  
    17 18 19 20 21 22 23  
    24 25 26 27 28 29 30


Before roughly 100 years ago, calendar dates were region-dependent anyway (Russia was still on the Julian calendar until 1918), and time zones didn’t really exist until roughly 150 years ago (their establishment mostly being motivated by the spreading of railways). It’s okay to apply the perpetual Gregorian calendar both into the past and the future by default. It probably won’t last more than a couple thousand years into the future as well, considering Earth’s decelerating rotation.


I think time zones not existing really meant there were infinite time zones, because you use the sun to tell the time.


The solar day varies in length by more than a minute over the year. That doesn't correspond to a well-defined time zone. Also, "zone" implies a geographical area, which meridians arguably aren't.


Some Orthodox Churches still use the Julian calendar even today. Of course they aren't major software customers.


If you're truly using Julian dates, the switch to the Gregorian calendar in the UK and US in September 1752 shouldn't affect you.


> if I need to do math with it, I often work with an offset julian date as an integer. But that only works if you stay on this side of September 1752

Wouldn't using Julian days prevent the ordinary problems involved with doing calendar math across a renumbering of the dates?


The mapping from year, month, day that I use (based on the Wikipedia page, but starting on march 1, 1600) doesn't take that gap into account. So the days in that gap exist for me, but we don't deal with dates that far back.


oof, there went 12 days. at least this isn't a regular occurrence outside of 1752 (unless you have DID, I guess)


Technically you could use TAI for most things internally and use an offset when you display. It works perfectly for records. The only issue is with scheduled things when your users probably want to point at something in their timezone.


The main problem is that future values of TAI converted into most time zones is nondeterministic. On the other hand, at least you can guarantee all TAI seconds will actually occur.


Or toLocalDisplayString(TAI timestamp). It seems like timezones and their rules change over time and geographic location, using todays rules to show dates back in time with previous rules would be broken right?


Well, any correct time handling library will use something like the IANA timezone database (aka tz/tzdb/zoneinfo) which stores all the changes in timezone rules for localities over the years - a library that only knows "today's rules" would be fundamentally broken as you say. So the internals of toLocalDisplayString(timestamp, timezone, calendar, locale) should look up the appropriate rules in effect at the timestamp and use those to decide the local time to display.


I’ve been thinking about this; does a timestamp even work, then, without not only a time zone, but a location? I mean time zones are poor man’s locations, but if we want to do this properly…


Sure, but now you've reduced the scope of the problem to a few edge cases. Every other solution I've seen still has these edge cases and doesn't solve the core problem as simply as UTC. If you think otherwise, can you describe or provide a link to these other solutions?


For region-specific events (which includes most physical meetings, by convention many financial processes etc.), it's much safer to store both the local time and time zone city, as well as the UTC offset at the time of writing, and to convert to UTC only at runtime using an up-to-date time zone database.

That way, you can identify all of:

- Future timestamps you might want to re-process, e.g. for notification scheduling, because the future UTC conversion has changed between scheduling/updating the event and now.

- Equivalently, timestamps written by software/an OS running an outdated TZ database. You should talk to them; things are about to go wrong if the date is in the non-too-far future!

- Trivially and without conversion, the local time that people will use in the meeting notes, documenting that you missed the meeting because you didn't update your TZ database and dialed in to the meeting an hour late


Dates of past events are always UTC, dates of future physical events must always be a timestamp and a location, dates of future virtual events are either UTC or location based depending on how virtual they actually are.

I can tell you from bitter experience that if you have an event where a person has to arrive at a place at a time and you store that as UTC instead of wall clock, you will have a problem at some point


Yes! All past events have well defined times, even in the face of human silliness like changing timezones and before such ideas existed.

The future is defined by intent which is imprecise (will Oregon drop daylight savings time or not — who knows?). I don’t know how many seconds it is until my next birthday, but I know exactly how many have passed since I was born.


I never really understood precisely what the challenges around dates were until this comment and the previous one.


Past/future doesn't matter, what matters is context. You don't need to use timezones for ie. setTimeout even though it refers to the future and you should have timezone available for past events ie. atm withdrawal.


> Past/future doesn't matter

Past/future does matter, as generally speaking it's possible to accurately convert between local time and UTC for times in the past, but not in the future.

There's a few exceptions where governments have retroactively changed timezones or DST rules, but at that point you've lost anyway.


setTimeout takes a duration in ms, not a timestamp. If it did take a timestamp, I think you would need to pass it a timezone to disambiguate.

Maybe a better argument is, “when you’re setting a phone alarm, you don’t tell it a timezone.” Maybe the distinction is whether the timezone is established at write time or read time.


> when you’re setting a phone alarm, you don’t tell it a timezone.

Which coincidentally and ironically makes phone alarms surprisingly difficult to implement in a way that does not break in the face of DST shifts, timezone changes etc., as Apple has learned the hard way a couple of times.


Scheduling on a calendar (occur on date X, time Y, in zone Z) and scheduling based on timedelta from instant time (occur exactly X milliseconds before or after instant Y) are both valid, and you can do timedelta scheduling in UTC without needing timezones. The issues come from conflating the two; using timedelta reasoning for calendar scheduling, or using calendar scheduling when you want an exact timedelta.


> the Jedi master: “just use UTC timestamps”.

That poor Jedi master is going to miss the recurring workgroup meeting at least twice per year though, after either DST switch :)


In my experience the Padawan's answer is actually "just use my personal local timezone and don't think about the needs of users elsewhere".

Maybe I'm thinking of a group with less experience than you had in mind, but novices do not even know about UTC.


Sadly UTC doesn’t work with future timestamps because we don’t know if timezones will be adjusted.


Yes that works for most use cases but there are use cases where you may need to store or shuttle the time zone. For instance you want to know this UTC timestamp was originally created in PDT. You would have to store two variables. Most other languages have this functionality it can be useful and is good to have, probably only needed by Jedi’s too.


The article gives an example, of you buying a coffee with your credit card while travelling to Sydney, and returning to Madrid, and a few months later seeing a charge for 3:50 AM on that date...

Google Photos also get confused with "When was this picture taken?", my older model camera just stores the EXIF date in "local time" and I have to remember to change its timezone when travelling, and if GPhotos can't figure it out, it might show pictures out of the airplane window, and then the next series of pictures are from the departure airport because they're from a "later" hour (since it's missing the timezone info).

I suppose I could keep it at my home time or UTC...


And if your spouse is checking the charges from Madrid, they probably want to see it in Madrid time. There is no single correct answer.


Exif (fun exercise: find out who specifies it and when it was last updated!) actually didn't even have a way of storing timestamps in UTC or with zone information until fairly recently: It was all local times without zone information.

I've seen some software work around that by combining the local date/time with an embedded GPS tag, if present, which does contain the time in UTC.


Ironically, time zones are a hack with bad precision about the position of the sun in the sky. The GPS coordinate side steps the nonsense (can be converted to the time zone as defined in that place at that moment).


That is how I would expect a bank statement to read though. I would find it infinitely more confusing if I bought something online in my bank showed the time of wherever the seller was located.

The photos problem is harder, but the app needs to just convert it from local time to UTC when you import it. There's not much that can be done if you take photos on a camera with a different time zone than you're in without more metadata.


You'll find that most bank systems avoid any notion of time precision higher than calendar days for a variety of reasons :) As a side effect, this practice conveniently avoids that problem entirely.

> That is how I would expect a bank statement to read though. I would find it infinitely more confusing if I bought something online in my bank showed the time of wherever the seller was located.

When using my banking app abroad (one that does show timestamps), I'm usually much more confused by their presence than by their absence.

> The photos problem is harder, but the app needs to just convert it from local time to UTC when you import it.

But I usually want to see the time in local hours and minutes for photos! Sunsets, new year's fireworks etc. happen according to local time, not UTC or my current timezone's offset.


Yeah, storage needs to be implemented in UTC and display needs to be in local time.


But which local time?

Sometimes, the local time at the place the photo was taken can make more sense, but it's not a general rule.


Yeah, these are workarounds we have to use because many pieces of software weren't implemented fully...


I have always wondered why breaking the timestamp (in UTC) and the timezone into two separate data points and storing both is not the accepted solution. It appears like the cleanest solution to me.


You can't accurately convert future local times to UTC timestamps yet, as that conversion changes when timezones change.

Let's say we schedule a meeting for next year at 15:00 local time in SF. You store that as an UTC timestamp of 2025-08-24 22:00 and America/Los_Angeles timezone. Now, imagine California decides to abolish daylight savings time and stay on UTC-8 next year. Our meeting time, agreed upon in local time, is now actually for 23:00 UTC.


Wow thanks for sharing this, this certainly is a use case not covered by the system I proposed. I imagine this will require versioning of the timezone so we can translate our America/Los_Angeles v1.0 timestamp to America/Los_Angeles v2.0.


Two different implementations might make two different local times out of that, e.g. due to not being aware of changing DST/timezone policies. Hence the recommendation of separating between user/clock/calendar time (which must be exact to a human) and absolute/relative timestamps (which must be exact to a computer).


From my experience, it certainly is. Easy to tell time in sequence as well as local time then. When daylight savings hits you can still calculate well, and can quickly get real time for hours worked out drive time for freight across time zones to follow the hours of service rules.


You can make the same meme about this use case, too. One you get to the right, you realise you want two variables for this.


Sounds like you are in the middle. Because the Jedi master knows you need both.


This is the language of your engineering team, but not the language of your customer.


Having a good datetime standard is half the battle. The other (more difficult) half is getting broad adoption for it. If it was just a matter of computing within the bounds of my application then there are a bunch of great libraries that make this stuff easy already. However now the question is - if I pass an encoded ZonedDateTime object from my browser/server to a third party, will it be able to interpret it? Realistically I'm just going to convert it down to an ISO string or unix TS as always just to be safe, thus going back to the exact same problem.


If you drop the time zone, then you lose essential context that the consumer won't have unless you encode the time zone out of band. And no, an offset is not a time zone.

The answer to this is RFC 9557, which was recently published. Rust's Jiff library (of which I am the author) supports it. And I believe java.time supports it. Otherwise, yes, adoption will take time. But it's the right way forward I think.


Temporal is getting standardized by the IETF so you can expect it will eventually have broad ecosystem support beyond Javascript, here is the RFC https://www.rfc-editor.org/rfc/rfc9557.html


> RFC 9557 Date and Time on the Internet:

> Timestamps with Additional Information

Oh, cool, we're finally adopting TAI!


Safari even already has a `useTemporal` flag in their technology preview for the upcoming version.

But yes I agree. There's no way any other standard could resist a standard adopted by the IETF and implemented by the language of the web


> Even when working with dates on an ISO format, including the offset, the next time we want to display that date, we only know the number of milliseconds that have passed since the UNIX epoch and the offset. But this is still not enough to know the human moment and time zone in which the payment was made.

Can someone explain to me what this means? ISO date strings should capture that exact information. Sure, there are caveats to storing dates/times, but I find the idea that JavaScript (or any language) needs built-in constructs for this purpose to be questionable.

Beyond that, both Temporal and Date solve what should be very simple problems in very complicated ways. I've yet to run into a circumstance where ISO strings don't suffice.


Because the timestamp and offset (i.e., a numeric indication of the time zone, as a difference from UTC) aren't actually enough information to calculate the local time, pedantically.

Why not?

1. Leap seconds. They exist at irregularly spaced intervals based on the Earth's actual rotation, which is a) not perfectly consistent and b) ever so slightly, continuously, on average, slowing down. The offset would only allow you to figure this out, in principle, if you already had the UTC time - which has to be calculated from the timestamp in a complex way that accounts for leap second information retrieved from a database.

2. Time zones change over time. Some parts of the world use daylight savings time and others don't. It's possible in principle for a daylight savings time transition (which occurs on different dates in different parts of the world) to occur between two displays of the date, invalidating the offset.

3. Time zones change over (longer periods of) time. They've historically been created, removed or had their borders changed. Sir Sandford Fleming's system is now 148 years old (and there were others before it), and geopolitics happens.

4. Calendars. Maybe you don't care about, say, the eras of the Japanese calendar (https://en.wikipedia.org/wiki/Japanese_era_name), but some applications will require knowing about it. If you need to deal with history, then the conversion between Gregorian and Julian calendars will also depend on locale, because the Gregorian calendar was adopted at different points in different parts of the world.


Aren’t all of those example problems solved with a versioned database of timezones and calendar algorithms? Any past “universal” date can be converted to a fully defined local, contextual time based on such a resource. But, it doesn’t exist.


In theory it's a solvable problem. In practice most libraries handling date and time aren't living up to that expectation, in particular and core system libraries, because it's just hard and tradeoffs are made.


Such databases do exist, and they're how more heavyweight libraries solve the problem. But there has to be code that applies the algorithms; there has to be a reasonable guarantee that the code will actually be used in the appropriate situations; and dates need to encode the information about which database version to use (otherwise you will run into problems e.g. if you're trying to plan around future dates; "render the date according to the current rules for the current locale" isn't always correct, and "render the date according to the rules for the place and time when and where the object was created / data was stored" isn't either, and "render the date according to the rules for the place and time that it represents" isn't either).

tl;dr: dates are hard because they represent an intersection between adjusting a model to meet physical reality, with l10n/i18n concerns (even if you don't care about language translation). Both of those are hard enough individually already.


Let's say I'm sorting some information about an event occuring in Florida, USA next year. It will be on July 4th at 3:00pm. I store this as an ISO string: "2025-07-04T03:00:00Z-5".

Florida (and the USA as a whole) have been discussing getting rid of the daylight savings change on-and-off for several years.

If that law goes through in December of this year, the date I stored for my event will now be off by an hour because come July 2025 Florida will be on UTC offset -6, not -5.

On the other hand, if I store the date with a fully qualified tz database timezone like this: "2025-07-04 03:00:00 America/New_York" the time will continue to be displayed correctly because the tz database will be updated with the new offset information.


If the calendar application want to be really accurate in a couple of years it's probably best to ask the user for coordinates for the event. You never know if that spot in Florida will be part of America/New_York next year.

(Of course a tz identifier will be better than an integer offset, but I'm only half joking: https://en.wikipedia.org/wiki/Time_in_Indiana#tz_database)


> You never know if that spot in Florida will be part of America/New_York next year.

The example really threw me; in the case where you assume that Florida stops observing DST, Florida is definitely not going to be part of America/New_York, so that example is guaranteed to have the same problem as the UTC timestamp.


You're highlighting an important edge case here: The TZ database only splits regions reactively, not proactively.

But an actual lat/long pair is often neither available, nor desirable to be stored for various reasons.

Now that you mention it, I think I've seen some web applications that had a list of cities much longer than what's present in the TZ database for calendar scheduling purposes, probably to accomodate for just that future edge case.


I appreciate those longer lists, though they do have some bizarre omissions.


The very most difficult part of writing date and time handling code is interfacing with systems whose developer thought date and time handling was completely straightforward :P.

It's hard, but it's not that hard so long as all the data is there. The biggest difficulty is working out the right frame of reference to store timestamps with. That's mostly only a catastrophic problem for future events involving people, but (as others in the thread note) it's still potentially an issue for audit (and other) data too.

Knowing which time zone a meeting is supposed to be held in is especially important around time zone changes.


> but I find the idea that JavaScript (or any language) needs built-in constructs for this purpose to be questionable.

Either this, or every project starts with a 200kb date library import. In reality, I haven't met a single project that I don't need to deal with dates and timezone. And I almost always have a date.ts in my utils library because I don't want to import a giant date library either. But for real, developers shouldn't need to deal with this. These operations and constructs that every sites need should be build into the language itself.


> Either this, or every project starts with a 200kb date library import.

It's interesting you say this but then go on to explain that you roll your own date library. Doesn't that discredit the idea that the alternative for every project must be a 200kb date library? I agree that most projects work with dates and time zones in some way, but that doesn't mean we'd have to use moment.js in the absence of the Date API. There's plenty of incompetent developers that would still do as such in the current year, but then this becomes a whole different argument.


The 200kb is already an underestimate. If you search temporal date polyfill and check the js size. It's 200kb for 'single' language. Date is much more complex than you expect.

And even yourself don't use it. The library authors will. There will be someday that people take the existence of temporal date api for granted. And you have no choice but import the polyfill if you want to deal with old browsers.


Shouldn't datetime handling happen in the backend. I cannot imagine why you would trust the client to give you accurate time.


If you want to display a time you have in UTC in the user's local timezone, what other choice do you have (unless there's a way you can ask them for their timezone, but I always find that incredibly annoying when traveling)?


Here's a simple example for where ISO timestamps break down:

"Thank you for meeting with me on 2024-03-27T15:00:00-07:00! See you same time next week."

When was the next meeting?


I think the author is talking about how ISO dates typically get converted and stored as integers or normalized (to UTC) ISO strings. If a payment was made in a different timezone from the server, the timezone locale (e.g. America/Los_Angeles) also needs to be separately stored in the db in order to preserve the full human readable meaning of the original payment date.

If timezone offsets are separately stored (or the UTC normalized ISO strings), this still isn't sufficient to know which locale the offset is referring to since multiple locales use the same timezone offsets.

It gets even more complicated when different locales shift their timezone offsets throughout the year due to daylight savings.


Something built into the language that can do real date math that accounts for leap seconds, DST, and so on is unusual. Especially if it's handling details like saving the timestamped history of political decisions in various places to start/stop using DST. Maybe not earth-shattering since you can get it with a library elsewhere, but handy.


It's long, and has examples in C#, but this article by Jon Skeet should instill some healthy doubt in your convictions:

https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a...


How does this API deal with political timezone and DST changes? Will JavaScript versions come with an updated hardcoded database in the spec, or browser updates, or will it require internet connection to get it from some server? What if 2 clients or a client and a server have different versions of this database, could inconsistency issues arise? Will all past changes keep being handled correctly (e g. if a region changed their timezone or DST rules multiple times in the past, does it take this into account for any past date)? How will the date math work on future dates where the DST rules are not yet known (e.g. could a future date change its display or UTC value after an update)?


Temporal specifies using IANA Timezone Database for all time zone aware implementations and recommends them to "include updates to the IANA Time Zone Database as soon as possible." Moreover:

> If implementations revise time zone information during the lifetime of an agent, then which identifiers are supported, the primary time zone identifier associated with any identifier, and the UTC offsets and transitions associated with any Zone, must be consistent with results previously observed by that agent.

Basically, the timezone data must not change while a browser tab, a Node program, or a service worker is running.

AFAIK, this does not prevent possible timezone data mismatch between different versions of browser and server, or even different browsers on the same machine.

https://tc39.es/proposal-temporal/#sec-use-of-iana-time-zone...


Looks very similar to Java Joda time, released in 2005 (later adopted as java.time package).


IMO, the pattern that Joda popularised is the most robust option for managing dates we've seen so far. If I'm evaluating date/time libraries in a language I'm not used to, ones that looks like their authors have looked at and understood the API of Joda or derivatives are going to be strong leaders.


I get the same impression. Not identical but the whole temporal API seems heavily influenced by Java.

Having worked with dates and times in both Java and JavaScript, I'm very happy with what I see in the temporal API. Huge improvement.


Every language should just copy Joda time's API. It nailed it.


I also came down to say the same thing -- Joda's library was the best library to deal with date & time ( Now with all the changes copied to the standard library, I haven't used that library in years )


Agree! And I like no longer having to use js-joda to support these types :)


The article would be more convincing if it used more examples of dates in the future.

For timestamps (i.e. recording things that happened), I still believe all you need is UTC and a location.

The bank example is just a matter of poor UX, not lost information (surely the bank could map transactions to a local time zone if they wanted).


> The Temporal API represents a revolutionary shift in how time is handled in JavaScript, making it one of the few languages that address this issue comprehensively.

Note that there's also a Rust datetime crate, jiff, that's inspired by the Temporal API and features zone-aware datetimes (from burntsushi, author of ripgrep and Rust's regex library): https://crates.io/crates/jiff


Yes! A number of projects have already adopted Jiff too. numbat and gitoxide to name two big ones. :-)


Temporal will be really nice in that it supports remote time zones, allows you to iterate through daylight saving transitions, etc.

I can't wait for it to be fully taken advantage of by things such as mui's date time picker. Imagine if after selecting the fall back day, it saw the transition and let you pick a time during the extra hour. If after selecting the spring forward day, it wouldn't let you pick a time during the hour that doesn't exist. Today that doesn't work because everyone uses different crappy third-party libraries and mui functions on top of a lowest-common adapter that certainly doesn't do stuff like "show me the DST transitions in this day".

This stuff matters sometimes; a user of my NVR wanted to save a clip during the fall back hour and got the wrong result because these things just don't work right today. https://github.com/scottlamb/moonfire-nvr/issues/244


My projects requirements are usually not timezone-aware, and/or I’m slow to see how this helps these.

What I’d like to have (in addition, not as a replacement) is timeless dates. I can store/carry a timezone manually, but give me a proper date[-notime] datatype. I mean, I sort of have it with just “yyyy-mm-dd” and calculating with temporary “T00:00:00Z” suffix. But why would this new format solve my “no timety” impedance mismatch?

More specifically, please give me all the components separately and then design interfaces which may accept any number of these and make sure it makes best sense. Dates without times, times without dates, dates or times with or without timezones, dates without months and/or days, days without years and/or months, weekdays, simply timezones, and so on. Having new dates like subj is yet another piece in the date logic tetris you have to play. Stop imagining developer stories and make them all 1x1 square.


The Temporal API discussed here includes PlainDate btw. See https://tc39.es/proposal-temporal/docs/plaindate.html


This thread is making me panic that I don't understand handling time in programming and now I am paranoid that one day it will bite me hard.

What is a good primer in understanding most of the serious issues raised in this thread and how they manifest themselves in (preferably) languages like Python?


Read the luxon.js docs (the timezone section.) It’s the successor to moment.js. They do a great job explaining the common footguns developers face.


Interesting, some of us migrated to date fns since


If you read GitHub issues for datefns you’ll find examples where it doesn’t handle timezones correctly


This is for .NET, but the issues described stand for any stack:

https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a...


Is there a timeline? I've been waiting for Temporal since like 2016 or so and it's still not ready.


It’s stage 3, and they offer production-ready polyfills. For a project I work on, that’s a reasonable enough approximation of “ready”. Your project needs may vary. But it should be stable enough to use now with a userland implementation if the API is what you’re after.


Where can I find production-ready polyfills? https://github.com/tc39/proposal-temporal/#polyfills only lists alpha and beta versions.


Safari even has a flag already. I'd expect it to officially land in 2025 though. It's not a focus of Interop 2024 https://wpt.fyi/interop-2024


Maybe I'm missing something but it seems like much ado about nothing.

Just use a timestamp — if you need to know the associated timezone, just store that as well. Done.

But most often (when the timezone where the event was captured isn't relevant to the user or the business logic) conversion to local time is a frontend/view concern.


Sort of relatedly, I’ve been fighting Safari bug for years that feels like it has to be related to dates in js.

Safari’s saved credit card support works pretty well, but when I switch between US and Asia timezones the expiration date will shift by a month in one direction. My best guess is that they store the expiration as midnight local time on the 1st of the month in a js Date which can shift when you travel outside of that timezone.


Am I the only one who feels like Date is actually a decent API, quite easy to use and good enough for almost everything I’ve ever needed? Temporal seems needlessly complex and not something I want to use.

Sure, Date could be better. I wish Dates were immutable. I wish UTC was the “default” instead of having to call all the UTC methods. And sure, formatting via toLocaleString() isn’t the most intuitive way to do things. But… it works and it’s pretty straightforward.

If I need time only without the date, there’s a method for that. If I want ISO 8601, there’s a method for that and it’s even the default when the Date is sent in JSON. If I want a UNIX timestamp, there’s a method for that and it’s even the default when the Date is used as a number, which also makes Dates easily comparable and sortable. The `Intl` API has a lot of ways to format Dates.

IMO Dates are underrated. Though they could benefit from a more ergonomic formatting method, a la `moment, maybe a more flexible parser, and some support for leap seconds.


Good, I also used to think storing timestamps in UTC was sufficient. The examples really explained the problems with that well. One other issue to note is that, if you store dates in UTC, now the fact that they are in UTC is a tacit assumption, probably not recorded anywhere except in organizational memory.

So the new API will be a very welcome addition to the JS standard library.

However, and this is probably a bit off-topic, why are the ECMAScript guys not also addressing the fundamental reasons why JS continues to be somewhat a joke of a language?

- Where are decimals, to avoid things like `parseInt(0.00000051) === 5`?

- Why are inbuilt globals allowed to be modified? The other day a 3rd-party lib I was using modified the global String class, and hence when I attempted to extend it with some new methods, it clashed with that modification, and it took me and hour or two to figure out (in TS, no less).

- Why can't we have basic types? Error/null handling are still haphazard things. Shouldn't Result and Maybe types be a good start towards addressing those?


Being able to modify built-in types is extremely useful in practice. This allows you to backport/"polyfill" newer features, like for example improvements to Intl or Temporal to older runtimes. If all the built-in types were locked down, we'd end up using those built-in types less because we'd more frequently need to use userspace libraries for the same things.

Like, you are asking for a Result type. If this was added to the spec tomorrow and existing objects were updated with new methods that return Result, and you can't modify built-in types, then you can't actually use the shiny new feature for years.

On the specific point of Maybe type, I think it's not very useful for a dynamically typed language like JS to have "maybe". If there's no compiler to check method calls, it's just as broken to accidentally call `someVar.doThingy()` when `someVar` is null (classic null pointer error) versus call `someVar.doThingy()` when someVar is Some<Stuff> or None, in either case it's "method doThingy does not exist on type Some<...>" or "method doThingy does not exist on type None".


> Like, you are asking for a Result type. If this was added to the spec tomorrow and existing objects were updated with new methods that return Result, and you can't modify built-in types, then you can't actually use the shiny new feature for years.

And that's a good thing, because you know that with a specific version of JS, the inbuilts are fixed; no mucking around to know what exactly you have, and no global conflicts. I find it surprising that you would defend this insane state of affairs. If you have worked on really large JS projects, you would see my point immediately.

It is like saying the immutability of functional programming is a bad thing because it limits you. The immutability is the point. it protects you from entire classes or errors and confusion.

> If all the built-in types were locked down, we'd end up using those built-in types less because we'd more frequently need to use userspace libraries for the same things.

This is the correct solution for now.


> - Where are decimals, to avoid things like `parseInt(0.00000051) === 5`?

There is a draft proposal for this: https://github.com/tc39/proposal-decimal

Additionally, BigInt has been available for years: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... (unfortunately does not serve as a u64 / i64 replacement due to performance implications and lack of wrapping/overflow behavior)

> - Why are inbuilt globals allowed to be modified?

> Error/null handling are still haphazard things.

How would you suggest addressing these in a way that is backwards compatible with all existing web content?


> How would you suggest addressing these in a way that is backwards compatible with all existing web content?

Version numbers. Solving the problem in the future but not the past is still better than leaving it unsolved in the future and the past.


> Why are inbuilt globals allowed to be modified?

Because that was the original behavior, and you can't just change that behaviour, or half of the web will break (including that 3rd party lib and every web site depending on it)

> Why can't we have basic types? ... Shouldn't Result and Maybe types

Neither Result nor Error are basic types. They are complex types with specific semantics that the entire language needs to be aware of.


So clean the language up, call it a slightly different name if you want, and let those who want to go modern do so. For those who can't, offer maintenance but no major new features for the original version.

Being wedded to mistakes made in the past for fear of breaking current usage is the root of all programming language evil.


> For those who can't, offer maintenance but no major new features for the original version.

How do you imagine doing that?

> Being wedded to mistakes made in the past for fear of breaking current usage is the root of all programming language evil.

Ah yes, let's break large swaths of the web because progress or something


They could use new directive though, ie:

    "use awesomeness";


    from future import awesomness


> - Where are decimals, to avoid things like `parseInt(0.00000051) === 5`?

I personally dislike decimals, I would prefer a full BigRational that is stored as a ratio of two BigInts (or as a continued fraction with BigInts coefficients.

Decimals solve the 0.1+0.1+0.1!=0.3 error but are still broken under division.


>Why can't we have basic types?

The same reason you couldn't in 1996: because the assumed entry point for the language is someone who can barely conceptually manage HTML; doesn't want to have to deal mentally with the fact that numbers and text are fundamentally different kinds of thing; and - most importantly - absolutely will not accept the page failing to render correctly, or the user being shown an error message from the browser, for any reason that the browser could even vaguely plausibly patch around (just like how the browser has to guess whether an unescaped random stray < is just an unescaped less-than symbol or the start of an unclosed tag, or guess where the ends of unclosed tags should be, or do something sensible about constructs like <foo><bar></foo></bar>, or....)


Does this mean I will no longer have to suffer with so many sites only offering relative times in lists without even hover for absolute date & time?

I’m looking for a particular build or deployment near a certain date and time and I must first calculate how many days ago it was then binary search through the items from that day.


> Does this mean I will no longer have to suffer with so many sites only offering relative times in lists without even hover for absolute date & time?

Why would it mean that? It seems tangentially related at best. The sites you’re complaining about don’t use the presentation you’re complaining about for lack of more precise Date APIs, they use it intentionally as a feature (even if you consider it a misfeature).


Can anyone shed light on the timeline behind the rollout of the Temporal API? I've been reading about it for many years, and it's always "soon" or "right around the corner". I'm kind of annoyed by all this teasing.


"fixed"

And won't be available in non-chromium browsers and LTS versions of Node until 2028.



Whats your source for it not being available in browsers/node until 2028? Update cycles are pretty quick these days for browsers and mozilla/safari has been pretty good to add APIs that don't increase attack/privacy/interface surface.


This is a great and important topic, one that I’ve bumped into against in past code. I think I understand the basics of time/zones/storage.

However, on one codebase I worked on in the past, dates were stored in mysql in a datetime field, with the time set to 00:00:00, iirc.

I’m hoping someone knowledgeable about this topic could comment on this. I never could discover why it was done that way. It wrong? Is it right? Based on the other threads here, it seems wrong, but maybe there’s a non-obvious reason to do it this way?


This is a solution for a problem that does not exist. It's just unnecessary bloat. If you need to store the time zone specifically, do that. Use a library or create an child object with an extra field. There are very few use cases where you need to know specifically what time zone a given DateTimeStamp was originally stored in.


Is this article arguing that a timestamp should include space ? I get that timestamp can be display differently depending on the space (i.e. TZ and locale).

But does it mean that we need to add the space component to a timestamp ? Feels like 2 distinct things. Sure, when I record an event, I might not only want to record "when" it happened, and "where". But the "where" does not change or impact the "when", and vice-versa. Those feel 2 distinct properties to me, and I might want to store them separately.

Maybe I am missing something.


For past timestamps, arguably a UTC record of when it happened is sufficient as converting it for display in a specific timezone is a fixed, and no longer mutable transformation. For a future looking time, it's not sufficient, because e.g. DST rules might change, timezones might change (and there's usually at least several occurrences a year of some timezone or DST rule changing somewhere in the world). If you have a reservation for the concert hall from 6pm next September, you don't want to show up there with the previous show still occupying the venue just because your government abolished DST in the year in between.


Perfect explanation. I did not thought about future events and changing TZ rules (which happen obviously depending on the country/government). Thanks!


A complete timestamp should include a timezone, and a timezone is best defined with a geographic location.

Example: you want to schedule events in Madrid, Spain and Warsaw, Poland. Currently they both use the CE(S)T timezone, but the EU is looking into abolishing summer time - which would lead to countries adopting their own timezones. At the moment "Europe/Madrid" and "Europe/Warsaw" are identical to CET/CEST, but that hasn't always been the case in the past and obviously there's no guarantee it'll always be the case in the future.

If you stored those events as CE(S)T, what are you going to do when summer time gets abolished and Madrid ends up at +1:00 year-round but Warsaw at +3:00 year-round?


Kind of, but space in the social sense of "set of places and people agreeing on what is local time", not the physical one.

We need to record that whenever we want to talk to people about time and schedule things for them, just like we need to record their language and regional format preferences.

In other words, you might not need that level of metadata for a physical simulation, but you'll definitely need it for a calendar app. For everything in between, it depends, and the safest bet is accordingly to just do it, if you can.


Seems shortsighted to make dates fixed. The date changes almost every day


I’ve been using Temporal, via a different polyfill than in the article[1], in production for about 6 months now. It’s a game changer. But JavaScript people are also broken from years of not having this so it’s been a hard sell. To many it’s just extra work. We’re about to start doing international stuff though so really hoping it becomes clear why it’s amazing to the team soon.

[1]: https://github.com/js-temporal/temporal-polyfill


It's a shame that this new API still doesn't support querying leap-second information within web-based JS, in any shape or form. The only solutions are either to store a big list yourself and keep it updated, or download it from someone else's website and trust them to keep it updated and keep their CORS settings permissive. Meanwhile, most OSes are fully aware of leap seconds (or at least, browsers could easily include an updated list), but there's just no way to retrieve them from the inside.


This is great but I really hope someone contributes strftime / TR35 to the v2 proposal, there's still no way to make date format strings in JS, despite being common in every other language, e.g. "%Y-%m-%d_%H-%M-%S_%s", or "%m/%d/%H/%M:%S".

- https://github.com/js-temporal/proposal-temporal-v2/issues/5...


Am I naive? It seems like POXIS ignoring leap seconds is a major mistake. Wouldn't a standard that means "number of seconds since January 1, 1970 UTC" be much more useful than "number of non-leap seconds since January 1, 1970 UTC"?

Does this mean that converting two DateTimes into their POSIX time and subtracting them will not yield an accurate representation of number of seconds between two timestamps?


> Does this mean that converting two DateTimes into their POSIX time and subtracting them will not yield an accurate representation of number of seconds between two timestamps?

It will indeed not, and if you care about that difference, you should not do that, and use something like https://github.com/qntm/t-a-i instead.

> Am I naive? It seems like POXIS ignoring leap seconds is a major mistake.

Depending on your needs it can be either seen as a massive mistake or a big convenience win.

In either case it's arguably not a complaint with POSIX, but rather with the commercial and scientific world at large using UTC instead of TAI (and there are indeed people arguing for abolishing leap seconds!). POSIX in turn just followed that standard.


If we simply stored all timestamps as "number of seconds since X" then we could safely convert that to whatever timezone, apply whatever leap adjustments, apply daylight savings time, convert to whatever calendar, etc anywhere we want

Instead of browsers/libraries handling all the complexity (standards and localization, dst, leap seconds, etc), couldn't we just do all our math and logic in this format and then convert back to whatever when we need to display a time to a user.

It seems like saving a #seconds along with lat/long information is enough to handle all timestamp use-cases.


> If we simply stored all timestamps as "number of seconds since X"

That works – if you never want to represent dates in the future (because we don't know how many leap seconds there will be in the future). But that way, you'd be incapable of implementing even a trivial calendar application involving more than one timezone.

> couldn't we just do all our math and logic in this format and then convert back to whatever when we need to display a time to a user

We could – using all the libraries encapsulating that complexity.

> It seems like saving a #seconds along with lat/long information is enough to handle all timestamp use-cases.

That's even worse, since often that's not available, and even if it is, now you need a database representing local UTC offsets as polygons on the surface of the earth!


> It will indeed not, and if you care about that difference, you should not do that, and use something like https://github.com/qntm/t-a-i instead.

That solution, as with most simple leap-second solutions, relies on the fixed file being kept up-to-date ad infinitum to account for any future leap seconds. Unfortunately, there isn't and still won't be any way to get a current list of leap seconds from JS, except for downloading them from someone else's API.


> That solution, as with most simple leap-second solutions

I never claimed it was a simple solution, just that it was necessary if you do care about that difference :)

The only solution that works for the future as well as for the past would be to schedule and communicate only in TAI, but good luck trying to convince the rest of the (non-scientific, and a good chunk of the scientific as well) world of that.


Is there any built in for human strings for durations and times?

“5 days ago.”

“3 hours and 33 minutes.”

I’ve found this kind of thing to be the main reason I have to drag MomentJS along for the ride.


Pet peeve: I mostly hate these.

Anything for maybe the last hour or I can see; "1 day and 5 hours ago" and making me hover over the string (and pray that there's an alt text available that I still can't copy-paste) is very annoying.

I really wish, space permitting, web designers would start at least including the actual timestamp in the regular/non-alt text as well, at the very least for professional tools if not for social media sites.


agreed it can be misused so easily.

One time it works is when your team is across many time zones, the data being listed spans many time zones, and what’s important is knowing the recent events from older ones. And yes, also showing the absolute UTC stamps too


I’ve written versions of that half a dozen times over my career. I recommend ditching MomentJS and pasting in your own ~15 line implementation.

I’m confident ChatGPT or Claude will write you one with one-shot prompt that does exactly what you want. Here’s an attempt at that: https://chatgpt.com/share/9bfe1d60-34cc-46fc-8327-3474c4a7d6...


It’s already available in browsers as part of Intl, no need to wait for Temporal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


Oh perfect! Thanks. I was looking at some other MDN page that suggested basically 0% support.


Yes! You’re looking for DurationFormat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... which will work with Temporal types


This looks perfect! Just gotta wait until anything supports it. I’m hoping it’s progressing and there’s plans to adopt it.


I sure hope so. Moment + Moment TZ = just shy of200kb gzipped. Learned that one the hard way the other day.


>I’ve found this kind of thing to be the main reason I have to drag MomentJS along for the ride.

Likewise. It feels long overdue to have template based formatting natively.


Surprising it took this long. I think timezones were missing in earlier versions of Python as well. I wonder why they weren't considered.


I understood it to be because the TZ library changes frequently enough that it should be an external package. Date math shouldn’t change based on the language version you’re using, nor should you have to upgrade the language when Arizona finds a new way to creatively alter time.


> TZ library changes frequently enough

The zone database adds more historic entries than they do modern ones. You can; however, calculate the correct 1942 "summer war time" in the british isles if you're so inclined. The tendency to do this while also copying copyrighted text into the file shows that the zone maintainers are not interested in the same problems most users are.

The TZ database is a complete mess. The whole thing should be replaced with a '.timezone' TLD, each civil time keeping authority should get control over it, and TXT records used to publish current TZ information. OS Vendors can cache all that in the OS if they like, but this secondary central time authority has outlived it's usefulness.


How do other languages handle this? Do they take the timezones database from the OS, which keeps it updated?


In Java, there is a bundled 'tz' file. It gets updated frequently, here's an example: https://bugs.openjdk.org/browse/JDK-8325150 It is possible to update it manually.


I think that'd be the worst place to take it from for the same reason. If I build a package using today's current TZ database and it works on my machine, it shouldn't stop working if I share it with someone using an old CentOS from several years ago.


You're right. But then tz updates would need a patch.


True, but it’s generally a lot easier to patch software than expect someone to update their whole OS.

(Generally. I know plenty of shops deployed Exchange in 2009 and it still works so why fix it, goshdarnit! Those people aren’t generally updating their OS either though.)


Probably just one of those hard problems which looks easy from the outside. Nobody wants to handle that thankless mess, which is guaranteed to be broken on any of a hundred edge cases.


And PHP


They were added to php in 2005. Js is at least 19 years late to the party


I wonder, when dealing with human readable formats, is there a notation dealing with the repeated hour at the end of daylight saving time (DST)?

E.g., in the US, DST ends on Nov. 3, 2024 and the hour from 1:00 to 2:00 am will be repeated. Is there a way to distinguish the second pass from the first one?


For human-readable purposes I usually just stick in the timezone. 1:59am EDT plus 1 minute is 1:00am EST.


This is a step in the right direction, but it really needs to be supported everywhere so that there isn't friction when crossing into Javascript, and out to a database or other language.

A language-agnostic test suite should be provided, including every edge case, so that this standard can be implemented everywhere.


Yes, it's being standardized by the IETF, here's the RFC: https://www.rfc-editor.org/rfc/rfc9557.html


Deno already supports it as many other web api: https://docs.deno.com/api/web/

Who would have thought that a JS / TS backend framework would not only be a convenient but sane choice.


This reminds me of crunchyroll, the anime website, which uses local utc us with fixed times for episode releases. This creates confusion in the rest of the world, because for 2 weeks, there's a time difference until daylight saving time changes in the US.


It is a source of wonder to me that every programming language I know has to go through this process where the design half-asses time handling and end up coming back decades later with a whole new API which, inevitably, fewer people use than the older, footgun-friendly API.


The military solution is to standardize on a zero offset time, specified as time zone Zulu. That is just a named time zone representing the TAI variant of UTC.

The challenge is that local computer time comes from a time and time zone set locally and stored in the hardware CMOS. That is not precise, varies from computer to computer, and provides all the offset problems mentioned in the article. Network hardware solved for that long ago using NTP. So the military applied a similar solution as an OS image enhancement that receives updates from a network time service whose time is defined according to the mesh of atomic clocks. Then it does not matter what offset is shown to the user by the OS when all your reports and data artifacts log against a universally synchronized time zone defined by a service.

The military solution works fine for the military but it requires centralized governance normal people do not have.


"About to" feels like a stretch to me, unless there's actual evidence for that. Temporal has been stuck in the bikeshedding phase for years, the same as a bunch of other promising JS proposals.


"about to be fixed" is pretty clickbaity with no substance.

From the July TC39 meeting [0]:

> After 7 years, Temporal is close to done. Our only focus is making sure in-progress implementations are successful, by fixing corner-case bugs found by implementors, and making editorial changes to improve clarity and simplicity.

It's not (yet) on the October TC39 agenda [1].

The Chromium implementation issue hasn't been updated since 2022. [2]

Webkit's implementation issue was updated once this year... to add a blocker [3]

V8 is passing 75% of test262's tests, and JavascriptCore is passing 41%. Spidermonkey is a no-show. [4] Note that test262 is incomplete and hasn't seen activity this year [5], so those percentages may decrease over time.

"about to be fixed" ehhhh. If it lands this year I'd be surprised.

   [0] https://ptomato.name/talks/tc39-2024-07/#2
   [1] https://github.com/tc39/agendas/blob/main/2024/10.md
   [2] https://chromestatus.com/feature/5668291307634688
   [3] https://bugs.webkit.org/show_activity.cgi?id=223166
   [4] https://test262.fyi/#|v8,v8_exp,jsc,jsc_exp,sm,sm_exp
   [5] https://github.com/tc39/test262/issues/3002


Yeah, I’ve been waiting for this for years and not optimistic it’s “near”.

The polyfill is too heavy and the services we deal with seem to cover the whole range of dates and times. Julian, epoch, ISO-8601.


On a related note, here's an algorithm for parsing user input into a sanitized time format:

    String.prototype.toTime = function () {
      var time = this;
      var post_meridiem = false;
      var ante_meridiem = false;
      var hours = 0;
      var minutes = 0;

      if( time != null ) {
        post_meridiem = time.match( /p/i ) !== null;
        ante_meridiem = time.match( /a/i ) !== null;

        // Preserve 2400h time by changing leading zeros to 24.
        time = time.replace( /^00/, '24' );

        // Strip the string down to digits and convert to a number.
        time = parseInt( time.replace( /\D/g, '' ) );
      }
      else {
        time = 0;
      }

      if( time > 0 && time < 24 ) {
        // 1 through 23 become hours, no minutes.
        hours = time;
      }
      else if( time >= 100 && time <= 2359 ) {
        // 100 through 2359 become hours and two-digit minutes.
        hours = ~~(time / 100);
        minutes = time % 100;
      }
      else if( time >= 2400 ) {
        // After 2400, it's midnight again.
        minutes = (time % 100);
        post_meridiem = false;
      }

      if( hours == 12 && ante_meridiem === false ) {
        post_meridiem = true;
      }

      if( hours > 12 ) {
        post_meridiem = true;
        hours -= 12;
      }

      if( minutes > 59 ) {
        minutes = 59;
      }

      var result =
        (""+hours).padStart( 2, "0" ) + ":" + (""+minutes).padStart( 2, "0" ) +
        (post_meridiem ? "PM" : "AM");

      return result;
    };
I posted it a while back to the following thread:

https://stackoverflow.com/a/49185071/59087

One of my minor bug bears is why we humans have to enter time into input fields in such rigid formats when there's a straightforward algorithm to make such inputs far more natural.


> One of my minor bug bears is why we humans have to enter time into input fields in such rigid formats when there's a straightforward algorithm to make such inputs far more natural.

GNU date supports very human-oriented date input, some examples:

  $ date --date "today"
  $ date --date "next sunday"
  $ date --date "now + 3 hours"
  $ date --date "3 months" # same as "now + 3 months"
  $ date --date "5 december 2020"
  $ date --date "oct 19 2007"
  $ date --date "08:00"
  $ date --date "8:00"
  $ date --date "0800"
  $ date --date "0800 UTC"


Your function converts "0:23" into "11:00 PM".


If you were a human, you would have written "00:23", not "0:23".

It'll also convert "0:45" into "00:00AM" (and "0:23" into "11:00PM").


> If you were a human, you would have written "00:23", not "0:23".

No, I wouldn't. Leaving out the leading zero is pretty common where I'm from.


You come from a weird place.


Don't worry, we say the same about the US, where 12pm comes before 11pm.


Does that mean that in your country, the day is considered to start at 1:00 AM rather than midnight? That choice is very strange when combined with the choice to call midnight "0:00" rather than "12:00 PM".


No, we just don't use AM/PM. In written language, the 24 hour format is used exclusively. When the 12 hour format is used in spoken language, we either leave it out entirely when clear from context, or use "in the morning" vs "in the evening".

So noon is "12 in the morning", which is an hour after "11 in the morning"; and midnight is "12 in the evening", which is an hour after "11 in the evening".


That's not really compatible with your comment about the US. By your own description, you do exactly the same thing that the US does,† but it's only strange when the US does it?

† That is, you follow 11:00 PM with 12:00 AM, and you don't actually refer to "AM" or "PM". Both of those are normal US practice. You are different in the use of 24-hour formats in writing, but that has no bearing on the question of whether the hour following 11 PM is 12 AM; you just assured me that that's true in both countries.


In your comparison, we follow 11:00 PM with 12:00 PM, not 12:00 AM.


So the day begins at 1:00 AM rather than midnight?


No, the day begins at midnight, we just call it 12:00 PM.


Good catch. Easy fix:

    // Preserve 2400h time by changing leading zeros to 24.
    time = time.replace( /^0*[0:]/, '24' );


> Easy fix:

That converts "0800" into "00:00AM".


True enough, the regex was overzealous. This looks better:

    time = time.replace( /^0*(0:)+/, '24' );


The use of “instant” to denote a time coordinate always rubbed me the wrong way. While “instant” arguably can mean a precise point in time (“in that very instant”), the primary dictionary definition is a (very small) time interval. This is also reflected in the meaning of “instantly”, which does not mean “simultaneously” or “at the same point in time”, but rather something like “immediately after”. Similarly for the adjective “instant”, which likewise generally implies a chronological succession, however short. I can’t quite put my finger on it, but it instinctively feels off to me to use it to denote timestamps.


The first dictionary definition of instant is “a precise moment in time”. That usage seems perfectly to fit how it’s used for a timestamp.


That might depend on your dictionary: https://dictionary.cambridge.org/dictionary/english/instant

In any case, it’s about how the word is normally used. In normal language you never say something like “those two instants are three hours apart”, or “three hours passed between the two instants”. In contrast, you would say something like that with “points in time”. For example. one can say “this was observed at two different points in time”. Nobody would say “this was observed at two different instants”.


The value of this didn’t really hit me until this line (which frankly should be the lede):

“This precision means that regardless of DST changes or any other local time adjustments, your date will always reflect the correct moment in time.”

Probably a failing on my part but I had somehow assumed all this time that calculating DST was a separate concern from time zone offset, given how tremendously opaque it can be.

To my knowledge, there is an area in Arizona that does observe DST, within an area that does not observe DST, within an area that does, within Arizona at large, which does not. Does this API really accurately represent this peculiar arrangement?

It looks like canonically there’s an America/Shiprock timezone but you’d think you’d need at least 4 to accurately represent the various areas.

https://upload.wikimedia.org/wikipedia/commons/3/38/AZNMUT_D...


> To my knowledge, there is an area in Arizona that does observe DST, within an area that does not observe DST, within an area that does, within Arizona at large, which does not. Does this API really accurately represent this peculiar arrangement?

What are you envisioning? That sounds like two timezones, with different regions belonging to one or the other. Does it matter to the API if two regions that aren't contiguous with each other share a timezone?


Dealing with dates in JS is easily one of the most annoying parts of the language. I usually avoid it at all cost.


Can we fix the numeric types next? Having the integer types and decimal types would resolve a whole lot of issues.


I can’t remember wishing I had int32 or uint8 if that’s what you mean. Not having int64 or int128 is kind of annoying but bigint fills in there fine.


I think they mean splitting the "Number" [0] type into an integer type and a decimal type.

Currently the "Number" type is used for every number and it gets stored as a double, and can "only" represent "integers" safely in the range of ±(2^53 -1). Though there is a "BigInt" [1] type.

[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data...

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data...


Mainly:

* Integer types of various sizes (for efficiency) and so you don't have to constantly do floating point comparisons (i.e. equal within a small delta) and generally have better compatibility with other languages. * Decimal floating point type so you can work in base-10 and don't have rounding issues.

Plus all of the error checking you get for free if you're using typescript instead of raw-dogging it with plain javascript or coffee-script :-)


Since timezone definitions change, how do we guarantee consistent behavior across browsers and browser versions?


I had to write a bunch of date logic in go a few weeks ago. Assumed it would be riddled with bugs, so wrote a dozen test cases for time zones, daylight savings, and leap seconds.

Everything just worked. Not one bug. A good library is so so helpful for complex use cases like dates.


If they don’t fix the 0 indexed months, this will all be for nothing.


Yeah, but imagine how much would break if they changed that.


It's about.... time


I was going to post:

Well, about time.


“About to be fixed” ≈ “this is the year of Linux on the desktop”

Wake me up when a browser ships Temporal.

Edit: Here’s Temporal on caniuse.com: https://caniuse.com/?search=temporal

Handful of APIs in the Safari Technology Preview, otherwise nada. I look forward to Temporal landing but it is still a very long way off.


scnr

So JavaScript gets what Java got in version 8, 10 years ago?


The Java team didn't initiate the process of making it an Internet Engineering Task Force standard; whereas this team did. Even though this is JavaScript centered, it really represents a much broader effort, that should help interoperability between languages and across online systems.


While the IETF might move slower, Java has standards as well. The java-date-time API standardization started in 2007: https://jcp.org/en/jsr/detail?id=310, than a JEP was created in 2012: https://openjdk.org/jeps/150


The point was that language specific standards aren't nearly as important as broad industry standards.


I don't get it, you can already get the user time zone with javascript ``` Intl.DateTimeFormat().resolvedOptions() ``` a good library like dayjs can handle timezones


It's quite annoying to deal with different library dependencies that all picked a different userspace library for something that should be standardized. For example, you might ship a date picker component using dayjs, but I already adopted Luxon in my application, so now I need to write some janky adapter or end up using both.

I would love to not have to import dayjs or another userspace library, so that i can pass datetime-with-timezone to other libraries and instead use an IETF standard way of handling time that's backed by performant native runtime code.

In my experience, userspace timestring parsing and timezone conversion can also be a surprising CPU bottleneck. At Notion, we got a 15% speedup on datetime queries by patching various inefficiencies in the library we selected. The runtime can be more efficient as well as saving page weight on tzdata and locale stuff.


What do the authors of tzdata say about it?


What about external libraries like dayjs?


I guess the point is that you won’t/shouldn’t need an externak lib for basic date stuff.


How would I handle this with Postgres?


From the article:

"Let's consider an example: suppose I want to record the moment I make a payment with my card. Many people might be tempted to do something like this:

const paymentDate = new Date('2024-07-20T10:30:00');".

What? I don't think anybody would define a date like this to record some moment. Most developers would simply use new Date() without any parameters, thereby creating a timezone-safe absolute underlying timestamp value. This value then is of course stringified relative to the timezone of the caller when retrieving it, but that's expected.

The new Temporal api might be nice but it doesn't need flawed examples to make its case.


Unless the payment already happened?


Was this blog post written by sonnet?


about time, Go has had this since 2012:

https://pkg.go.dev/time#Date


Go probably has the last standard library I would take as a reference for my own datetime implementation.

Their datetime formatting implementation is simply nuts. Why not follow standards? Why the NIH?


agreed the format parser is stupid, but thats more a cosmetic issue. its had the ability to create location aware dates since the first version 12 years ago


> "... number of milliseconds since the EPOX given this CET instant"

What? UNIX epoch? Central European Summer Time?


For once, an article on the internet has commas in the right places. Well done.


Oh good, from the language that brought us WAT, a new complicated approach to timestamps, what could go wrong.


Still don’t get it. Is it to be able to show the user a local time for the timestamp? If that is a requirement , why not just also store the timezone along with the utc timestamp? Should the date-object really know this?


The problem is that the result of toUTCTimestamp(datetime: January 1st 2077 1:15am, timezone: New York) may be X today, but timezone rules could change soon, and then it becomes Y tomorrow. If you've only persisted (unixTime: X, timezone: New York) in the database and you try to turn X back into a local time, maybe now you get December 31, 11:45pm. If you're a calendar application or storing future times for human events, people will think your software is broken and buggy when this happens, and they will miss events.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: