Accessible Date & Time Data
Permalink to "Accessible Date & Time Data"Almost every data interface is full of time: created and updated stamps, due dates, durations, SLA countdowns, schedules, timelines. Time data is deceptively hard to present accessibly. The compact forms that make dense tables readable — “5m ago”, “01:23:45”, “CET”, a coloured bar across a calendar — are exactly the forms that screen readers mispronounce, that keyboard users cannot expand, and that people outside the author’s time zone misread. And the components that collect dates — date pickers, range selectors, calendars — are among the most frequently inaccessible widgets on the web.
This topic covers both sides: presenting time data so it is heard and understood correctly, and collecting it with components that work from the keyboard and with assistive technology. The users most affected are screen reader users (pronunciation, missing absolute times, unstructured calendars), keyboard users (calendar-only pickers, drag-only schedules), users in other time zones (ambiguous times, which is everyone at some point) and users with cognitive disabilities, for whom relative times that drift and abbreviations that need decoding add real effort. The failure this topic prevents is time information that is technically present but unusable — a time without a zone, a duration read as a clock time, a calendar that reads as a pile of titles.
This topic belongs to core ARIA & keyboard navigation for data UIs.
WCAG criteria in scope
Permalink to "WCAG criteria in scope"| Criterion | Level | Relevance to date and time data |
|---|---|---|
| 1.3.1 Info and Relationships | A | Absolute times, zones and event times programmatically available |
| 1.3.2 Meaningful Sequence | A | Calendar DOM in chronological order |
| 1.4.1 Use of Color | A | Event categories and overdue states in text |
| 1.4.13 Content on Hover or Focus | AA | Tooltips with absolute times are dismissible and persistent |
| 2.1.1 Keyboard | A | Date pickers and calendars fully keyboard operable |
| 2.2.2 Pause, Stop, Hide | A | Ticking relative times and countdowns update silently |
| 2.5.7 Dragging Movements | AA | Event move and resize without dragging |
| 3.3.2 Labels or Instructions | A | Date formats shown for typed entry |
Prerequisites
Permalink to "Prerequisites"- Composite widget roles & states, for the grid pattern used by date pickers and month views.
- Keyboard focus trapping & navigation, since pickers open in dialogs.
- Semantic HTML table construction, especially units and formats in headers.
- Accessible names & descriptions for data widgets, used to attach absolute times to relative ones.
ARIA & HTML spec reference
Permalink to "ARIA & HTML spec reference"| Element / attribute | Valid values | When to apply | Common misuse |
|---|---|---|---|
time datetime |
ISO 8601 date, time or duration | Every rendered date, time or duration | Expecting readers to speak the attribute |
aria-describedby |
ID of absolute time text | Relative timestamps | title attribute only |
abbr |
Expansion in title |
Zone abbreviations in headers | Relying on title being read |
role="grid" |
— | Date picker month, interactive month views | Table of plain numbers |
aria-selected |
true |
Selected date in a picker | Colour only |
aria-current="date" |
— | Today in a picker or calendar | Bold text only |
aria-disabled |
true |
Unavailable dates | Removing them from navigation |
input inputmode="numeric" |
— | Typed date entry | Read-only input that opens a popup |
Step-by-step implementation
Permalink to "Step-by-step implementation"Step 1 — Mark up every time (SC 1.3.1)
Permalink to "Step 1 — Mark up every time (SC 1.3.1)"<time datetime="2026-10-02T10:42:00+02:00">10:42</time>
See the time element and relative timestamps.
Step 2 — Absolute behind relative (SC 1.3.1, 1.4.13)
Permalink to "Step 2 — Absolute behind relative (SC 1.3.1, 1.4.13)"<time datetime="…" aria-describedby="abs-1">5 minutes ago</time><span id="abs-1" hidden>2 October 2026 at 10:42</span>
Step 3 — Zones in headers (SC 1.3.1, 2.4.6)
Permalink to "Step 3 — Zones in headers (SC 1.3.1, 2.4.6)"<th scope="col">Opened (your time, Central European Summer Time)</th>
See announcing time zones in data tables.
Step 4 — Durations with full units
Permalink to "Step 4 — Durations with full units"<time datetime="PT1H23M"><span aria-hidden="true">1h 23m</span><span class="visually-hidden">1 hour 23 minutes</span></time>
See formatting durations for screen readers.
Step 5 — Typed entry first, calendar second (SC 3.3.2, 2.1.1)
Permalink to "Step 5 — Typed entry first, calendar second (SC 3.3.2, 2.1.1)"<input id="due" aria-describedby="due-hint"><span id="due-hint">Format: YYYY-MM-DD</span>
See the date picker grid keyboard pattern.
Step 6 — Structured calendars with an agenda (SC 1.3.1, 2.5.7)
Permalink to "Step 6 — Structured calendars with an agenda (SC 1.3.1, 2.5.7)"<li><a href="/events/41">Design review, 10:00 to 11:00, internal</a></li>
See accessible calendar and timeline views.
Keyboard interaction contract
Permalink to "Keyboard interaction contract"| Context | Key | Action | Expected AT announcement | Failure indicator |
|---|---|---|---|---|
| Date field | Typing | Enter date | Characters echoed | Field read-only |
| Date field | Calendar button | Open picker | “Choose date, dialog, October 2026…” | Opens on focus |
| Picker | Arrows | Day / week | “Saturday, 3 October 2026” | “3” only |
| Picker | Page Up / Down | Month | New month and day | Silent month change |
| Picker | Enter / Escape | Choose / cancel; focus to field | Field with value | Focus lost |
| Month view | Enter on day | Into event list | Event name with time | Titles only |
| Event | Edit command | Edit time without drag | Dialog with start and end | Drag only |
Screen reader compatibility matrix
Permalink to "Screen reader compatibility matrix"| AT | Relative time + description | Zone abbreviation | Digital duration | Picker day names |
|---|---|---|---|---|
| NVDA | Description after pause | abbr title not read by default |
Clock-like | Full names if provided |
| JAWS | Verbosity dependent | Setting dependent | Clock-like | Full names if provided |
| VoiceOver | Description after pause | Not read | Varies | Full names if provided |
| TalkBack | Inconsistent descriptions | Not read | Clock-like | Full names if provided |
| Narrator | Description read | Not read | Clock-like | Full names if provided |
Edge cases & failure modes
Permalink to "Edge cases & failure modes"1. Sorting by displayed text
Permalink to "1. Sorting by displayed text"Diagnosis: “yesterday” sorts after “5 minutes ago”. Fix: sort by the underlying value.
2. Ticking text in a live region
Permalink to "2. Ticking text in a live region"Diagnosis: “4 minutes ago… 5 minutes ago” announced every minute. Fix: update silently.
3. DST-wrong zone names
Permalink to "3. DST-wrong zone names"Diagnosis: “CET” shown in summer. Fix: derive names from the displayed date with Intl.
4. Picker that skips disabled dates
Permalink to "4. Picker that skips disabled dates"Diagnosis: arrow keys jump over unavailable days unpredictably. Fix: keep them focusable with aria-disabled and say “unavailable”.
5. Calendar read as a jumble
Permalink to "5. Calendar read as a jumble"Diagnosis: absolutely positioned events in arbitrary DOM order. Fix: chronological DOM inside day containers, and an agenda view.
The time element and relative timestamps
Permalink to "The time element and relative timestamps"Relative times are friendly; absolute times are precise. Use both: relative text, absolute time as text or description, silent updates and absolute dates beyond a week. See the time element and relative timestamps.
new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' }).format(-1, 'day'); // "yesterday"
Announcing time zones in data tables
Permalink to "Announcing time zones in data tables"One zone per column, stated in the header with a long name; per-record zones in their own column with city names; formatting from Intl so daylight saving is right. See announcing time zones in data tables.
new Intl.DateTimeFormat(undefined, { timeZone: 'Asia/Singapore', timeZoneName: 'long' });
Date picker grid keyboard pattern
Permalink to "Date picker grid keyboard pattern"A dialog with a month heading and a grid of fully named day buttons; arrows, Home, End and Page keys; typed entry always available. See the date picker grid keyboard pattern.
<button tabindex="0" aria-label="Friday, 2 October 2026">2</button>
Formatting durations for screen readers
Permalink to "Formatting durations for screen readers"Compact visual forms with long-form text for assistive technology, ISO durations for machines, header units for single-unit columns. See formatting durations for screen readers.
new Intl.DurationFormat(undefined, { style: 'long' }).format({ hours: 1, minutes: 23 });
Accessible calendar and timeline views
Permalink to "Accessible calendar and timeline views"Structure under the visuals, events named with time and category, keyboard movement by day and period, non-drag editing and an agenda alternative. See accessible calendar and timeline views.
<td role="gridcell" aria-label="Friday 2 October, 3 events">…</td>
Cross-cutting concerns
Permalink to "Cross-cutting concerns"Locale. Date order (day-month or month-day), week start, 12- or 24-hour clocks and decimal separators all vary. Use Intl everywhere, and in typed inputs either follow the locale’s format or use ISO with a visible hint.
Consistency. A product that shows “2 Oct” in one table and “10/2” in another forces users to re-decode dates constantly. Choose formats per context in the design system and use them everywhere.
Exports. Exports should carry absolute, zone-qualified values, never relative text — see accessible CSV and Excel exports.
Filters. Date range filters must interpret dates in the zone the user sees, or results will be off by a day around midnight. See accessible date range filters.
Worked example: an incident timeline
Permalink to "Worked example: an incident timeline"An operations team reviews incidents in a timeline view: a horizontal axis of time with coloured bars for alerts, deploys and outages. An on-call engineer who uses NVDA could not use it at all. The redesign keeps the visual timeline and adds, beneath it, the same events as a table ordered by time, with columns “Start (UTC)”, “End (UTC)”, “Duration”, “Type” and “Summary”. Durations read as “23 minutes”; types are words, not colours; the table header says “UTC” once. In the visual timeline, each bar became a focusable element named “Outage, 14:02 to 14:25 UTC, 23 minutes, checkout API”, and arrow keys move between events in time order. The engineer now reviews incidents from the table and uses the timeline’s focusable bars when discussing them with sighted colleagues on a call.
Who is affected, and how
Permalink to "Who is affected, and how"| User group | Typical failure | Fix |
|---|---|---|
| Screen reader users | Durations read as clock times; calendars as jumbles | Long-form text; structure; agenda view |
| Keyboard users | Calendar-only pickers; drag-only scheduling | Typed entry; edit dialogs |
| Users in other zones | Ambiguous times | Zones in headers; viewer’s zone |
| Users with cognitive disabilities | Drifting relative times; abbreviations | Absolute dates beyond a week; expansions |
| Colour-blind users | Event categories by colour | Categories in words |
How a timestamp travels through the stack
Permalink to "How a timestamp travels through the stack"A single value — an invoice’s due date — passes through several layers before a user hears it, and each layer can lose accessibility:
Keep conversion and formatting in one shared utility, so that tables, exports, filters and charts all agree. When they disagree, users see one date in the grid and another in the export — a confusing bug for anyone, and very hard to diagnose for a screen reader user.
Measuring date and time quality
Permalink to "Measuring date and time quality"Audit a product’s time data with three quick checks: search the codebase for title= on time-like elements (each is a candidate for an inaccessible absolute time); list every date picker and confirm it accepts typed input; and listen to a table of durations with a screen reader to catch clock-style readings. Fixing those three classes of issue removes most of the problems this topic describes.
Design system integration
Permalink to "Design system integration"Provide Timestamp, Duration, DateField (with optional calendar), DateRangeField and calendar views as components that format with Intl, render accessible text alongside compact forms, and expose zone and precision as configuration. Column types for tables — datetime, date, duration — should drive formatting, header units and sorting together. Document formats per context (tables, feeds, headers) so every product uses the same ones.
Testing checklist
Permalink to "Testing checklist"Automated
Permalink to "Automated"Keyboard
Permalink to "Keyboard"Screen reader
Permalink to "Screen reader"FAQ
Permalink to "FAQ"Should I always show absolute times?
In tables and anywhere users compare or audit, yes. In feeds, relative is fine as long as absolute is available on demand.
Are native date inputs accessible?
They are improving, and they provide typed segments, but behaviour differs between browsers. Test them with your target screen readers before relying on them.
How do screen readers read ISO dates like 2026-10-02?
Usually as “2026 dash 10 dash 02” or similar. It is unambiguous but not pleasant; for display, use locale formats with month names in dense contexts where clarity matters.
Do I need an agenda view for every calendar?
For any calendar users rely on to find or manage events, yes. It is the most accessible view and often the fastest for everyone.
How do I handle dates without a year in tables?
Only omit the year when every date in the column is in the current year, and say so in the header (“Due (2026)”). Otherwise include it.
Should countdown timers be announced?
Not continuously. Announce thresholds that require action — “5 minutes until the session ends” — and provide a way to extend, as WCAG 2.2.1 requires for session timeouts.
Where should I start fixing time data in an existing product?
Start with date pickers, because they block tasks outright when inaccessible. Then fix durations and relative times in the most-used tables, and finally add agenda alternatives to calendars. Each step is independent and delivers value on its own.
Related
Permalink to "Related"- Accessible filtering & faceted search — date range filters
- Inline editing & form controls — date editors in cells
- Semantic HTML table construction — units and formats in headers
- Tooltips, popovers & overlays — revealing absolute times on demand
- Real-time data stream announcements — timestamps on live data