The time Element and Relative Timestamps
Permalink to "The time Element and Relative Timestamps"Data interfaces are full of timestamps: “Updated 5 minutes ago”, “Created 3 days ago”, “Last login yesterday”. Relative times are friendly for recency and terrible for precision. The real time usually hides in a title tooltip that keyboard and touch users cannot reach and most screen readers do not read. And because relative text changes on its own, careless implementations announce “4 minutes ago… 5 minutes ago” to screen reader users every minute.
This guide belongs to accessible date & time data.
Spec reference
Permalink to "Spec reference"- HTML
timeelement — represents a date or time; thedatetimeattribute holds a machine-readable value (ISO 8601, for example2026-10-02T10:42:00+02:00). Screen readers read the element’s text content, not the attribute. - SC 1.3.1 Info and Relationships (A) — the absolute time is information; it must be programmatically available, not only in a hover tooltip.
- SC 1.4.13 Content on Hover or Focus (AA) — a custom tooltip with the absolute time must be dismissible, hoverable and persistent.
- SC 2.2.2 Pause, Stop, Hide (A) — auto-updating content is in scope; silent text updates are acceptable, announced ones are not.
- SC 4.1.3 Status Messages (AA) — relative time ticking is not a status message; do not announce it.
When to use — and when not to
Permalink to "When to use — and when not to"Use relative times for recency in lists and feeds, where “how long ago” is the question — activity logs, last-updated stamps, comment threads. Pair them with absolute times wherever precision matters.
Use absolute times in tables where users compare, sort or audit — transaction times, due dates, timestamps in logs. “3 days ago” cannot be compared at a glance with “2 days ago” across a hundred rows, and it means different things depending on when you read it.
The misapplication: a “Created” column showing only relative times, sorted by the hidden absolute value. Screen reader users hear “3 days ago, 3 days ago, 4 days ago” with no way to distinguish rows.
Annotated code example
Permalink to "Annotated code example"<!-- Relative text visible; absolute time in a description users can reach (SC 1.3.1) -->
<td>
<time datetime="2026-10-02T10:42:00+02:00" aria-describedby="t-88-abs">5 minutes ago</time>
<span id="t-88-abs" class="visually-hidden">2 October 2026 at 10:42 CEST</span>
</td>
<!-- Or: both visible, compact -->
<p class="updated">Updated <time datetime="2026-10-02T10:42:00+02:00">10:42</time> (5 minutes ago)</p>
// Update relative text every minute — silently (no live region, SC 2.2.2)
const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: 'auto' }); // "yesterday", not "1 day ago"
function refreshTimes() {
for (const el of document.querySelectorAll('time[data-relative]')) {
const t = new Date(el.dateTime);
const mins = Math.round((t - Date.now()) / 60000);
const days = Math.round(mins / 1440);
el.textContent = Math.abs(days) >= 7
? t.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' }) // switch to absolute
: Math.abs(mins) < 60 ? rtf.format(mins, 'minute')
: Math.abs(mins) < 1440 ? rtf.format(Math.round(mins / 60), 'hour')
: rtf.format(days, 'day');
}
}
setInterval(refreshTimes, 60_000);
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Event | Expected announcement | AT deviations |
|---|---|---|
| Arrow onto the cell | “5 minutes ago” then (after a pause) “2 October 2026 at 10:42 CEST” | Descriptions read per reader verbosity |
| Timer updates text | Nothing | Text read the next time the user reaches it |
| Copy the timestamp | Relative text copied | Offer a copy of the absolute time in a menu if needed |
| Time older than a week | “24 Sept 2026” | — |
Integration context
Permalink to "Integration context"Absolute times need zones when users span regions — see announcing time zones in data tables. Freshness stamps on grids use the same markup in stale and partial data indicators. A toggletip for the absolute time follows the toggletip pattern for column help.
Gotchas
Permalink to "Gotchas"Sorting by relative text. Always sort on the datetime value. Sorting the displayed text puts “yesterday” after “5 minutes ago” alphabetically.
Reading “10:42”. Screen readers read times according to locale; “10:42” is fine, but “10.42” may be read as a decimal number. Use the locale’s time format from Intl.DateTimeFormat.
Future times. Relative wording must handle the future: “in 3 hours”, not “-3 hours ago”. Intl.RelativeTimeFormat does this.
Framework notes
Permalink to "Framework notes"In React, a <RelativeTime date={d} /> component can share a single minute-ticking context so a table of 500 timestamps does not run 500 timers. In Angular, an impure pipe re-evaluated on a shared timer works, but mark it carefully for change-detection cost. GitHub’s relative-time web component (@github/relative-time-element) renders a time-like element with accessible defaults and works in any framework.
const now = useContext(MinuteClock); // one timer for the whole app
return <time dateTime={iso} aria-describedby={absId}>{formatRelative(iso, now)}</time>;
Worked example
Permalink to "Worked example"An admin console lists 400 user accounts with “Last login” shown as “2 hours ago”, “3 days ago” and so on, with the exact time in a title. An auditor using JAWS needs to find accounts that have not logged in since a specific date and cannot: the absolute values are invisible and the relative ones are not comparable. The fix splits the column. “Last login” now shows absolute dates (“12 Sept 2026, 08:15”) and sorts by them; a small “Active recently” badge marks accounts used in the last 24 hours. In the activity feed on the same page, relative times stay — “5 minutes ago” is the right answer there — with the absolute time as a description.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Provide a Timestamp component with mode="relative" | "absolute" | "both", a shared clock and built-in absolute descriptions. Data tables should default to absolute; feeds to relative with descriptions. Make the threshold for switching to absolute a documented token.
FAQ
Permalink to "FAQ"Do screen readers read the datetime attribute?
No. They read the element’s text. Put anything users need to hear in text or in a description.
Is the title attribute ever enough for the absolute time?
No. It is not reliably available to keyboard, touch or screen reader users. Use it at most as a duplicate of information available elsewhere.
Should relative times update live?
Yes, silently. Users expect “5 minutes ago” to stay true, but they do not want to hear it change.
How precise should relative times be?
Minutes for the first hour, hours for the first day, days for the first week — then switch to dates. “47 seconds ago” is rarely useful.
What about time stamps in screen reader-friendly exports?
Export the absolute ISO value or a locale-formatted absolute time, never the relative text.
Related
Permalink to "Related"- Announcing time zones — when times cross regions
- Formatting durations for screen readers — elapsed time done well
- Stale and partial data indicators — last-updated stamps in practice