Hacker News new | past | comments | ask | show | jobs | submit login
Temporal: Getting started with JavaScript's new date time API (2ality.com)
377 points by Amorymeltzer on June 28, 2021 | hide | past | favorite | 194 comments

Didn't find to much info on arithmetic support, although it is mentioned.

However, I hate working with dates because many APIs make question about dates a slog.

Consider the following for the poor people on the dark side of the planet (UTC + something). If any application only saves the date and not the time and you have dates like 2021-06-28T00:00:00 or something like that.

Now it gets saved in a DB as 2021-06-27Z23:00:00, because the user lives in UTC+1-land. It is correct but makes my blood boil and propably causes a lot of errors, even in apps that set their time zone correctly. How often have I seen implementations that just /(.*)T.*/ match the date and create a bug.

I have no solution for this, but I wasted too much time on problems like this. Such errors also notoriously evade unit tests...

This seems decent from a quick look. But let us be honest, it has to compete with moment.js, not with Date().

I've personally worked on issues stemming from this; situations where the date is important, but not the time, such as correctly reporting when it is someone's birthday.

One of the earliest axioms we drill into programmers is "use UTC, use date-time types". Don't store dates as strings, use your database's DateTime type.

If your database has a Date sans-Time type, use that. But many databases don't, and that's where the axiom starts breaking down, because its very natural to say "well, I'll use the next best thing, a DateTime type". But this is wrong! Its far, far better to simply use a YYYY-MM-DD string. I cannot stress how much better this is, but its not obvious why its better to someone who has had that axiom drilled into their heads, and has never ran into this situation before.

Worst case of this I've personally had to fix was an airline where you couldn't purchase tickets if you lived anywhere with a negative time zone offset. They're a small European carrier so it was never a very big issue for them, and all the developers and management were in Europe so they never noticed it first hand.

Basically when you tried to purchase a ticket, their frontend would take the date-of-flight, and adjust it by the user's timezone. So a ticket for 2015-01-30 00:00 would become 2015-01-29 20:00, or whatever. The dates in the request did not match what was expected for your booking code, and 500 error.

As a developer in Australia, I have made these sorts of errors that I didn't notice until my colleagues in the US picked them up. I am actually very happy that America is in a negative timezone, because I believe these errors would be a lot more prevalent if they were not.

Yeah, "always use UTC" is overrated. It's useful for things that have happened, but quickly becomes unintuitive for things that will happen - including birthdays, meetings, ...

It can even become wrong for future dates. Always use UTC is a broken, bad rule for future dates, it only makes sense for things that have already happened.

As for how it breaks, you have a booking for 8pm the day after the start of summer time. You stored the date/time in UTC. Government changes the start of summer time. Your database now thinks it's at 7pm because it uses UTC.

Here's some examples:


Also the EU is seriously debating getting rid of clock changes, and it comes up in the UK semi-regularly.

Your database will not fix your bad data when that happens.

Yes, timezones do not at all work with reservations for physical objects.

I work on a registration and reservation system for rowing boats in my rowing club https://github.com/dsroklub/roprotokol

The only time that matters is whatever the clock in the clubhouse shows (Danish time) If you sign out a boat from the UK (or more realistically from a laptop that thinks it is in the UK) at 14:00 it is still 14:00 Danish time. This is surprisingly difficult to achieve with JS Date object, browsers and popular JS components.

So the Temporal Plain time looks promising.

And especially the explicit timezones. Because even without timezone you need to handle DST. For example my system needs to know that if you start a 3 hour trip at midnight a certain day in the spring, you are expected back at 04:00.

I just hope that browsers and components will support is properly.

Even more common:

* Your meeting is scheduled for 2pm tomorrow.

* Oh oh! Your meeting got bumped to the next day.

* Daylight savings switched overnight.

Some things are measured in relative time. Some things are measured in absolute time. Never confuse the two in your database.

> Your database will not fix your bad data when that happens.

Because it’s not bad data, HaHaOnlySerious. I tell developers that if the database attribute accepts a value it must be in the domain of allowable values. Often people refuse check constraints in order to avoid expressing business logic in the database.

The GP's example doesn't involve invalid datetimes. The datetimes aren't out of bounds or invalid in any way that a check constraint would detect. They've just become factually incorrect ("bad data"), because they are derived data that wasn't updated when the derivation rules changed (i.e., regulatory changes).

If you're storing future datetimes that semantically represent wall clock time, you need to store the locale time plus the full time zone (such as America/New_York) so that your program does the right thing in response to any common regulatory changes that happen after you store the value. Storing the time zone abbreviation (e.g., EST) is inadvisable, as computers sometimes care whether you asked for EST vs EDT. Storing the time offset (e.g., -500) is incorrect, as it has the same pitfalls as storing UTC - you're precomputing the locale's expected time offset at storage time, and your data won't automatically be corrected if time regulations change.

If you're storing historical timestamps, UTC is fine because you can safely convert it to whatever time zone you want to display, knowing that changes to time zone / DST regulations tend not to affect the past.

> If you're storing future datetimes that semantically represent wall clock time, you need to store the locale time plus the full time zone (such as America/New_York) so that your program does the right thing in response to any common regulatory changes that happen after you store the value.

