Content

Date and time

Words for days, numerals for everything else, 24-hour by default. The system uses the user's locale; the rules are written so any locale lands legible.

Documentedby Derek Fidler

Principles

  • Medium is the default; reserve short numerical for accounting and reporting.“5 May 2026” never gets misread as month-day or day-month; “05/05/2026” can. Reach for d MMM yyyy in every product surface — table cells, sidebars, list rows, body copy, hero metrics. Short numerical is reserved for accounting + reporting contexts where line length is genuinely scarce or the output is machine-read: receipts, printed reports, XLS exports. Logs and audit trails use ISO 8601, never short numerical.
  • Never break dates or periods across lines. “27 Jan” is one token; “25 – 28 Jan” is one token. Wrapping the day onto one line and the month onto the next reads as two separate dates and forces a re-parse. The Table component defaults noWrap: true precisely because of this rule; in body copy use a non-breaking space inside short formats. Truncate with an ellipsis before you wrap.
  • 24-hour clock by default.Across all our European markets and inside the product UI everywhere — including the US locale once it ships. The 12-hour clock with am/pm is only used in US marketing copy and system surfaces that mirror the user's OS preference.
  • Relative dates close in, absolute dates further out. “2 minutes ago” is friendlier than 14:32; “5 May 2025” is friendlier than “372 days ago”. The crossover is 7 days.
  • Always disambiguate time zoneswhen a number could mean two different moments — meeting times, deadlines, scheduled jobs. Append a short zone tag (CET, BST, UTC) or convert to the user's zone with a tooltip showing the original.

Default formats

These are the formats source copy is written in (en-GB) before localisation. Renderers swap them per locale at runtime.

Dates

  • Long

    5 May 2026

    Default for body copy, headings, and any place a date is read as a sentence. No commas, no leading zero on the day.

    d MMMM yyyy

  • Medium · default

    5 May 2026

    The default everywhere. Three-letter month locks the day-or-month order at a glance, so it reads correctly in every locale. Reach for it in table cells, sidebars, list rows, body copy, and hero metrics — anywhere a date is meant to be read by a person.

    d MMM yyyy

  • Short numerical

    05/05/2026

    Accounting and reporting only — receipts, printed reports, XLS / CSV exports. Never in product UI: a customer reading 05/05/2026 has to guess the month-day order, and that's a receipt-only tax we don't pay on screen. Always day-first; locale-flips to MM/DD/YYYY in the US.

    dd/MM/yyyy

  • ISO 8601

    2026-05-05

    Technical surfaces only — logs, audit trails, API responses, anything sortable. Never customer-facing.

    yyyy-MM-dd

  • Day of week

    Tuesday, 5 May

    Calendar surfaces and meeting confirmations. Drops the year when it's the current year.

    EEEE, d MMMM

Times

  • Time

    14:30

    Default everywhere. Two digits with a colon. No leading 'at'.

    HH:mm

  • Time with seconds

    14:30:42

    Logs, transaction timestamps, anywhere precision matters.

    HH:mm:ss

  • Date and time

    5 May 2026, 14:30

    Default joined format. Comma between, never the word 'at'.

    d MMM yyyy, HH:mm

  • Time with zone

    14:30 CET

    When the audience spans time zones. Three-letter zone abbreviation in caps.

    HH:mm zzz

  • 12-hour (US only)

    2:30 pm

    US-localised surfaces only. Lowercase 'am' / 'pm' with a non-breaking space, no periods.

    h:mm a

Relative dates

A short scale, ordered from now to far past. The product reaches for the relative form when it's shorter than the absolute one and the reader doesn't need precision.

  • < 1 minute

    Just now

    Just now

  • 1 – 59 minutes

    N minute(s) ago

    14 minutes ago

  • 1 – 23 hours

    N hour(s) ago

    6 hours ago

  • Yesterday

    Yesterday at HH:mm

    Yesterday at 18:42

  • 2 – 6 days

    EEEE at HH:mm

    Friday at 09:15

  • 7+ days, this year

    d MMM

    3 May

  • Older

    d MMM yyyy

    3 May 2024

Hover for the absolute, always

Wrap relative dates in a <time> element with the ISO datetime in the dateTime attribute, and surface the absolute on hover. Audit trails, dispute timelines, and anything legal needs the exact moment one click away.

Time zones

The backend stores everything in UTC. The interface displays in the user's local zone. The friction is at the boundary — exports, scheduled jobs, audit trails, any time the reader and the writer aren't in the same room.

  • Display in the user's zoneby default — that's what they expect. Detect from the browser, fall back to the merchant's configured zone, fall back to UTC.
  • Label the zone when the audience is mixed (an event time shared with merchants in different countries, a deadline that ends at midnight somewhere). Three-letter caps: CET, CEST, BST, EST, UTC.
  • Use UTC in exports and APIs. A CSV that opens in three different cities should read the same in all three. Localisation happens at the rendering layer, not in the data.
  • Don't mix forms in one view. A page that shows “14:30 CET” in one row and “14:30” in another asks the reader to do work. Pick one and stay with it.

Date ranges

Yes

  • 5–10 May 2026
  • 5 May – 10 June 2026
  • 5 May 2025 – 10 June 2026
  • 09:00–17:00 CET

No

  • 5 - 10 May 2026 (hyphen, spaced)
  • May 5 – June 10, 2026 (US format in en-GB)
  • 5 May 2026 to 10 June 2026 (uses a word; use an en dash)
  • 9:00am–5:00pm (12h in product UI)

Use an en dash (–) for ranges, never a hyphen (-) or the word “to”. Drop the duplicate month or year if both ends share it: “5–10 May 2026”, not “5 May 2026 – 10 May 2026”.

Per-locale formats

The runtime renderer picks the format based on the user's locale. Source copy is en-GB. The values below are what each locale ends up rendering for the medium-length date and the time.

MarketLocaleDateTimeWeek starts
United Kingdomen-GB5 May 202614:30Monday
Denmarkda-DK5. maj 202614.30Monday
Finlandfi-FI5.5.202614.30Monday
Germanyde-DE5. Mai 202614:30Monday
Francefr-FR5 mai 202614h30Monday
Italyit-IT5 mag 202614:30Monday
Netherlandsnl-NL5 mei 202614:30Monday
United Statesen-USMay 5, 20262:30 pmSunday

Three things to watch

Finland uses a period as the date separator (5.5.2026), Danish uses a period in the time (14.30), and the US flips date order and clock format. Don't normalise these — they're what users in those markets expect.

In code

Always format through Intl.DateTimeFormat with an explicit locale. Never concatenate strings. Never trust the environment's default locale — make it visible.

ts

// ✓ Pass the user's locale and the format spec
const fmt = new Intl.DateTimeFormat("en-GB", {
  day: "numeric",
  month: "long",
  year: "numeric",
});
fmt.format(new Date("2026-05-05")); // "5 May 2026"

// ✓ For the time, opt explicitly into 24-hour
new Intl.DateTimeFormat("en-GB", {
  hour: "2-digit",
  minute: "2-digit",
  hour12: false,
}).format(new Date()); // "14:30"

// ✗ Don't hand-roll
const month = ["Jan", "Feb", "Mar", ...][d.getMonth()]; // breaks every locale
return `${d.getDate()} ${month} ${d.getFullYear()}`;

Page history

1 revision
  1. Documented
    Derek Fidler@derekfidler

    First documented version. Default formats, the relative-date scale, time-zone disambiguation, and locale exceptions for the eight markets.