Instants, times of day, time zones etc. In the programmer’s mind, these concepts often conflate into a singular entity known as “time”. In many standard libraries, all time-related tasks are relegated to only one or two classes. Libraries also attempt to often guess what the coder has meant in a given situation and they try to make things “better” behind the scenes. The end result being that code relating to time tends to contain an unusually high number of errors. These errors can be difficult to diagnose because some of them may negate each other, or the code works as intended most of the time.
This writeup attempts to clarify the differences between some time-related concepts. Hopefully this will alleviate some readers’ temporal troubles and result in fewer time-related bugs.
I am by no means an expert in these matters; this article serves mainly as an outlet for my own deliberations and as a way to untangle my own thoughts.
An instant is a point in time. Imagine someone starting up a stopwatch. The starting instant defines the timeline’s origin, its epoch. All other instants can be described in seconds before and after the epoch. One reading of seconds can be used all over the world to refer to the same instant. If the time in Finland is 73 seconds after the epoch, at that same instant the time in Japan is 73 seconds after the epoch as well. Everyone is referring to the same stopwatch.
One of the most well-known epochs is the Unix epoch. At the time of writing, the “Unix stopwatch” has run for 1,653,470,633 seconds. Conversely, Windows systems employ what is known as the NT epoch. The aforementioned instant translates to 13,297,944,233 seconds after the NT epoch. Aside from seconds, other units of time are also used, such as milliseconds or tenths of a microsecond, which is what Windows uses. These are only details, however, and it’s preferrable to concentrate on being able to represent instants as abstract points in time.
Instants are relatively easy to grasp. When you add 7 seconds to an instant, you get an instant that comes 7 seconds after the first instant. The difference in seconds tells the amount of time between the instants. Comparing the amounts of seconds tells you which instant came before.
The easy part ends here, however.
For the purposes of this writing, date refers to a combination of time of day and date, such as 2022-05-25 12:23:53. As a standalone unit, a date never refers to a specific instant as its interpretation depends on the viewpoint. From my viewpoint (Finnish time) that date refers to the Unix instant of 1,653,470,633 seconds, but from a Swedish viewpoint the Unix instant would be that of 1,653,474,233 seconds. A date alone doesn’t refer to “local time” either, as this would assume a specific time zone.
A date, then, is simply a sequence of numbers or other attributes that represent years, months, and other concepts that follow certain rules. As an example, the day must be between 1 and 31, wherein the maximum is dependent on the month and the year.
As you might have guessed, dates aren’t as simple as instants. What may surprise you is how convoluted things can actually get. For instance, what does it mean to add a calendar month to the date 2019-02-28? Do you get 2019-03-28 or 2019-03-31? What about adding a whole calendar year and one calendar day to that same date? 2020-03-01 or 2020-02-29? Instances like this need to be handled on a case-by-case basis. The first step is in understanding the types of problems that may occur.
Where things really go off the rails is when instants and dates are confused with one another. Let’s say that you need to add one day to the date 2022-10-30 00:00. The client’s intention was one calendar day (with the result being 2022-10-31 00:00) but the programmer saw “one day” as one “stopwatch day”, i.e., 86,400 seconds, and came up with 2022-10-30 23:00 because they were located in Finland and clocks were just moved to Standard Time.
Comparing dates can be problematic as well. When asked which date came before, on the face of it the answer is simple. The problem here is that what is being asked is in fact which of the two local times came before and this doesn’t always come with a single answer. When clocks are set to Standard Time, dates are repeated, but their initial occurrences refer to earlier instants than the later ones.
A time zone constitutes a list of rules with which an instant can be translated into a date. The pivotal time zone is UTC, which is basically governed by two rules:
Leap seconds do add their own little caveats, but they can usually be ignored (famous last words?).
Other time zones are usually represented using UTC. For instance, Finnish Summer Time is UTC+3 (hours), meaning that an instant can be described in Finnish Summer Time by converting it to UTC and adding 3 calendar hours. However, a time zone isn’t only a difference in time zone offset, such as 3 hours. A time zone can be a little more involved. The Finnish time zone comes with its own set of stipulations:
From time to time there’s been talk about dropping either Summer or Standard Time in Finland. If this ever comes to pass, this will mean changes to the time zone rules!
To summarise, the Finnish time zone isn’t “UTC+2 (UTC+3 in summer)” or “EET/EEST” because historically the time zone has been a lot more complicated and may still change. Instead, the Finnish time zone is Europe/Helsinki (zoneinfo identifier) which refers to the entire dynamic set of rules.
When the instant and the time zone are known, it’s possible to determine what local time is. For instance, the Unix instant of 1,653,470,633 seconds translates to 2022-05-25 12:23:53 in Finnish local time. Local time communicates both the place and what the clockface is displaying there.
These translations aren’t always possible both ways. As an example, the date “2022-03-27 03:30:00 Finnish time” is erroneous as that particular date was skipped due to the jump into Summer Time and therefore the date doesn’t represent an actual instant. On the other hand, the date “2022-10-30 03:30:00 Finnish time” has multiple meanings, as this date will occur twice due to the jump into Standard Time. That is, unless Standard Time is abandoned before then…
We can construct at least a couple of ground rules for how to store time from all of this. Care should be taken when implementing these:
Past times should usually be stored as instants. If they’re only stored as local dates, they can no longer be arranged chronologically, not even if the time zone is known. They can’t be translated into instants or local times of other time zones either. They also make it impossible to determine how much time has passed between two dates. If local time has to be calculated, it might be better to just store the instant and the time zone.
Future times are even more troublesome. Let’s consider a reminder application. It is now 2 pm and the user sets a reminder for 5 pm. Then the user quickly flies into another time zone. If the reminder was stored as an instant, it will go off 3 hours after it was set regardless of date. If the reminder was set as a date, it will go off at 5 pm regardless of how much time has passed since it was set. The ground rule here could be that when the user inputs a date, it gets stored as a date, since the corresponding instant can’t be reliably calculated. If the user inputs an amount of time to be waited, it should be stored as an instant, since the corresponding date can’t be calculated.
Why the DateTime class in .NET is problematic https://blog.nodatime.org/2011/08/what-wrong-with-datetime-anyway.html
Common – or at least somewhat frequent – misconceptions regarding time https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time
The author of this blog, Kristian Setälä, works as a Senior Software Developer at Trineria.
Nadia Sabour has joined Trineria's team as Chief Sales and Marketing Officer (CSMO).
Anna-Leena Poukkanen has started as HR Manager at Trineria.
Trineria’s new quality management system meets the ISO 9001:2015 requirements.
The digital Scout Petitions platform is a tool for collecting and processing development initiatives.
It's easier to buy software development if you consider a few things from the start. We've put together six practical steps to make the software buying process easier for your company.
IteWiki's series of articles showcases software companies and the people behind them. Trineria, a developer of customised industrial internet software, is targeting 40-60% annual growth.
We adopted the OKR model at Trineria because we wanted to translate the company's new strategy into day-to-day work.
Justus Sydänmaa has started at Trineria as a Software Developer Trainee.
Johannes Manninen has started as a Software Developer. He brings with him a wealth of experience and expertise.