How to deal with time
We count dates and read the time on the clock with ease in our every day lives. These concepts may appear simple, but they are riddled with complexities. As people who write software that users depend on, it is critical that we get them right.
Time
When we mention time, we could be referring to two different concepts — the local time or a point in time. The local time refers to the time at a specific place. Where you are, your local time is what the wall clock shows. If you were to call your friend who is living on the other side of the Earth and ask what their wall clock is showing, it would be very different.
A point in time, on the other hand, refers to time at that instant. For example, the first manned moon landing occurred on July 20, 1969, at 20:17 UTC. This would be the same for both you and your friend as it is not relative to any location.
But what if we want to refer to the local time in other places, or describe a point in time in our local time? This is where time zones are useful.
Time Zones
A time zone is a region that follows a uniform local time. This means that as long as you stayed within the region, your local time would remain the same but if you moved to a different time zone, that local time might differ.
All time zones contain an offset in minutes, relative to the primary time standard - UTC (Coordinated Universal Time). UTC, by its nature as a guideline, has a permanent offset of 0 minutes.
Unlike the time standard, each time zone’s offset may be subject to change due to things such as Daylight Savings Time (DST) or new legal rules.
Similarly, the regions under each time zone may also change. Russia, for instance, has moved Udmurtia between Moscow Time and Samara Time a few times.
This unpredictable nature of time zones makes rules hard to codify, which is why changes are recorded in databases and frequently updated.
IANA Time Zone Database
The IANA Time Zone Database goes by several other names such as tz database, tzdata, tzdb and zoneinfo database. It is used in all major operating systems and browsers, with the exception of Windows.
It uses a combination of area and location as identifiers for time zones. Area may refer to continents or oceans (Asia, America, Pacific), and locations are usually cities (Seoul, London, San Francisco). Some examples are Asia/Seoul
, America/New_York
and Etc/UTC
.
Microsoft Time Zone Database
The Microsoft Time Zone Database is only used in Windows. It is not used in Edge as browser standards dictate the use of the IANA Time Zone Database.
Microsoft also provides an API to translate between the two databases. https://docs.microsoft.com/en-us/rest/api/maps/timezone/gettimezonewindowstoiana
Recap
- Time zones determine a region of local time.
- Every time zone has a offset relative to UTC in minutes.
- Time zone offsets can change. These changes are recorded in the IANA time zone database.
Timestamps
Timestamps are representations of points in time.
The international standard ISO 8601 contains multiple formats for dates and time. Its ISO 8601 Extended Format in the form of YYYY-MM-DDTHH:mm:ss.sssZ
is also used in the RFC3339 profile and ECMA-262 Date Time String Format.
Another alternative is the Unix Timestamp where January 1st 1970 at 00:00 is the fixed reference starting point. Do take note that this means Unix Timestamps only work from 1970 onward and it does not account for leap seconds.
Storing Timestamps
The format you use to store timestamps depends on its purpose. Past timestamps and future absolute events work great with UTC. That is, timestamps that only go as far back as 1970.
Workflows such as alarm clocks, recurring tasks and due dates depend on future local events. For example, when you set an alarm, you would want it to ring at 6am even if the offset for the time zone has changed.
For scheduling events, it requires storage of the local time, the time zone and the recurrence pattern (if any). This allows the proper scheduling of events even if time zone offset changes occur.
Accepting user timestamps as inputs
The process of taking in strings and transforming them into timestamps is called parsing. Parsing is not a reliable way of obtaining timestamps. For example, “2-10-1980” is interpreted as October 2nd 1980 for most of the world but it is February 10th 1980 in the United States.
As such, the most straightforward way is to allow the user set the date or time using a calendar widget.
If that is not an option, hinting the format using a placeholder is highly advised. This format should be in the user’s locale.
Accepting timestamps programmatically
Servers often send and receive timestamps as data. While most modern computers track time consistently, no two clocks are perfectly synchronized. Clockdrift can occur, such as a server receiving timestamps occuring in the future, and result in programatic errors.
Even a server’s own generation of timestamps may occasionally backtrack, causing errors where programmers assume that the timestamps are always monotonically increasing.
As we can see, timestamps are tricky, thus is is important to challenge our assumptions whenever we work with them.
Displaying timestamps
When we are displaying timestamps, we more accurately formatting them. There are two ways to format timestamps.
Relative Timestamps
Relative timestamps inform users of the amount of time between the event and their reference of time. Some examples of relative timestamps are “Just now”, “Two hours ago” and “Tomorrow”.
Formatting relative timestamps is situational as the granularity of the duration varies by application. As such, there is no standard for relative timestamps.
If you are implementing relative timestamps, do keep in mind of things like localization, Daylight Savings Time and leap years.
Absolute Timestamps
Absolute timestamps like “19 July 2020 at 16:34” answers the question of “when did this happen?”.
The best practice is to follow the user’s time zone and locale, which ensures that users can understand the time without misunderstanding.
How to store repeating dates keeping in mind of Daylight Savings Time
Network Time Protocol (NTP)
NTP is used in computers to synchronize clocks to the UTC time. Under rare circumstances, NTP may set the local clock backwards, or introduce clock slew in case the local time is too far forward. Understanding these edge cases may help in debugging and avoiding critical bugs.
Timestamps and Dates in JSON
JSON does not specify a format, but the ISO 8601 Extended Format in the form of YYYY-MM-DDTHH:mm:ss.sssZ
is human readable and well supported, especially in modern JavaScript engines.
Alternatively, Unix Timestamps can be used. Although they are more compact and faster to process, they are not human readable. Confusion and bugs may also arise as the specification only support seconds but JavaScript engines return Unix timestamps in milliseconds.
Timestamps and Dates in JavaScript
https://css-tricks.com/everything-you-need-to-know-about-date-in-javascript/
JavaScript’s Date
object is in fact a timestamp, as it has a time component and follows the local time zone.
Obtaining dates
Use new Date()
to get the current time as a Date
instance and Date.now()
to get it as an Unix timestamp in milliseconds (ms).
Use new Date(year, monthIndex, [day, ...
constructor format for other dates.
It is not advisable to pass a string to the Date
constructor as the implementation varies by browsers.
Displaying dates
There is Intl.DateTimeFormat
that can format dates, although some methods are still not widely supported even among modern browsers.
There is also Intl.RelativeTimeFormat
that provides relative time formatting, but browser support there is even poorer.
For best practices, cache and reuse the Intl.DateTimeFormat
and Intl.RelativeTimeFormat
instances for better performance.
Manipulating dates
It is advisable to use battle-tested libraries like Moment, Date-fns, or Luxon to manipulate or calculate dates as they can handle edge cases well.
There is a proposal for a new global object Temporal
which would replace the need for these libraries. https://github.com/tc39/proposal-temporal
Getting the local time zone
In modern browsers, you can get the local time zone via Intl.DateTimeFormat().resolvedOptions().timeZone
.
For best practices, ask the user for their time zone with a default option, and allow them to change it at any time.
Converting between time zones
In modern browsers, you can convert a date’s time zone by first converting it to a en-US
localized string with a IANA time zone identifier, then back into a Date again.
Either new Date(Intl.DateTimeFormat("en-US", {timeZone: "Australia/Brisbane"}).format(localDate))
or new Date(localDate.toLocaleString("en-US", {timeZone: "Australia/Brisbane"}))
will work. The former is better for performance if you cache and reuse the Intl.DateTimeFormat
instance.
If you need to support Internet Explorer and older browsers, you will need an external library like Moment Timezone. Do remember to only select the locales you need, or your bundle size will become huge otherwise.
JSON and ISO 8601
JavaScript Date
s toJSON
method outputs an ISO 8601 conformant date in the UTC time zone. toISOString
does the same, but it raises an exception instead of null if it encounters an invalid date.
A word of warning
Do note that browsers update their own IANA Time Zone Database independently, and their implementation of Date
and Intl.DateTimeFormat
may contain bugs. If your application cannot tolerate such errors, do everything on the server side instead.
Timestamps in PostgreSQL
PostgreSQL has two timestamp types.
TIMESTAMP WITH TIME ZONE
is the time zone aware timestamp type.TIMESTAMP
, which is an alias forTIMESTAMP WITHOUT TIME ZONE
, contains both date and times, but not time zones.
Storage of local time with respect to a certain timezone requires a TIMESTAMP
field to store the timestamp and a TEXT
field to store the time zone.
Timestamps and Dates in Java
What’s the difference between Instant and LocalDateTime?
Timestamps and Dates in .NET
Although C# has a native DateTime class, it only encapsulates a timestamp in the local time zone. Furthermore, it depends on Windows time zone identifiers instead of the IANA identifiers used in every other environment.
As such, as a best practice, use the Noda time library.
For a longer answer, read https://blog.nodatime.org/2011/08/what-wrong-with-datetime-anyway.html.
Falsehoods
https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca
References
- https://stackoverflow.com/questions/2532729/daylight-saving-time-and-time-zone-best-practices/30897258#30897258
- https://stackoverflow.com/questions/19626177/how-to-store-repeating-dates-keeping-in-mind-daylight-savings-time?noredirect=1&lq=1
- https://stackoverflow.com/questions/19166995/java-calendar-date-and-time-management-for-a-multi-timezone-application/19170823#19170823
- https://en.wikipedia.org/wiki/Tz_database
- https://medium.com/@toastui/handling-time-zone-in-javascript-547e67aa842d
- https://stackoverflow.com/questions/45688749/what-is-simplified-in-ecmascript-5-date-format-compared-to-iso-8601
- https://ux.stackexchange.com/questions/133066/user-expectations-when-measuring-durations-relative-time-periods-around-daylig
- https://stackoverflow.com/tags/timezone/info
- https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca
- https://en.wikipedia.org/wiki/Leap_second
- https://css-tricks.com/everything-you-need-to-know-about-date-in-javascript/
- https://exploringjs.com/impatient-js/ch_dates.html#time-standards