At this point in the process first normal form flies out the window. Trying to generalize too much can lead you down some weird garden paths. If it looks like you need a function to validate an prospective column value then you probably need to model the value as a relation corresponding to the function parameters. Then you can make it into a foreign key constraint and get on with your project.

I truly appreciate the efforts of those who attempt to expand the utility of datetime value representation to perfect a wider variety of denotational semantics. But with a relational model it may be better to delegate to simpler abstraction sufficient to the specific case.

But would using local time help in this case? For example, what if the future event is something like the solstice?

It's usecase dependent so it seems like the application should deal with this, at which point persisting in UTC is still a reasonable choice.

When you schedule an event in the future like “meet me at 14:00 in cafe foo”, this means local time and it’s going to mean 14:00 local time even when the timezone changes.

If you store this in UTC and the timezone changes, you end up converting to the wrong time.

So for future events you should store local time with a location you can map to a timezone.

The solstice is based on astronomical phenomena. If you store it in UTC it will always be right.

Unless there is a leap second scheduled on short notice or you install the updated tz database rather late. For astronomical events or long term measurements (think CERN) there is astronomical time, which does not add leap seconds.

But if you want to have precise date time far in the future you must not use UTC based milliseconds representations unless you want to check for necessary database migrations every time the time zone definitions change.

If you store zoned time in UTC then the zones or UTC change then the time can be formatted correctly in the future.

My point in mentioning the solstice is that while some events are at “2pm”, others are “when something happens”.

Sure, UTC is not suitable for conducting astronomy. It is suitable for working with human friendly times, since it is the foundation of timezones.

Yep! It sounds like overkill, but the suggestion I've seen that seemed to cover all bases was to store:

(1) The time in the local timezone (+ the timezone itself) (2) The UTC conversion (3) The version of the IANA database used to compute (2)

Then you can use (2) the UTC time for most computations/queries, but if a timezone changes then you can use (3) to tell which UTC dates need to be updated, and (1) to recompute the new correct version of (2).

That seems like overkill for most apps. Better to just store the date/time without timezone, and either the windows or linux timezone name of the location (which has far more specificity than the ISO 8601 offset). Then calculate the actual time on the fly when pulling it out of the DB.

No mucking around with IANA databases, everything's easy to program, server usually keeps itself up-to-date.

Personally I solve this problem by just doing everything in UTC. All my clocks, calendars, phones, everything read UTC.

"Always use UTC" works if you really, really always use UTC.

What about interfacing with the outside world, which doesn't always use UTC?

Translate. But frankly I often still use UTC to coordinate with people, especially when coordinating meetings with people in 3 timezones.

Always use UTC for timestamps.

Never use UTC for dates. Like OP says, a simple YYYYMMDD string is better.

This is wrong depending on the use case and where the user is and where the user might be going.

Always use UTC is not overrated. It is true, it may be overrated if you don't store time with your date or don't have a datetime type in your database (that makes me think WTF). You are always better using UTC to store dates if you have a datetime type.

This might be silly, but I kind of wish Date-with-Timezone types existed. It would be nice to be able to take a date and ask "Is does timestamp X fall within date Y", where Y is a calendar day in a particular place.

You don't need a zoned date. You can invert the question and ask if a zoned datetime falls within a plain date.

Temporal is very well designed thanks to building on years of prior art. Give it a try and you'll realize that it leads you to ask those questions the right way.

This is why i use microtime (epoch with miliseconds) over any database supported DateTime thingy. It is supported everywhere out of the box.

I've had this discussion with many confused developers.

Seems like everyone has to stub their own toe on this to get it.

I don't get why inventing some other ways of storing dates that are not RFC 3339 strings.

Multiple reasons. First, the DateTime type in a typical database isn't even an RFC-3339 string; its a unix timestamp. That's it; an unsigned integer. This is very space efficient; far more-so than a string. So, then, why the indirection; why not just use the integer type directly? It communicates to the database engine the intent of the data stored, so the database can do useful things like, for example, time-based queries, interfacing with that integer in more human friendly ways when writing queries, displaying it in a more friendly way... its a low-cost abstraction, generally.

Except, there is the cost intrinsic to this discussion, where going back to strings makes sense. That doesn't mean that decision comes with no cost. If you store someone's birthday as "1990-02-05", then need to answer the question "who has birthdays in the next month", that's actually essentially impossible to do in practically any database query engine. You have the problem of both ignoring the year field of that string (ok, so, maybe we just store "02-05" because the year doesn't matter for birthdays, moreso just 'dates of birth', we can denormalize)... but even beyond that, the closest most if not all engines could get is generating an array of every upcoming day in application code (02-05, 02-06, 02-07, etc) then matching on each. Alternatively, just sort on the field (sans year) then filter in application code, but that's also not happening in the database, which has negatives, and moreover, this would suddenly start failing across year boundaries, but you can special-case that by running a second query starting at 01-01 if the first one didn't return enough results...

There's no easy solution. Time is just really hard. Its good to have as many tools as possible in your belt, but with that you have to know when to reach for one versus another. There isn't one solution to any of these problems.

This. I have through much pain embraced this approach.

It's better to use a native date/time with a CHECK that ensures that the time portion is 0.

You'll have to do a CHECK on the YYYY-MM-DD string anyway to protect your database from bad data, so why not just do it on the native type?

