Timestamp format

Last updated: Apr 30, 2026


Rationale

All timestamps produced and stored across our infrastructure, applications, logs, and databases conform to a single canonical format:

YYYY-MM-DDTHH:MM:SS.sssZ

This is the strict intersection of ISO 8601-1:2019, RFC 3339, and ECMAScript's Date.prototype.toISOString(). Any conformant parser for any of these standards accepts it, and any conformant toISOString() generator produces it.

The main reasons why we chose this exact format over the looser variants permitted by each standard are:

  • It guarantees a single canonical string per UTC instant, so two services serializing the same instant produce byte-identical strings.
  • Lexicographic comparison matches chronological order, so timestamps can be sorted as plain strings without parsing.
  • It enables byte-stable hashing and signing of timestamped payloads.
  • It makes log correlation across services unambiguous, with no reliance on local-time conventions or implicit zone assumptions.
  • Its alignment with UTC removes any need for timezone arithmetic at read time.

Alternatives

Below are the other approaches we considered for storing and exchanging timestamps, ordered from most to least interesting based on our specific needs.

Unix timestamps

Storing instants as integer seconds (or milliseconds, microseconds, nanoseconds) since the 1970 epoch, e.g. 1745939025123.

  • They are compact and unambiguously UTC by definition.
  • They sort correctly when compared as integers.
  • They are not human-readable: logs, database dumps, and API responses require conversion before an engineer can reason about them.
  • The unit (s, ms, µs, ns) is not self-describing, so every consumer must agree out of band on the resolution. Mistakes are silent and produce off-by-1000 bugs.
  • Storing them as 32-bit signed integers exposes us to the Year 2038 problem; storing them as 64-bit avoids it but loses interoperability with systems that still default to 32-bit.
  • They lose context that the canonical string form preserves (calendar date, time of day), which matters for partitioning, retention, and audit reads.

Locale-formatted strings

Human-friendly forms tied to a regional convention, e.g. 30/04/2026 09:23:45 or Apr 30, 2026 9:23 AM.

  • They are familiar to humans within a single locale.
  • They are ambiguous across locales: 04/05/2026 is April 5 in the United States and May 4 in most of the rest of the world.
  • Lexicographic order does not match chronological order, so they cannot be sorted as plain strings.
  • There is no standard parser, so every consumer reimplements validation and timezone handling.
  • They are unsuitable for machine-to-machine interchange in JSON, OpenAPI, or JSON Schema contexts.

Naive datetimes (no zone designator)

A date-and-time string without any zone information, e.g. 2026-04-30T14:23:45.123.

  • It looks clean and is easy to type.
  • The same string denotes different instants depending on the implicit zone the reader assumes.
  • It breaks log correlation across regions and across daylight-saving transitions.
  • It forces every producer and consumer to track the implicit zone out of band, which is exactly the kind of hidden coupling that causes incidents.

A timezone other than UTC

Anchoring timestamps to any zone other than UTC, whether each producing system uses its own local zone (e.g. 2026-04-30T09:23:45.123-05:00) or the whole platform standardizes on a single regional zone like America/Bogota.

  • It matches the wall clock visible to engineers in that zone.
  • Two producers in different zones — or the same producer before and after a daylight-saving transition — emit different strings for the same instant, breaking byte-stable hashing, deduplication, and lexicographic ordering.
  • Daylight-saving transitions create ambiguous and skipped wall-clock times that have no canonical representation.
  • Cloud services, databases, container runtimes, and standards default to UTC; picking a different anchor means fighting that default in every layer.
  • Foreign consumers of our APIs must learn and remember our zone to interpret responses, which is friction we have no reason to impose.

Specification

Rules

  • Year (YYYY): exactly 4 decimal digits, range 00019999. No sign, no extended years.
  • Date separator: literal hyphen - (U+002D).
  • Date/time separator: literal uppercase T (U+0054). Space and lowercase t are not permitted.
  • Time separator: literal colon : (U+003A).
  • Time fields: HH in 0023, MM in 0059, SS in 0059. Leap seconds (:60) are not permitted.
  • Fractional seconds: literal dot . followed by exactly 3 decimal digits (millisecond precision), left-padded with zeros. Example: 40 ms is written .040, not .40.
  • Time zone: literal uppercase Z (U+005A), denoting UTC. Lowercase z, numeric offsets, and omitted zone designators are not permitted.
  • Total length: exactly 24 characters.

Validation regex

^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$

The regex does not validate day-of-month against calendar month or leap years; that requires a calendar-aware check.

Examples

Valid:

  • 2026-04-30T14:23:45.123Z
  • 2026-01-01T00:00:00.000Z
  • 2026-12-31T23:59:59.999Z
  • 2026-07-15T08:05:02.040Z

Invalid (each violates one of the rules above):

  • 2026-04-30T14:23:45Z — missing fractional seconds.
  • 2026-04-30T14:23:45.1Z — fractional width is not 3.
  • 2026-04-30T14:23:45.40Z — fractional width is not 3.
  • 2026-04-30T14:23:45.123456Z — fractional width is not 3.
  • 2026-04-30T14:23:45.123 — missing zone designator.
  • 2026-04-30T14:23:45.123z — lowercase zone designator.
  • 2026-04-30T14:23:45.123+00:00 — numeric offset.
  • 2026-04-30T14:23:45.123-00:00 — numeric offset.
  • 2026-04-30 14:23:45.123Z — space separator.
  • 2026-04-30t14:23:45.123Z — lowercase date/time separator.
  • 20260430T142345.123Z — basic (compact) form.
  • 2026-04-30T24:00:00.000Z — hour 24.
  • 2026-04-30T14:23:60.000Z — leap second.
  • 2026-02-30T14:23:45.123Z — February 30 is not a real date.
  • 26-04-30T14:23:45.123Z — two-digit year.
  • +2026-04-30T14:23:45.123Z — signed year.

Usage

This format is applied across the platform in the following ways:

  • API responses serialize all temporal fields with it.
  • Database records store all temporal data with it.
  • Application and audit logs emit timestamps with it.
  • The SIEM ingests and correlates security events using it.

For convenience, the platform's user interface displays timestamps in the user's local timezone, but all underlying data is stored and processed in UTC under this format. See Standard timezone for the broader UTC policy this format enforces.

On this page