Date and time handling is arguably one of the most complicated things in computer science. It seems simple, since everybody knows how to read a clock or how to use a calendar. But the devil is in the details, as always. Since so many people seem to get it wrong over and over, we should talk about it.
In this blog post I want to offer a mental model to you. It created this famous click moment for me. Maybe it helps you too. Let me know, I'd be delighted.
Terminology
Let's give clearly defined names to the things we'll talk about. The topic is already complicated enough without confusion induced by the ambiguous nature of human language.
- A
Date
is the information you can find on a physical calendar. It's a day, month and year. - A
Time
is the information you can find on a wall clock. It's an hour, minute and second. You can of course go finer by giving milliseconds etc. - A
DateTime
is aDate
and aTime
. - An
Instant
is a point in time on the planet. If twoInstant
s are the same, this means that they happened at the same moment, physically speaking. - A
Timezone
is an algorithm to convert anInstant
to aDateTime
.
The key takeaway
A DateTime
is NOT an Instant
.
If you take one thing home today, it should be this. A DateTime
is NOT an Instant
. Neither can you convert between the two losslessly. They are fundamentally different concepts. In my opinion the confusion between the two is the cause of most bugs in date and time handling.
The mental model I'm offering is that these are different things that can't be converted without loss of information. You need to ask yourself every time which of the two you're working with and you can't mix and match them.
Let's use an example to show the difference. Say you're speaking on the phone with a friend who lives on the other side of the planet. Where you live it's maybe the evening, while your friend's about to start their day. Your DateTime
is very different from your friend's DateTime
. Yet, you're having a conversation at the same Instant
in time. Of course, because you're saying something and your friend responds immediately (ignoring the slight delay imposed by the speed of light).
So let's repeat this again. A DateTime
is NOT an Instant
.
Timezones
Let's take the example further. Say you didn't have your conversation yet. Since you and your friend are both busy, you schedule it. Easy, you agree on an Instant
in the future and be done with it. While it's true that you must agree on an Instant
, it's unfortunately also true that people don't work on Instant
s. They work on DateTime
s. How impractical. And since your friend lives far away, the DateTime
s for the Instant
of your call won't be the same.
You need to convert the Instant
to a DateTime
for each one of you. The algorithm to do so is called a Timezone
. I'm saying algorithm, because Timezone
s are more than just a simple offset. There are complications like daylight saving time, leap seconds, and historical changes in time zones. I'd consider them black box algorithms, because they're complex and there's no shortcut around this fact.
Say you're living in France. Your Timezone
is called "Central European Time" (CET
) or Europe/Paris
. Your friend lives in India. Their Timezone
is called "Indian Standard Time" (IST
) or Asia/Kolkata
. You both agree on an Instant
in the future. Then, you need to apply the respective Timezone
s to the same Instant
to get your DateTime
s. I've used India as an example, because its Timezone
doesn't experience daylight saving time. In France, however, it does. So the way to calculate the DateTime
s in each case are very different.
Universal Time Code (UTC)
The keyword "UTC" comes up a lot in discussions around time management. It is the name of a Timezone
called "Universal Time Code". It is very often used to communicate Instant
s between actors in different Timezone
s. It has a special property that makes it very useful for storing Instant
s. It doesn't experience daylight saving time. This means that the conversion between Instant
s and DateTime
s becomes reversible. Or in other words: A DateTime
in UTC
is equivalent to an Instant
. You can convert between the two without loss of information.
Storage
So how do you store Instant
, DateTime
and Timezone
?
Instant
s can be stored as offset from a known Instant
(epoch) in seconds. This is also called a timestamp. A common choice of the epoch is the Instant
that corresponds to the DateTime
Jan 1st 1970 at midnight in the Timezone
UTC. The offset can be in seconds, milliseconds, etc. You can choose the granularity. This storage method is space efficient, since you only need to store one number.
DateTime
s can be stored as strings or as Instant
in UTC
. There are several formats for the string representation. The most common format is the ISO 8601 format. It looks like this: 2023-03-15T12:34:56
. This corresponds to March 15th, 2023 at 12:34:56. The Timezone
is not specified in this example.
The second storage method is to store DateTime
s as Instant
s in UTC
. As already discussed, Instant
s in UTC
map uniquely to DateTime
s and vice-versa. This storage method is space efficient, since you only need to store one number. However, it is important to note that it is easy to make mistakes when using this method. You have to be very careful with your conversions.
Timezone
values are usually stored as strings. There are standardized names for timezones. You should use these names. They are supported by libraries implementing Timezone
.
DateTime
or Instant
?
Let's do some examples of when to use which for your information.
The time information of a log entry in a computer system: Instant
, because it happens at a specific point in time.
Showing an Instant
to a user: DateTime
(obtained by applying the users Timezone
).
A users birthdate: Date
/ DateTime
, because that's what's written on their official documents.
The timing information of a one-off calendar event in a shared calendar: Instant
, because it happens at a specific point in time. The Timezone
of one user does not influence the Instant
.
The timing information of a recurring calendar event. That's a tricky one. Think of every Monday at 2AM. What happens if the user changes their timezone? What happens on the day the clocks are reset for daylight saving time? Does the event happen twice? In my opinion there's no absolute answer here. It depends on how you want your system to behave. I'd store a DateTime
+ Timezone
. I'd add code to handle the edge cases of the Timezone
(using a library of course). And then I'd start praying.
The JS Date
class
So what does the JS Date
class represent? It's JS, so let's not be surprised by the answer: an Instant
. Yes, that's the hard truth. And I want to be very clear: There is no Timezone
information stored in the Date
class. No UTC
. It's an Instant
. End of story.
But why does it have getters and setters for years, months, days, hours, minutes, and seconds? Because it's badly designed. And what do these return? They convert the Instant
to a DateTime
in the local Timezone
of your user (frontend) or your server (backend). And then they extract the year, month, etc. and return them as numbers.
And here's another hard truth: Your users Timezone
might be anything. You can't assume that:
- it's correct.
- it does / doesn't experience daylight saving time
- its offset to
UTC
is positive / negative - it won't change
You need to code with this in mind.
I think the JS Date
class should be avoided for anything related to DateTime
. Instead, use one of the established libraries: Moment.js or Day.js. And in the near future, use the successor of Date
, the new Temporal API.
Top comments (1)
Don't use
Moment JS
as stated on their homepage (momentjs.com/) or here (momentjs.com/docs/#/-project-status/).