The native type is likely to be more compact, performant, consistent across the database (with strings, some columns may be YYYY-MM-DD, some may be YYYY.MM.DD etc.) and more readily convertible to the client's type system.

The Temporal cookbook has more information on time arithmetic: https://tc39.es/proposal-temporal/docs/cookbook.html#arithme...

That looks very promising, thank you.

The Temporal proposal is dramatically better than moment.js (and Luxon, the better version of moment). It accurately models a much wider range of time based concepts, and draws on (mostly just copies) the last 30 years of developments in this space from other languages (mostly Java)

> But let us be honest, it has to compete with moment.js, not with Date().

Moment.js is deprecated: "Moment.js is a legacy project, now in maintenance mode. In most cases, you should choose a different library."


That's only half the story. It's "deprecated" because it's done. There's nothing more to add, fix or improve in the eyes of the maintainer. What a nice place to end up in:

_"We now generally consider Moment to be a legacy project in maintenance mode. It is not dead, but it is indeed done."_


You skipped the line right after this which completely changed the meaning:

> We recognize that many existing projects may continue to use Moment, but we would like to discourage Moment from being used in new projects going forward.

Fair enough, but the parent mentioned that without quoting this part. So I just posted the other half. Regardless, you’re right - new projects shouldn’t use it because of unknown future problems.

> There's nothing more to add, fix or improve in the eyes of the maintainer. What a nice place to end up in

What a strange thing to say. Baffling.

It's deprecated because programming standards have changed, the team agrees with those standards and nevertheless feels it would be too much work to incorporate them. Not "there's nothing more to add" but "we choose to prioritize stability over new features"

Wow! Next time someone says "no piece of software is ever finished" I'll know where to send them.

Note that there exists a spiritual successor to Moment.js in Luxon[1]. The creator was a Moment maintainer and it lives under the Moment project umbrella.

1: https://moment.github.io/luxon/

Also check out dayjs

I've been very happy with date-fns, picked it up after hearing good feedback from others: https://date-fns.org/

date-fns's insistence on using the native Date object makes handling timezones horrible and error-prone though. Luxon is much better in this regard.

I found its interface inconsistent and unintuitive, especially compared to moment.js and day.js. Still better than Date though. :)

True, but I think it will be some death of author kind of story.

Nevertheless the handling has to be at least on a similar level.

Moment is depreciated because all of its functions were moved to date-fns, which lets you import single functions.

The correct answer is that you should save it as 2021-06-28 with no time component. I get frustrated with KeyPass because it saves password expiration datetimes and not dates which is incredibly inconvenient when you go between timezones a lot.

> Temporal does not support parsing human-readable strings.

Well shit, that's one of the biggest things I want from a time library. There are about 15 different forms of human readable string formats that various APIs give you in their JSON responses and if they don't solve that, they haven't solved the biggest pain point.

I guess congratulations for not solving the problem and instead making Javascript even more complicated to use by introducing yet another incomplete date library that skirts around perhaps the most important feature and which millions of hippie JS devs will jump on the bandwagon to use.

If you’re parsing human readable strings to determine a datetime in your codebase you messed up somewhere. The user should select a datetime with a picker that uses a calendar and dropdown menus (no freeform text input). Everywhere from then on in your code you should pass it around using some standard, either as a UNIX timestamp or ISO8601.

Well no, the problem is I very frequently have to interface with APIs that give me human readable time strings only. Those APIs were not written by top quality engineers and I often have no other choice or way to interface with those systems.

Also you mention ISO-8601 but this document does not even describe how to input an ISO-8601 string.

…until you’re working on an app for servicing companies, shipping departments, etc. where on some forms nearly 1/2 of the input fields can be dates. Then your users hate you because they can’t just tab > type > tab to enter a date.

I agree with you on the rest though. And I would love to be able to enforce ISO 8601 for text entry of dates.

I use a calories counter app called MyNetDiary and I live in Sydney (UTC+10:00), if I want to change my calories budget I can only do it in the evening, or otherwise the change will apply to the previous day.

PS except from this one issue I'm a very happy customer, it's a great app :-)

> If any application only saves the date and not the time and you have dates like 2021-06-28T00:00:00 or something like that.

I literally just spent hours going around a code base fixing this exact problem.

Our back-end is a legacy, 20+ year old database where it was decided to save dates without a timezone.

console.log(new Date("2011-09-24"));


Fri Sep 23 2011 20:00:00 GMT-0400 (Eastern Daylight Time)

These dates were all "thought" to be EST, not UTC. Good times.

More fun examples here:


>Now it gets saved in a DB as 2021-06-27Z23:00:00

Why would you ever do this? Save it as a JSON date. coolDate.toJSON() would give a timezoned human parseable date. Just parse it back to the users locale with a built in Date constructor. You can do milliseconds UTC but someone like my younger stupid self would screw it up like most people.

If it's only a date, it probably should be days since the epoch.

But starting from which timezone?

Irrelevant for dates. Dates do not refer to a point on the global timeline.

Dates are set against calendar which in turn is set against hourly increments that are offset by local time zones. There is the infamous AoE or "Anywhere on Earth" time zone which can be used to say things like "we won't start until after the 16th Anywhere on Earth", meaning it will at least be the 17th in all timezones before the work gets started, but even in that case, you're referring to an implicit time zone.

