In our daily jobs as programmers we tackle coding challenges and abstract them in algorithms and functions, happy when we find an elegant solutions to a tricky problem. We find comfort in dealing with easily understandable, deterministic and logical concepts.
Time is NOT one of them. And that’s a big problem. We are so used logical terms, that we try to apply our thinking to the concept of time as well. But the truth is: Time is just a big mess.
This first part about time zones is platform independent and illustrates some of the pitfalls when dealing with time.
A naive junior programmer might assume the following
- every day has 24 hours
- every year has 365 days, every fourth year is a leap year and has 366 days
- when you note the time of the start and end of a task, then the end is always later than the start. The difference between end and start is the duration of the task.
- every point in time in a day happens exactly once
None of those is true
- when changing between summer and winter time, days can last 23 or 25 hours
- a year is a leap year, if divisible by 4, but not by 100 except if also divisible by 400
- when you boil an egg at 2:59 at the day when switching from summer to winter time, you’ll finish boiling at 2:02. You traveled back in time, the duration is negative
- when switching to summertime the moments between 2:00 and 3:00 never happen. when switching to wintertime, they happen twice
Now you have graduated to senior programmer. You know all this, you know about time zones and UTC. But you still might think:
- It’s ok to save time zones as UTC offsets (e.g. Austria is CET which is UTC+1)
- The time difference between two time zones is constant
- Time offsets are always +/- hours of UTC
- When you store the time zone of a city in your database that’s unambiguous and you are safe. You can easily calculate from UTC to local time
- When you store the timestamps for events happening at a location then it’s ok to figure out the nearest city in the same time zone e.g. Vienna and always use that for your time calculations.
Again none of the above is true
- Due to summer time changes, Austria alternates between CET which is UTC+1 and CEST which is UTC+2. So you always have to store time zones in tz database format e.g. “Europe/Vienna”
- Countries do not change to summer time at the same point in UTC time. Some countries do not have summer time. So the difference between two timezones varies during a year.
- Some countries have 30 minutes time difference, some have 15 minutes to their neighbouring countries
- In the Gaza strip in 2011 Palestinians and Isreali people in the same geographical location followed different time zones!
- When you store events happening in Hainburg (East of Austria) then it would seem intuitive to save Europe/Bratislava as time zone as this is the nearest tz time zone. But beware, in 2021 the EU will let each individual country decide which time zone it wants to remain in. They can choose between CET or CEST. So from then on events happening in Hainburg might be wrong if stored as Europe/Bratislava but should be stored with Europe/Vienna instead.
But that’s not all. Countries change timezones sometimes within days, some opt out of summer time, back in, then out again, Wars move borders and time zones. Sometimes countries change time zones and drop whole days from their calendar. And that’s all just within the commonly used Gregorian calendar which has only been in use for a few hundred years, so don’t even try to properly handle historical dates.
Three different types of time
When dealing with time, usually you should differentiate between three types of time
- UTC, which is easiest to understand, one universal time zone, no summer time. But be careful, it sounds universal, but only has been in existence since 1972, so who knows if it will stay that way. We might change to a decimal universal time one day.
- Local time: The time at a certain geographical location for a certain political individual (e.g. time for an Israli citizen in the Gaza strip).
- Wall time: The time a person observes on a clock independent of time zone. Wall time/date is used for many things like
- Birthdays. You celebrate your birthday on the same calendar day, no matter which time zone you are currently in
- Daily events. You eat breakfast at 8:00 in the morning
- Calendar events. You note down: “Meeting on Friday at 14:00”, even if the meeting is happening in New York, and today you are in Singapore.
Some suggestions on how to store time
- When storing events in your database, always store UTC (either as UTC timestamp or ISO e.g. 2019-04-05T9:00Z)
- If you need the event later in local time, storing UTC offset is sufficient (additionally to UTC you store e.g. +2:00)
- If you need to do time calculations based on that event in local time, store tz time zone (for example Europe/Vienna)
- If you need to do long term time calculations (like adding 10 years) for that event, then you simply can’t do that in a reliable way. Do the same as in 3. and hope no war or political decision will interfere with your stored time zone. Good Luck!
- When storing wall time, don’t use any time zone. Store as April 5th 2019, 9:00. Be careful when using time zone independent data types. Strange things can happen, which we will discuss in Part 2 of this series. Sometimes using a string like “2019-04-05 09:00” is the safest way.
Time is a highly chaotic, politically motivated concept that has absolutely nothing to do with logic or determinism. Try not to code time zone handling yourself. For web development use moment-timezone
Last but not least, time is best described in The Time Warp song by Richard O’Brien:
Time is fleeting
Madness takes it’s toll
Still not enough? Watch this video.
P.S. For the physics nerds out there. Yes, we know about general relativity and quantum effects. So to be really on the safe side you should store the following in your database: UTC, velocity vector, mass of nearby objects like planets and/or black holes, scale to account for quantum physics and “Europe/Vienna”.