There are basically three types of time: coordinated time, based on atomic clocks, zoned time, derived from coordinated time by applying a time zone rule to calculate an offset, and wall clock time. The latter two are often lumped together as local time but are not in practice the same thing.
TZ rules are one way, you can derive zoned time reliably from utc, but not the other way around unless you already know the offset. That’s why seemingly storing utc is best, because you can apply whatever tz rule you want. But, because tz rules are decided by politicians (dst, ramadan, date line shifts, it gets pretty weird, …) and often with little advance notice, tz rule databases are often wrong in practice, which means storing only utc and getting zoned time back out of it reliably is hard. That’s why storing iso timestamps with offsets is best. It preserves both utc and intended local (zoned) time at the time of storing, even if the tz database then changes.
But this is not the author’s problem. They’re dealing with wall clock time. They want to store 9 am and have it always be 9 am, no matter what happens with time zones. That is easy: store the timestamp without tz info. But, the hard part is knowing the instant in time (in coordinated time). Wall clock times may occur twice, so there is no single reliable way to get utc out of it, and even when calculating utc the result can be wrong or become wrong due to the political insanity that is zoned time so it is dangerous to store it and rely on it. You see this problem with anything that schedules people or resources. Something scheduled at 9 am is always 9 am, but if the system needs to send a reminder at 9 am, when do you schedule it? There are no solutions I’m aware of without edge cases.
I solved this for a reservation system by storing wall clock time without offsets and the time zone (id) of the resource separately, and then calculating zoned time for that zone and comparing it with the stored wall clock time every time the instant in time mattered. It worked but was tricky to code.
I think it's even more complicated than that. Human time is much richer even than wall-clock time. E.g., if I go out for a dawn run every day, to me that's the same time. Or think of the Jewish Sabbath, which runs from sundown Friday to after dark on Saturday. Lunchtime. Teatime. Lunar calendars. Whatever people will do when they live on Mars. Or the way wall-clock time existed before railroads changed it.
It's often our job to cram human concepts into a small number of bits, so I get why we do what we do. But I think it's sometimes worth being explicit that the anthropological concepts we are dealing with are very complex, and we may leave a lot out when we simplify them enough to make sense to the sort of machine that can be cheaply built in its era.
Wall clock is solved by having a camera pointed at local live news where local time is shown, and having your system updated periodically where this period is arbitrarily short.
A similar issue can get developers in trouble with dates, specifically the YYYY-MM-DD for a given event. In C# for instance, there used to only be the DateTime, which stored a date AND a time, which was set to 00:00:00. It was on you to know to ignore the time in comparisons and/or always use the DateTime.Date method to compare with a 00:00 DateTime. This was fixed with the introduction of DateOnly (https://docs.microsoft.com/en-us/dotnet/api/system.dateonly?...).
Databases usually have types to represent just dates or dates/times. Always use the lowest resolution type to represent what you actually are trying to capture.
Rule number one and the only rule about wall clock times is that they are display style only and must never be stored. Local times must use 24 hour format.
This isn't what they're talking about, though. Say your boss says "your work shift starts at 9am". That isn't an instant in time, it's all about the display rule: clock says 9am and you're not at work, you're fired.
We don't typically care about this because we work 9-5 M-F, and daylight savings time conveniently happens at 2am on Sunday morning. But someone was told to come in at 2am this morning and couldn't actually do that because 1:59:59 became 3:00:00. How do you handle that? It's not a code problem, it's a business rules problem.
No, this is not a display rule. This is the domain data type "local time", which stores time component without offset or time zone.
Data stored this way is basically telling that regardless of current UTC offset, locally on the clock there will be always the same time shown.
Display rules are saying how exactly this time will be displayed on the clock (i.e. locale, 12/24 hour format etc). Wall clocks usually do not show "am/pm" information, so the same time indeed may occur twice on them.
I do not think this is what they meant, 24h or 12h+am/pm are equivalent amount of information.
I believe they were referring to the fact that because of DST and other time adjustments, 24h wall clock times can in fact occur twice (or not at all, like this morning).
That is still either display rule or scheduling process topic, rather than a data type issue.
Few examples:
1. Local public transportation systems must commit to certain schedule of departures from every stop and make it unambiguous. This means that they have to always use local time in publicly communicated schedule, but plan the time table in UTC assuming that offsets will not change. Usually results in departures happening twice on the same time, requiring some extra resource planning.
2. Long-range trains and flights must (and usually do) operate in UTC, communicating arrival and departure times via display rules.
3. Shift planning in 24-hour working processes: offsets complicate the planning process and add unnecessary risk of breaking compliance, so they better be avoided and UTC must be used in combination with display rules.
4. Execution of scheduled jobs: depends on recurrence setup, for which display rules must distinguish between "once per day" (local time + take first occurrence on spring offset change) and "every 24 hours" (UTC + display times of occurrence).
It seems to me that you are mostly arguing about what kind of time to use for a given business problem here. This is actually an interesting problem, but it is a business problem.
For example where I live trains will have a 1h delay (sometimes there are buffers that you can use to reduce the delay, or you cancel trains that anyway only run in the time window that is missing) when “springing forward” and will take a 1h break in the middle of the night when “falling back.” The schedule itself doesn’t change. Seems weird to me, but it is how the business rule is.
But what if the business rule is to use wall clock time? And that is actually a sensible choice for things like concerts or dinner appointments in a calendar.
When you use wall clock time (I assume that is what you mean when you call it local time) in example 4 you choose
> local time + take first occurrence on spring offset change
which is _one_ possible way to handle duplicate wall clock times; wall clock times are underspecified which you implicitly admit by choosing the first occurrence. So it _is_ a data type issue, unless you define the wall clock time data type to contain the occurrence number (e.g. something like `2022-10-30 02:32 II`).
To me that’s not a wall clock time but a wall clock time _with occurrence_ (or whatever name you want to give it) and it’s probably not a concept the user will know, so it needs some UX tweaks (e.g. hide the occurrence when there are no duplicate times and when there are notify the user about the ambiguity, offer a default and explicitly state it, maybe allow to change it to the 2nd occurrence, or maybe disallow duplicate times and ask for another time).
Another problem is that when switching forward there are holes in the time line, but that is easier to solve (it’s an invalid input).
> For example where I live trains will have a 1h delay (sometimes there are buffers that you can use to reduce the delay, or you cancel trains that anyway only run in the time window that is missing) when “springing forward” and will take a 1h break in the middle of the night when “falling back.” The schedule itself doesn’t change. Seems weird to me, but it is how the business rule is.
The ferries near me essentially keep the offset for the whole sailing day. The sailing day starts around 5 am, and has a number of trips; service has been reduced during covid, so typical last sailing times are around midnight or 1 AM, but when service continued through DST changes, a 2:30 AM ferry would sail an hour after 1:30 AM, regardless of what local time was then, and so on. The time between the end of Saturday's sailing day and the start of Sunday's sailing day varies due to the time change however. But we don't have 24 hour ferry coverage, so it's easy to keep the schedule aligned without adjusting service.
What really bothers me is that the tools we use (databases, time data libraries) have really poor support for wall time. Most of the times, there is no support at all.
That's why people go and store times in UTC (with a separated time zone), and then everything breaks because some tool always expects non-tz data to be on the local tz, or the original system converts tzs behind the scene, or whatever.
TZ rules are one way, you can derive zoned time reliably from utc, but not the other way around unless you already know the offset. That’s why seemingly storing utc is best, because you can apply whatever tz rule you want. But, because tz rules are decided by politicians (dst, ramadan, date line shifts, it gets pretty weird, …) and often with little advance notice, tz rule databases are often wrong in practice, which means storing only utc and getting zoned time back out of it reliably is hard. That’s why storing iso timestamps with offsets is best. It preserves both utc and intended local (zoned) time at the time of storing, even if the tz database then changes.
But this is not the author’s problem. They’re dealing with wall clock time. They want to store 9 am and have it always be 9 am, no matter what happens with time zones. That is easy: store the timestamp without tz info. But, the hard part is knowing the instant in time (in coordinated time). Wall clock times may occur twice, so there is no single reliable way to get utc out of it, and even when calculating utc the result can be wrong or become wrong due to the political insanity that is zoned time so it is dangerous to store it and rely on it. You see this problem with anything that schedules people or resources. Something scheduled at 9 am is always 9 am, but if the system needs to send a reminder at 9 am, when do you schedule it? There are no solutions I’m aware of without edge cases.
I solved this for a reservation system by storing wall clock time without offsets and the time zone (id) of the resource separately, and then calculating zoned time for that zone and comparing it with the stored wall clock time every time the instant in time mattered. It worked but was tricky to code.