What do you mean? In Auckland _right now_ it's Wednesday, 30th of June, while in Los Angeles it's Tuesday, 29th of June.

The epoch is a specific moment in all time zones. It didn't happen at one moment in Greenwich and then another moment twelve hours later in Auckland & Wellington. Any subsequent moment can be represented based on the amount of time that has passed since that moment.

No "which time zone" confusion necessary.

Thanks, I get it now :)

Thank you for restoring my faith that I can occasionally effectively communicate on social media!

JSON date is not a thing.

Sure, my bad. You know what I mean though.

I really actually don't. Dates in JSON are going to require you to pick a serialization form. ISO8601 seems like a totally reasonable pick. So if not that, then what? Could you give an example?

while developing my project I ignored what the officially named format was and simply checked what converted properly between three browsers.

Safari: "2021-06-28T22:59:18.366Z"

Chrome: "2021-06-28T23:01:09.131Z"

Firefox: "2021-06-28T23:00:14.385Z"

The three that I strive to support all utilize this format. YYYY-MM-DDTHH:mm:ss.sssZ. Looking on MDN this seems to call toISOString() which returns a simplified extended ISO format date.


> I ignored what the officially named format was

You ignored the officially named format and instead settled on the officially named format. Those are ISO8601 timestamps, specified in UTC time. (The Z at the end means there's (Z)ero timezone offset from UTC). If you're going to talk about a standard, its worth knowing its name.

I don't care what the name of something is so long as it does what I want and has a form that I can understand and utilize.

Those are timestamps, not dates.

Each of them fall on two separate dates, depending on time zone.

This is why dates are such an issue.

A timestamp is the number of seconds or milliseconds or units like 1624938650 seconds from epoch UTC. What I posted above are human parseable datetimes in ISO 8601. Each of them is a point in time that depending on the locale and tz fall on two separate dates but the most important aspect is that it doesn't matter whether I'm using them in my DB or on a user's machine in a different locale and tz. That's the point.

I don't think we disagree?

That works great when every interaction you make with the data is in JS. But less so if you want to, say, run manual SQL queries from time to time.

They're sortable. I don't see what the issue is. Can you explain?

Maybe a better choice would be to use a DateTimeOffset.

The real issue is that time is actually a complicated subject that everyone assumes is easy because it's so ubiquitous.

Dates were invented in case anyone thought programming was fun.

The user doesn't live in UTC+1-land. He lives in "Europe/London" or "America/Los_Angeles", because this way DST is also considered.

I've settled with storing Zulu time and in a separate field the timezone offset of the event in seconds.

I used to work on a web application where showing correct time stamps was extremely important, so I set my system-wide time zone to Mumbai so that TZ-related bugs would be really obvious in testing

The original mistake you did was let someone else handle the timezone conversion. You should probably always default to storing UTC-normalized timezoneless datetimes (there's of course a number of exceptions, but generally "an instant" in the past should be translated into UTC before being stored into the database... with future dates/times, it's more ambiguous, but in any case, you should be doing the TZ conversion yourself).

Yes, this exactly. Use timezones when displaying something to a user and otherwise not at all, imho.

This falls down if the timezones themselves change. e.g. if DST was planned, but was then cancelled. You really want to store the original time with timezone as well as UTC.

Tangent: moment.js has been replaced by date-fns (and dayjs) as the modern alt to native Date().

I had better luck with date-fns than with moment.js, from Chile perspective, better daylight saving time handling.

Working with datetimes on JS for the first time was mind boggling. Few highlights:

- Months start at 0

- Converting from string datetime to a datetime object will automatically convert the time to the local tz

The months-start-at-0 is a legacy of POSIX's datetime API, as is the year field being a count from 1900. Java continued these mistakes, and then added the everything-defaults-to-local-tz functionality (POSIX had different functions for UTC versus local tz), and JS copied Java's API in this respect without modification.

Java eventually torched its date/time API not once, but twice: the JDK 1.1 addition of Calendar that deprecated most of the naïve methods on Date, and JDK 8 adding java.time.*, which is roughly the modern model of date/time APIs.

I don't know where POSIX stands on this, but I think the real WTF of JavaScript's months starting at 0 is that the days start at 1. Pick one!

POSIX does the same thing.

ETA: Ahaha, I managed to read "days" as "weekdays". The following is now. permanent marker of my reading incomprehension.

IIRC (and it's been a while, so I may well not RC), specifies that 1 is Monday, 2 is Tuesday, ..., 6 is Saturday and both 0 and 7 are Sunday. And the reason that looks weird is that there is (IIUC) a genuine difference between western nations as to if a week starts o Sunday or Monday.

Which, you know, sucks. But, also makes it somewhat easy to make a C array that you can index with a (.tm_wday%7) to easily get the day of the week.

Which may also be the reason why months start at 0.

I think months from 0 in POSIX were intentional, not mistakes. The things you might want to do with an integer month number include:

1. Look up some attribute or property of the month in a table (number of days in the month in a non-leap year, name of the month, list of fixed holidays in the month, etc).

2. Determine if the month is before or after a different month.

3. Determine how many months one month is after or before another month.

4. Determine which month is N months before/after a given month.

5. Print the month number for a human.

In languages that use 0-based indexing:

#1 is more convenient with 0-based months.

#2-4 only need that the month numbers form an arithmetic progression, so are indifferent to where we start counting.

#5 is more convenient with 1-based months.

So it comes down to is it better to have your low level date functions require that you have to remember to subtract 1 whenever you do something from #1, or to add 1 whenever you do something from #5?

I'd expect most programs do a lot more #1 than #5, so the code is going to be cleaner if you go with 0-based months.

In Javascript's Date API, the month number is 0-based (January is 0), but the day number is 1-based (the first day of January is 1, not 0). That inconsistency is unexpected and makes it harder to use.

I'm glad most modern date APIs use 1-based numbers to represent the month and the day (not to mention the year from 1 AD onwards).

> 1. Look up some attribute or property of the month in a table (number of days in the month in a non-leap year, name of the month, list of fixed holidays in the month, etc).

The first two cases basically exist internally within the date-time library, so it's not a particularly common example for end users to be doing. It's also not particularly hard to do #1 with 1-based months, because you can either subtract one before indexing, or you can just have entry 0 be a null-ish value.

The thing is, month names already have an implicit numerical mapping: ask anyone, even most programmers, what number to assign to the month of January, and you're likely to get 1. So an integer representing a month name is going to be presumed to be 1-based unless documented otherwise, and having it return 0 instead is going to confuse more programmers than not.

In other words, the trade-off isn't "is the code going to be cleaner for this case or that case" but rather "do we make an API that is going to confuse everyone's first impression of it, or do we make the code marginally more complex in one specific scenario?"

> The thing is, month names already have an implicit numerical mapping: ask anyone, even most programmers, what number to assign to the month of January, and you're likely to get 1.

Couldn't you make a similar argument then that when representing English text, 'A' should be 1? That's the number most people would give when asked to assign numbers to the alphabet. Not many people are going to say 65 which is 'A' in ASCII and Unicode.

Programs are not people.

I generally prefer, and believe it leads to less bugs, to represent things in programs in the ways that best fit the operations that the program will do doing on them, translating between that representation and external representations upon input and output.

> Couldn't you make a similar argument then that when representing English text, 'A' should be 1?

It actually is!

  >>> hex(ord('A'))
  >>> hex(ord('a'))
The five-bit intuitive numerical mapping of the letter is prefixed with the bits 10 (for uppercase) or 11 (for lowercase). A similar thing happens with digits, where the four-bit intuitive numerical mapping of the digit is prefixed with the bits 011.

For letters, this leads to a "hole" at 0x40 and 0x60. Instead of making the letters zero-based (that is, 'A' being 0x40 and 'a' being 0x60), they decided to keep the intuitive mapping (which starts at 1), and fill the "hole" with a symbol.

> I think months from 0 in POSIX were intentional, not mistakes

These are absolutely not exclusive. 0indexing months was probably intentional, and was without a doubt a mistake.

#5 does not seem like something you should be writing anyway, there ought to be a standard library mechanism for that. So even if you do that often you might not need to care about it.

I think it's basically tradition at this point that languages make a hash of their date/time abstractions, maybe getting them right after a half dozen iterations.

> Converting from string datetime to a datetime object will automatically convert the time to the local tz

This happens in C# too:

Outputs 5 instead of 12 (for me, US Pacific) due to a default conversion to local time. It does have an opt-in flag to "adjust to UTC" though.

Not intuitive at all, at least to me...

[EDIT] Realised this is the behavior I would expect. Parse is doing what I would expect it to, taking into account the given time being UTC. It's then returning an object that's in the local timezone. Still goes to show just how confusing this datetime stuff can be when even the expected behavior looks wrong.

Wait… how is this not a bug according to both Microsoft's own spec[0] and ISO 8601[1]? The Z specifically means this time is in UTC.

The behavior is not at all what I would expect from reading the docs [0]:

> A string that includes time zone information and conforms to ISO 8601. In the following examples, the first string designates Coordinated Universal Time (UTC), and the second designates the time in a time zone that's seven hours earlier than UTC:

> "2008-11-01T19:35:00.0000000Z"

> "2008-11-01T19:35:00.0000000-07:00"

[EDIT] I get it now: it's parsing it right, it's just that it's then putting it into a datetime object that's in the local timezone – which is what I would expect. The alternative would be counterintuitive to me.

[0] https://docs.microsoft.com/en-us/dotnet/api/system.datetime....

[1] https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal...

That is describing the different formats it will accept.

Further down the page:

> If s contains time zone information, this method returns a DateTime value whose Kind property is DateTimeKind.Local and converts the date and time in s to local time. Otherwise, it performs no time zone conversion and returns a DateTime value whose Kind property is DateTimeKind.Unspecified.

Yeah, I confused myself and put an edit in there. I first thought it was just ignoring the Z then giving out a UTC object. Now I realize it was doing what I expected it to: parse correctly and hand out a local time.

Another fun one:

    > new Date().getYear()
Of course this is avoided by using getFullYear(), but I've always wondered why a language that came out in 1995 had a function that returned two-digit years.

That is absolutely baffling, I cannot think why that was ever implemented

Date.parse returning a number always trips me up.

> - Converting from string datetime to a datetime object will automatically convert the time to the local tz

I think this makes sense in the context of client side javascript whose sole point is to present UI to the user. The dates aren't necessarily converted to local TZ (the date will always remain in the same and fixed point in time), but rather any output is local to the timezone.

I think this is more good than bad IMHO, given the intention of JS is to build (or augment) UIs. It's just not great that it's hard to do anything other than that.

"January is month 1"

This is what we were all waiting for.

What is Monday and Sunday?

edit: The dayOfWeek read-only property gives the weekday number that the date falls on. For the ISO 8601 calendar, the weekday number is defined as in the ISO 8601 standard: a value between 1 and 7, inclusive, with Monday being 1, and Sunday 7. For an overview, see ISO 8601 on Wikipedia.

They had me at “all objects are immutable.”

They lost me at “ date time values without time zones.”

I’m mostly kidding with the second line. Working with third-party React calendar components requires doing some moment.js math and then hacking off the last few characters of the .toISOString() to make the calendar happy. So I can see how it’s a very practical addition. Please don’t misuse it.

They lost me at “ date time values without time zones.”

This is rather useful for date of birth, which shouldn't be set to a time zone in most use cases (it can get rather confusing if you do).

Every DOB field I’ve seen is just a date, with no timestamp at all.

There’s a footgun when you convert DOBs to Date objects. This can return different dates depending on the local timezone:

``` new Date("1/1/2021").toLocaleDateString() ```

If you want it to return the same DOB regardless of local timezone, you have to pass a timezone to the Date constructor, which is not super obvious. This kind of bug is also unlikely to get caught by unit tests unless you’re testing date formatting for different timezones, which you probably wouldn’t do if you’re not aware of the footgun to begin with.

Were we? Would have made more sense to change the day-of-month to be zero based too IMO. Or explicitly have both: `monthOne` and `monthZero`.

ISO 8601 says January is 1. We have a standard.

ISO 8601 is a serialisation format. What's your point?

What is "January" -> "1" if not serialization?

Point is: there is a standard. Use it.

Are you suggesting JS printed "ISO 8601" strings with 0-based months before now?

Do people really not know the difference between internal representation and serialisation? Are we even speaking the same language? Truly baffled by this.

Why would you want that? It is super confusing since 1~first as well as every real life date representation I know has 1=January

Because it only needs to be "1" when you format it for human consumption. 0-based is much easier to work with internally.

No it doesn’t. Dates aren’t meant to be worked with as raw numbers, so the utility of 0-based numbers is gone and now you only have February ending on day number 27

If people don't work with them as numbers then the argument is completely moot. All you need is timestamps, deltas and formatting functions.

How many days is in the delta 1 hour? 1? or 0?

The whole reason months start at zero is because somebody wanted to work with them as raw numbers.

Yes and they were wrong. There’s a whole “misconceptions programmers have about dates” you might want to read. The only number for dates is the Unix timestamp.

So if I want to have a style per month or something in my program, I'm wrong? If I want to select the next month's style I have to do something awkward like `(current mod 12) + 1` instead of the more natural `(current + 1) mod 12`?

Then why do days not start from 0. If that argument is value it should be applied consistently so 2000-01-01 is day=0, month=0.

0-based months were meant to assist with lookups in 0-based arrays.

Probably because the ancients didn't have a strong grasp on the number 0.

Midnight is zero. What's confusing about the number of days starting from 0 too?

Why do we use months? No sane internal representation stores the number of months. They are completely and utterly useless and exist only in printed representations of dates.

Props to the Mayans, whose dates are (almost) a base-20 number counting from the Mayan date of creation. It's not fully base 20, because the 2nd digit only counts to 18 because 360 is close enough to a year. So is followed by instead of Bonus points: no leap years to worry about!

The downside is that their full calendar is somewhat more complicated, so they would say that today is (checks) 8 Chuwin 9 Sek, and tomorrow is 9 Eb' 10 Sek (that Tzolkin calendar component doesn't work like our months do!).

I was meaning in the context of JavaScript, where the first day is 1 and the first month is 0.

A case can be made for either 0 (for consistency with time representation) or 1 (to go with the defacto standard of how most if us bipedal meatsacks process dates) but there must have been alcohol or something stronger involved when the current date object came into being.

Erm... nobody was arguing for it being the current way. That's retarded. This whole thread is about making them both 0-based rather than both 1-based.

> 0-based is much easier to work with internally.

Why's that?

Even if it was I'd still want days and months to start at one and, you know, match everything else in existence that represents them.

Mainly because you can use normal modulo arithmetic and use them as array indices.

But as I mentioned in another comment, really you should be touching the internals of the datetime object at all. It's a mistake to do something like `myDatetime.day += 1`. You should instead do `myDatetime + timeDelta(days=1)` or something.

So it shouldn't matter whether it's 0-based or 1-based at all. But since it apparently does (I don't know why), then it might as well by 0-based like everything else.

It is disappointing to see GMT conflated with UTC. It is not "UTC plus zero hours". GMT is based on solar time, wheras UTC is based on TAI. UTC is TAI corrected (with leap seconds) to match GMT. Hopefully I have got that right.

GMT is not (officially) used anymore. UT1 is based on the Earth's rotation within the ICRF (almost J2000) reference frame based on the positions of distant quasars. UT1 is not a steady time frame, e.g. if you measure UT1 at two times one second apart, it probably will differ by something other than one second. This is due to the fact that the Earth's rotation rate is constantly changing.

UTC is what people normally mean when they refer to GMT or Universal Time. It has a steady definition for a second based on TAI, and is adjusted by adding leap seconds to keep it within .9 seconds of UT1.

Every day the International Earth Rotation Service publishes values for UT1-UTC based on actual observations as well as other Earth Orientation Parameters. They are also responsible for determining and announcing in advance of when a leap second will be added.

E.g.: https://www.iers.org/IERS/EN/DataProducts/EarthOrientationDa... See "finals.all IAU2000"

While all that is super cool and good to know, does it change or affect at all how js dev or their app interacts with date or time since they are for all practical purposes synonymous?

One huge difference between them is that UTC is, well, universally used, while GMT is switched out for BST (an hour ahead) in summer months on client machines. Not that you should be assuming anything about the state of the client machine but this is an extra footgun.

Probably not, but there's no reason to perpetuate the confusion of the two. I'd think the only practical situation where it'd be a problem is if you enter GMT into software that actually interprets it as GMT rather than UTC.

For any readers who, like me, weren't sure of the differences (or what "TAI" refers to), this page helped clear things up for me: https://www.nist.gov/pml/time-and-frequency-division/nist-ti...

Thank you. I really don’t understand people who use rare acronyms on a global forum and don’t write what it is.

Thank you for the link

I am afraid that’s wrong. What you are calling GMT is actually called UT1.

GMT does not exist as a separate thing any more. It was the timescale maintained by the Greenwich observatory, but their timescale matched UTC since the rubber seconds era in the 1960s, and in 1972 Greenwich time became UTC with leap seconds. The Greenwich observatory as an institution does not exist any more (there’s a museum where it used to be but it isn’t a working national scientific laboratory). GMT is now an informal synonym for UTC that still appears in things like British law for historical reasons.

Will the two ever provide different values?

> UTC is based on TAI

What is TAI?

TAI is International Atomic Time. It is used as the reference point for UTC, which is defined as TAI minus a fixed number of seconds (currently 37). To add or remove a leap second, the TAI - UTC offset is adjusted. Leap seconds have no effect on TAI itself, which always runs at 1 second per SI second, as measured by a consensus of atomic clocks.

If UTC is corrected to match GMT: what's the difference, aside from conceptual?

My understanding is it's corrected with whole leap seconds. So they could differ by a fraction of a second.

GMT is different from UTC during British Summer Time (UTC+01:00)

That's incorrect. GMT is always the same as UTC for all intents and purposes (setting aside things like leap seconds). British Summer Time is GMT+01 (which is the same as UTC+01). The time zone in England is Europe/London, which uses GMT in the winter, and GMT+01 in the summer.

Java 8 Date/Time API is surprisingly nice. I think that every language should just copy it. It's comprehensive and easy to use.

Java 8's time package is a sort of second version of Joda-Time by Stephen Colebourne, who is not very impressed by the new JavaScript API:


He has opened a few issues along the way, trying to help TC39 learn from Joda's experience, with varying results:


Damn, he says [1]:

> Sorry to be blunt, but the current approach really is a terrible one.

That is blunt, and would require my utmost restraint to avoid falling into the trap of getting overly defensive if I were the proposal authors. Which is a shame, because he makes a good point.

That said, it is also reassuring to see that they did actually consider his point, and I can follow their motivation [2]. I'm not sure whether I'd have made the same decision, but it's certainly a valid one to make.

[1] https://github.com/tc39/proposal-temporal/issues/1454

[2] https://tc39.es/proposal-temporal/docs/calendar-review#why-a...

Oh, so the embedded calendar system in Joda-Time is a mistake. I too disliked that choice and wondered if it only exists for localization uses (but then I'd like to have a separate set of "display" classes), and well, he knew (or came to know) it. I'm relieved now.

Hard won lessons have to be relearned over and over in software.

And those of us who used Joda-Time were so pleased when it became almost a "built-in" in Java 8.

They already have that for JS!


Careful, Oracle is likely to sue you for copying their API.

Yeah is every language going to have a big date/time API. Temporal looks good but its big.

I always use java.time.* for doing any sort of date arithmetic like how many days are between two days or when is 20 days after some date. jshell makes it really easy to use, just do "import java.time.*".

Fyi comparison of java.time to Temporal https://widdindustries.com/ecma-temporal-vs-java-time/

I've been looking at the calendar and date types for BACnet, which is a protocol used in building automation (and hence, scheduling is super-important) - one of the things that it's got is some nice support for repeating dates/date patterns, sort of like cron but with odd extensions. (Because you might want to run a fan or purge a storage tank on specific days)

For example, month is a number between 1 and 14, where 1 through 12 are Jan-Dec, but a month of '13' means "Odd Months" and month '14' means 'Even Months', and there's an explicit 'Not specified' for things you might want to run every month.

Similarly there are special values for 'Even Days' and 'Odd Days', and special values for Week of Month: 1-7(1), 8-14(2), 15-21(3), 22-28(4), 29-31(5), 'Last 7 Days of the month'(6), and 'Unspecified'(255)

I like that there's nice compact and standard ways to express and serialize these common choices. I wish there was something like that included in temporal but it doesn't look like there is?

mandatory link: https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b...

Falsehoods programmers believe about time

The stable polyfill (and eventual native version) can't come soon enough. We're looking to move off of Moment given its deprecation status and how much it bloats our bundle, but we're waiting so we can go straight to Temporal instead of another intermediate library

This plus Safari desktop support for html5 date and datetime inputs will mean that soon date input and handling will be a lot less of a pain in the ass. I'm jealous of the young devs who will never even know how stupid js dates used to be.

My team just implemented the native date inputs for a client's site this past week. With native date inputs working for every good browser but safari desktop (it's in technology preview), we saved lots of time and effort with minimal styling of the native date input. Plus, it works soooo much better on iOS, the interface for date inputs is great. I assume it's similar on Android these days.

> every good browser but safari desktop (it's in technology preview)

It’s finally in the mainline release now as of version 14.1!

Yes! I can't wait until enough people are using the version of Safari that has html5 date input support. We have a big dumb custom date input that we can just completely throw away at that point and delegate to the browser.

It's not even on caniuse.com or kangax.com's browser compatibility tables. I think it'll be a while before we can use this in production.

It's in Stage 3. When it hits Stage 4 the API will be locked in and there should be a stable polyfill available. It may be a while before you can drop the polyfill, but it shouldn't be too long until you can use it

Except for this warning, unfortunately:

> Although Temporal is currently Stage 3 in order to gather feedback from implementers, implementers must ship Temporal support behind a feature flag until we finish work with IETF to standardize the string formats used for calendar and time zone annotations


Presumably this will be resolved before stage 4, no?

I think it's the other way around: it won't reach stage 4 until that is resolved.

> Temporal does not support parsing human-readable strings.

Well shit, that's one of the biggest things I want from a time library. There are about 15 different forms of human readable string formats that various APIs give you in their JSON responses and if they don't solve that, they haven't solved the biggest pain point.

I guess congratulations for not solving the problem and instead making Javascript even more complicated to use by introducing yet another date library that skirts around perhaps the most important feature.

Parsing human-readable strings is application-specific. It's good that this is kept in userland. Not matter how they do it, it's not going to work for at least 50% of use-cases. It would result in additional bloat of Temporal that's probably useless to you anyway. How do you parse (and do you parse) “01. 02. 2003.”, “jan 2 2003”, “2nd jan '03”, “today”, and “tomorrow night”? The API should parse only the ISO standard(s).

I was equally disappointed to see this feature absent.

Mandatory XKCD. One last time? https://xkcd.com/927/

“ “Time Difference” (“Zeitverschiebung”) lists time zones for cities etc. – for example, the time zone for San Francisco is America/Los_Angeles.”

Does anyone know how the canonical cities/regions associated with a time zone were determined? It seems kind of arbitrary. Wouldn’t “America/California” or “America/Sacramento, CA” make more sense?

The full theory on how the names are selected is here: https://www.ietf.org/timezones/tzdb-2021a/theory.html#naming

For most zones, it's the largest city in a region where the timekeeping has been the same since 1970. This can cross state or national boundaries.

There is also US/Pacific, which is just an alias for America/Los_Angeles. America/California is ambiguous, because there are several Californias in the "America" area, which encompasses North and South America.


> Country names are not used in this scheme, primarily because they would not be robust, owing to frequent political and boundary changes. The names of large cities tend to be more permanent...

> Usually the most populous city in a region is chosen to represent the entire time zone, although another city may be selected if it are more widely known, and another location, including a location other than a city, may be used if it results in a less ambiguous name.

Basically: globally, regions and countries change, but cities are much slower to change. They don't just pick up and move like borders can.

AFAIK the common case is that it's just "whatever is in ICU/CLDR", there is no "standard". And I've seen them renamed many times - OS versions don't always agree with each other, for example, much less different OSes.

I've been using Temporal in production for a few months now. I really enjoy using it. Use it mostly for creating ISO date strings and translating between timezones. I can't wait for it to make it through the TC39 process.

What are you going to do if there’s a breaking change to the API between now and when the native version is released?

Why do you care?

If we are going to nanosecond precision, I'm a little surprised Temporal doesn't include a `TAIInstant`? Maintaining a leap-second table (and the conversion logic) sucks for the same reason that maintaining your own copy of tzdata sucks. It really wouldn't need any methods other than a (possibly lossy) conversion to or from the corresponding UTC `Instant`.

Where are you going to get TAI from without a leap second table?

Does it strike anyone else how inelegant this new API is? With the plethora of accepted date/time packages out there with wide usage, this is the most cumbersome API surface of them all.

The name for the variable is kind of weird, it feels a bit like Java (having 25 years of history to account for). I wonder what the Kotlin for JS will be.

That was coffeescript, but it's come and gone already.

I'd put coffeescript as the "Groovy" of JavaScript.

Kotlin is more like a mixture of Typescript and/or Reason (or whatever the name is now)

Groovy was that flash-in-the pan idea that had some really cool ideas.

But then again, I'm not sure if it really inspired any real language changes to Java like Kotlin has. CoffeeScript did a TON of inspiration for ES6... so heck, maybe Kotlin IS the CoffeeScript

I hope Kotlin survives though.

various replies mention java.time and the similarity Temporal has to it, so I'm posting a comparison I did a while back: https://widdindustries.com/ecma-temporal-vs-java-time/

Looks like JS-Joda, I like it

Applications are open for YC Winter 2024

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