HTML <time>
Formatting dates and times is a hard problem. This is relatively easy with single page web apps (SPAs) that work with structured data (usually from APIs), because they run in the context of the user’s browser and have both the user’s locale and timezone readily available for formatting. This is more difficult for traditional multi-page web applications (MPAs); while the HTTP Accept-Language
header specifies the user’s locale, the browser’s timezone is not communicated to the server. Statically-generated websites have it even worse, as they don’t get to easily provide any variation in times, because all of the data is pre-rendered in one particular locale context.
There is a nicer and elegant solution, but I believe that it is not as well known, and could be easily built-in to the HTML specification to ease the burden on developers.
Single Page Apps
The de facto standard for handling times in programming, database storage, and API requests/responses is to use RFC 3339 formatted UTC timezone times. E.g. 2006-01-02T15:04:05Z07:00
. These are machine readable pieces of data that get converted to appropriate locales and timezones using many built-in standard libraries or 3rd party time libraries.
In JavaScript, this would be Intl.DateTimeFormat. Given a JS Date
object, a locale (e.g. en-US
, de-DE
), and format options, the browser can produce for you a validly localized and converted time stamp.
const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
console.log(new Intl.DateTimeFormat('en-US').format(date)); // "12/20/2020" American English
console.log(new Intl.DateTimeFormat('en-GB').format(date)); // "19/12/2020" British English
console.log(new Intl.DateTimeFormat('de-DE').format(date)); // "19.12.2020" German
console.log(
new Intl.DateTimeFormat('en-GB', {
dateStyle: 'full',
timeStyle: 'long',
timeZone: 'Australia/Sydney',
}).format(date),
);
// Expected output: "Sunday, 20 December 2020 at 14:23:16 GMT+11"
console.log(
new Intl.DateTimeFormat('de-DE', {
dateStyle: 'full',
timeStyle: 'long',
timeZone: undefined, // Current timezone
}).format(date),
);
// Expected output: "Samstag, 19. Dezember 2020 um 19:23:16 GMT-8"
In SPA libraries/frameworks such as React or Vue, it’s relatively straightforward to setup a component that takes in a Date
and produces a short or long time stamp with the user’s current locale and time zone according to their browser settings. No need to ask the user what their timezone is or what language/locale they prefer. After all, that’s the whole point of HTTP’s Accept-Language
and JS’s navigator.languages
and Intl
.
Multi-Page Apps
MPAs receive the HTTP Accept-Language
header, specifying the browser’s locale. This is great for providing different content for different users, where pre-translated content is available for the specific language and dates, times, currencies, and more are formatted according to the proper locale.
For instance, a browser requesting the page of a website with Accept-Language: en-US
could receive content in American English, with dates formatted like MM/DD/YYYY
, currencies with the $
sign preceding the amount, .
decimal separators and ,
thousands separators, etc. While de-DE
would provide German content, DD.MM.YYYY
dates, succeeding currency symbols, ,
decimal separators and .
thousands separators, etc. This is done in different programming languages and HTTP packages generally by changing the HTTP handler’s context or thread locale properties from the Accept-Language
header, and having the time formatting use the currently-set locale. Of course, this means that you can’t specify your time format specifically; rather, you want to provide more general options such as JS Intl
’s “long”, “full”, “short”, “medium” type of formatting.
However, conspicuously absent is the browser’s time zone. The database may store a time in UTC (as is general practice), the HTTP server which is rendering the page may live on the US west coast (-07:00
to -08:00
), and the user’s web browser may be in Germany (+02:00
, +01:00
). Note that while Germany only has one time zone, other countries have more than one time zone can it can not be inferred properly from just the locale. What is the HTTP server to do?
Something seen in a lot of apps is the use of relative durations, such as “Posted 5m ago”. Durations as the difference from the past time to the current time is not influenced by different time zones, and is thus straightforward to implement and show to the user. This works well for applications where the relative time is more useful than the actual time stamp. But if a user does want the time stamp, it’s generally expected that hovering over that HTML element provides a tooltip with the stamp.
Posted <span
title="Sunday, 20 December 2020 at 14:23:16 GMT+11"
>5m ago</span>
Static Sites
Statis sites are even more limited, in a sense. Being statically generated before the content is served over HTTP, it rarely gets the chance to vary content based on locale, let alone time zone.
This may make some sense, though. Take a blog post for instance. Blog posts are rarely localized for languages outside its source. This blog post is certainly not going to be translate outside of English. So, it would follow that all of the times, currencies, other locale-dependent pieces of data stick with the locale used at the time of generation.
As an example, reading an American English (en-US
) blog or article but seeing a “Published on Samstag, 19. Dezember 2020 um 19:23:16 GMT-8” time stamp might be an odd sight. Not only will the user get some whiplash while consuming content that switches locales back and forth, but it violates the fact that the HTML document generally is set with one locale (<html lang="en-US">...</html>
). It’s true that lang can be changed per element tree, but it’s still potentially confusing for a consumer of the content.
Outside of the blog example, though, are web apps with dynamic content. Localizing user-submitted data is much more difficult, but the surrounding labels, navigation, etc. are usually localized and it makes sense that automatic time labels be localized as well. Of course, this applies more to MPAs than static sites.
HTML <time>
It may be unfamiliar to a lot of developers, but HTML actually does have a <time>
element. Its purpose is however limited.
<p>
The Cure will be celebrating their 40th anniversary on
<time datetime="2018-07-07">July 7</time>
in London's Hyde Park.
</p>
<p>
The concert starts at
<time datetime="20:00">20:00</time>
and you'll be able to enjoy the band for at least
<time datetime="PT2H30M">2h 30m</time>.
</p>
Semantically, the content bounded within <time>
is supposed to indicate a time of some sort. The actual method of display is entirely up to the SPA reactive code, the MPA rendering, or the static site’s chosen locale and format.
Meanwhile, the datetime
attribute is what contains a machine-readable representation of the human-friendly content within the element. This provides a lot of functionality and is very useful. Browsers that have a feature to automatically translate a page to a desired locale have an easier time re-rendering the contents of the element to a new locale. Screen readers may choose to ignore the rendered contents of the element and re-render from the machine-readable datetime
instead. And I’m sure there are many more use cases.
Being able to inject a machine-readable time into the HTML, especially with a spec-compliant element and attribute, is super promising. But sadly that’s all there is to it. Whoever renders the <time>
element is still responsible for the work of choosing the format, choosing the locale, and rendering that content once.
Discord, and HammerTime
It would be great if HTML and browsers did the job of converting the machine-readble time to the correct locale and timezone for you!
Take a look at Discord, for instance. Discord is not just plaintext, it has support for rich content. Typing :smile:
will automatically replace it with the appropriate emoji for you. This is well known. Included are items like @s to mention and link to a user, #s to mention a channel, and more. These are represented in the actual message string in storage and APIs as <@USER_ID>
, <#CHANNEL_ID>
, etc. See their docs for more.
Less known are Discord timestamps. Using the format <t:TIMESTAMP[:STYLE]>
with Unix-style seconds-since-epoch numbers, and custom formats (t
, T
, d
) one can show a time stamp regardless of the source locale or timezone. This is useful for schedules of expected events in the future without having to communicate which timezone a plaintext time stamp is in.
Shout out to HammerTime for generating these easily to copy & paste into Discord.
Upgraded HTML <time>
Why not have Discord-style time stamps in HTML? I would love to have the following just work.
<p>
The Cure will be celebrating their 40th anniversary on
<time datetime="2018-07-08T19:00:00Z" date="medium" time="short" />
in London's Hyde Park.
</p>
<!-- The Cure will be celebrating their 40th anniversary
on 9 Aug 2018, 05:00, in London's Hyde Park -->
All the server has to do is provide the UTC time stamp, and the generalized formatting options (much like Intl.DateTimeFormat
in JS), and the browser should be able to format it during rendering for us. That would solve so many problems! You don’t have to worry about storing the user’s time zone in cookies or a database, like many websites are prone to do due to HTTP’s limitations. Automatic translation can handle these pieces of information much more precisely. Screen readers could pronounce “9 Aug 2018” as an actual date instead of numbers and three-letter words. And changing a browser locale could automatically change these live as well.
It saddens me that this is not built-in to the HTML spec or browsers. Fortunately, I don’t think it’s very difficult to implement something similar in practice.
Implementation
First, an MPA or a static site can generate the static HTML for a <time>
element with the time data and format options. Use data-
attributes for valid HTML element extensions. The inner content should likely be pre-rendered in the same date/time styles, but would have to be in the UTC time zone by default.
<time
datetime="2018-07-08T19:00:00Z"
data-date="[full|long|medium|short]"
data-time="[full|long|medium|short]"
>
9 Aug 2018, 19:00 UTC
</time>
Second, have some JavaScript that executes after page load which looks for all <time>
elements and re-renders the inner contents of the element using the datetime
and date/time style parameters. And honestly that’s basically it. I wouldn’t be surprised if there’s already several versions of this as React/Vue components or plain JS libraries, or many similar home-grown solutions.
Some considerations:
- Users may disable JS in their browser for untrusted sites. By pre-rendering the contents as close to the intended result as possible, their experience is not degraded aside from an incorrect timezone.
- Browsers may have their locales set to a user preference, but
lang
may be set on the document and override the browser preference. Ideally, the following should render the time inde-DE
based on the element hierarchy: `<time … /> - A solution should listen for dynamically inserted HTML elements from document modifications are initial render, as many sites are wont to do.
- Setting a default style and overriding with data elements may ease burden of sprinkling styles everywhere and inconsistent styles. This is where built-in support would be useful, because it could maybe utilize CSS for cascading styles.
I tried searching for such a solution briefly and was surprised to not easily find any such re-usable library. Having done a quick, ad-hoc implementation in a pet project (Go, HTML rendering, no API, using as little JS as possible) I know it seems possible. However, I’m not as embedded in the JS ecosystem to make a correctly implementation, fully realized and publishable solution. Here’s to hoping someone can, or that this behavior becomes part of HTML! It would certainly make developers' lives that much easier.