I decided to launch TimeTime.in because, contrary to popular belief, working with dates is still terribly complicated for developers. (It's no coincidence that the Datepicker is often paid in many UI libraries).
Not long ago, I was on Twitter and came across a post by Ben Nadel, a developer not suspected of being a junior, discussing a problem he faced with dates and linking to a post explaining his solution.
To my surprise, his solution isn't entirely correct, so I was inspired to write this post to prevent more developers from making the same mistake.
First things first!
Not wanting to make this post too long, I'll start by introducing the basic concepts required to understand it.
Civil Time vs UTC
As many people know, on the Internet most dates are represented using UTC time (actually UNIX time is used, which isn't UTC and has significant issues if we want to be serious, but that's material for another post). Simplifying, we can say that UTC time represents the number of seconds from a reference time.
Practically, instead of continuously talking in seconds, humans use a different system to represent dates called civil time. We don't say “I was born at 89891283”, we say “I was born on July 4, 1992, in Pontevedra, Spain”.
The crucial point here is to understand this difference. Humans speak in civil time and machines in standardized time, and if we want a functioning computer system, we need to effectively transform one format into the other.
Time Zones, Area Zones, and GMT Offset
In simple terms, the 1884 meridian conference was held where it was agreed to divide the earth into 24 time zones, each separated by 15º.
Time zones are different from area zones. An area zone is a geographical region that shares the same official time. It's essential to understand that an area zone is a civil time concept whose rules can change arbitrarily.
Lastly, we should discuss the GMT offset. The GMT offset is the difference in hours and minutes between the reference time (GMT) and a specific time.
(People often talk about the “UTC offset”, even though it's important to remember that GMT is a time zone, whereas UTC is a time standard.)
ISO 8601
It's a standard regulation specifying the notation to represent dates, times, and more. This standard indicates that the format for representing a date with hours, minutes, seconds, and milliseconds is:
yyyy-mm-ddThh:mm:ss.mmm
Furthermore, this format allows specifying time using the local time zone, adding how much offset there is from GMT. If the offset is 0, a Z is added at the end.
yyyy-mm-ddThh:mm:ss.mmmZ
Meaning, the same moment in time (UTC) in Spain will be called "26/07/2023 15:51:19 in Spain" and in Portugal, which is in a prior time zone, it'll be called "26/07/2023 14:51:19 in Portugal".
ISO | GMT | UTC (ms) | Civil Time |
---|---|---|---|
2023-07-26T13:51:19.724Z | 2023-07-26T13:51:19.724Z | 1690379479724 | 26/07/2023 15:51:19 in Spain |
2023-07-26T14:51:19.724-01:00 | 2023-07-26T13:51:19.724Z | 1690379479724 | 26/07/2023 14:51:19 in Portugal |
Developers
Now that we've covered this, what implications does it have for us as developers?
Level 0: Timestamps
Many people use timestamps to represent dates. As we've seen, timestamps are an absolute time concept that doesn't account for the offset or time zone.
This is the problem Ben faced and explained on his blog. He has a sports app where he saves training data. It has a “training streak” feature which essentially shows which days the user trained.
Initially, he saved workouts as timestamps. The problem is for a user in the Eastern US, which is in GMT-4 offset. This means if they train at 5 in the morning and 10 at night, they've trained on two different days in GMT time, but the same day in GMT-4.
Examples like this show that saving timestamps is often inadequate if we want to preserve date semantics.
Level 1: GMT Offset
A common solution is to store dates, including the GMT offset. This way, knowing a user's offset, we know whether to classify workouts on different days or not. However, this approach has one problem: Assuming that time zones are static.
As previously discussed, a time zone differs from a GMT offset. The classic example we all know is the switch between daylight saving time and standard time. Moreover, time zones themselves can change due to political decisions. For instance, Spain changed its time zone in 1940.
Level 2: ISO + Time Zone
Without delving into many tedious details, I can confidently say that the correct way to store and transmit dates is usually by using the ISO format + Time Zone.
Let's take an example. Suppose I have an app that allows booking medical appointments. Since healthcare in Spain isn't as good as it should be, they schedule my appointment for months later.
Your appointment is scheduled for Saturday, July 20, 2024, at 09:00 AM, Spanish mainland time.
This date and time are expressed in civil time.
Imagine that we save this date in my database, storing the date and GMT offset in ISO format.
2024-07-20T09:00:00.000+02:00
This date corresponds to a certain UTC moment:
1721458800000
Which in turn corresponds to this GMT date:
Sat, 20 Jul 2024 07:00:00 GMT
Now let's imagine that in the meantime, some countries in Europe, including Spain, decide to abolish daylight saving time. By the time the day comes in our database, we still have:
2024-07-20T09:00:00.000+02:00 (1721458800000 UTC)
Transformed to the hypothetical Spanish civil time after the time change will be:
Saturday, July 20, 2024, at 08:00 in the morning, Peninsular Spanish time.
However, is this what we want? Do we want to persist the original UTC moment to which the civil date corresponded? Or do we want to preserve the initial time when the patient requested the medical appointment?
In my opinion, when a person schedules a medical appointment for 9 in the morning, they don't care about UTC or GMT or any daylight saving time changes that occur in their country in the meantime. They care about their appointment being at their local civil time.
In my case, when I say "Saturday, July 20, 2024, at 09:00 in the morning, Peninsular Spanish time," I don't care about anything that happens in between—I want my appointment at 09:00 in the morning!
Conclusions
In this text, we have only scratched the surface of all the implications and pitfalls you can encounter when dealing with dates, just to name a few:
- Until 1908, Russia used the Julian calendar, causing them to arrive 12 days late to the Olympics that year.
- There was a February 30th.
- The UNIX time (used in JavaScript dates) is not exactly the UTC time.
For now, whenever you handle dates, I recommend keeping this text in mind, and if necessary, persist the time zone along with the date to preserve as much information as possible.
Top comments (1)
Good write-up. I will freely admit that I use GMT and UTC to be interchangeable. Or, rather, I would say that I usually refer to to UTC when it comes to storing date/times and only mention GMT when it comes to dealing with "where I am" in the world.
I think it's worth pointing out that times in the "past" vs. times in the "future" have different constraints. A timezone can't change retroactively in the past. So, any date/time stored to represent something that happened should be safe and stable.
The future date/time is interesting. And, I think it represents one of those times where we see just how messy the world is. This reminds me of placing an online order; and then, the product that I ordered is broken when they go to get it out of the warehouse. At some point, the "clean digital" world has to be reconciled with the "messy unpredictable" world.
So, to your question about what to do if the timezone for a future medical appointment changes? I don't think there's an easy answer. I think a human would have to get involved to figure out what makes the most sense (do we cancel? Do we move? Do we skip lunch to open up another appointment? Do we extend the hours of the office for another hour on that day?). Such a scenario can't be solved by a machine alone.
Really good food for thought!