new Date
Considered Harmful
While I don’t know that I’d consider myself a “professional” Javascript
developer, I do find myself needing to dive into some Javascript at work. One
curiously recurring bug I see is misuse of the Date
constructor. Can you spot
the bug in the following code?
const date = new Date("2023-01-10");
console.log(date.toDateString());
Depending on where you’re reading this post, this might seem to work
fine. However, you also might find that it’s off by one day. Indeed, outputs
"Mon Jan 09 2023"
for me right now in US Eastern Time.
What?
Looking at MDN, you’d be forgiven if you missed this little comment
Date-only strings (e.g. “1970-01-01”) are treated as UTC, while date-time strings (e.g. “1970-01-01T12:00”) are treated as local. You are therefore also advised to make sure the input format is consistent between the two types.
new Date
(and Date.parse
) actually treat the given date as a UTC date! I
live on the east coast of the US, so upon construction of this object, this is
actually parsed as January 10th, 2023 at 12:00 AM UTC. If you call .toString()
on the date object above, it becomes a bit clearer what’s going on.
> date.toString()
'Mon Jan 09 2023 19:00:00 GMT-0500 (Eastern Standard Time)'
We can actually see that the time zone offset is marking this as the prior day.
Frustratingly, it actually parses the date as local if you pass the string
"2023/01/10"
. However, I’ve never seen an API or database return a value like
this; it’s always in the ISO YYYY-MM-DD
format.
Obviously, this can lead to frustratingly subtle bugs if you’re not paying attention. Worse, some date picker libraries will return you a date in the local time zone, so this may even display fine to a user, but when you submit your data to the backend as a date string, you’ll store the wrong date.
What can we do?
Honestly, my opinion is that we should avoid using Date
objects as much as
possible. While it’s certainly possible to use it correctly, doing so is
something that requires too much care to be viable. Personally, I like
luxon
, but there are plenty of others.
> luxon.DateTime.fromISO("2023-01-10").toISODate()
'2023-01-10'
Of course, as with anything in software, there are no silver bullets. I do think many developers need to think carefully when dealing with dates or times. They’re so much less simple than meets the eye, and it’s far too easy to fall into time zone based traps without realizing it 1. Another key part of this is that our date libraries are just a little smarter than we think; even if you’re only working with “simple” dates (no time or time zone information). Sometimes we use these tools to simply format dates, and end up shooting ourselves in the foot with all the extra power they bring us.
-
One passing thought I have is I wonder if it may be worth making time zones part of our types in statically typed languages, and forbid comparisons between times in different zones, even if they’re technically possible. ↩︎