Date Formatting Cheat Sheet — ISO 8601, RFC 3339, and Format Tokens Across Languages
Dates look simple until you have to move them between a browser in Mumbai, a server in Frankfurt, and a database that thinks in UTC. This cheat sheet covers the one format every developer should default to — ISO 8601 — plus its stricter cousin RFC 3339, the neat trick that makes ISO dates sortable as plain strings, the golden rule of storage vs display, and a side-by-side reference of format tokens in JavaScript, Python, and SQL.
Anatomy of an ISO 8601 Timestamp
ISO 8601 arranges every component from largest to smallest — year, month, day, hour, minute, second — which is exactly what makes it unambiguous and sortable. A full timestamp breaks down like this:
2026-07-16T14:30:05.250+05:30 └──┬─────┘│└──┬──────────┘└─┬──┘ date T time offset 2026-07-16 date part: YYYY-MM-DD (always zero-padded) T literal separator between date and time 14:30:05 time part: HH:mm:ss (24-hour clock) .250 optional fractional seconds +05:30 UTC offset (IST) — or "Z" meaning UTC itself 2026-07-16T09:00:05Z same instant, expressed in UTC 2026-07-16 date only — also valid ISO 8601 14:30:05 time only — also valid ISO 8601
- Z stands for "Zulu time" and means UTC — a zero offset.
- +05:30 means the local clock is 5 hours 30 minutes ahead of UTC (India Standard Time).
- A timestamp without a Z or an offset is a "local" time — its actual instant is ambiguous, which causes real bugs. Always include the offset in data you exchange.
RFC 3339: The Strict Internet Profile
ISO 8601 is a large standard that also permits exotic forms — week dates (2026-W29-4), ordinal dates (2026-197), and timestamps with no offset at all. RFC 3339 is the subset used on the internet: it requires a complete date and time with an explicit offset, and drops the exotic variants. When an API says "ISO 8601", it almost always means RFC 3339 in practice.
- Every RFC 3339 timestamp is valid ISO 8601; the reverse is not true.
- RFC 3339 allows a space instead of
Tas a readability concession — but for maximum compatibility, always emit theT. - JSON Schema's
date-timeformat, OpenAPI, and Kubernetes manifests all specify RFC 3339.
The Superpower: ISO Dates Sort Correctly as Strings
Because components run from largest to smallest with fixed zero-padded widths, lexicographic (alphabetical) order equals chronological order — as long as all values share the same timezone (use UTC). You can sort ISO timestamps with a plain string sort, no date parsing needed:
// String sort = chronological sort for ISO 8601 UTC strings const events = [ '2026-07-16T09:00:00Z', '2025-12-31T23:59:59Z', '2026-01-05T08:15:00Z', ]; events.sort(); // ['2025-12-31...', '2026-01-05...', '2026-07-16...'] // This is why ISO dates work in filenames, S3 keys, and log lines: backup-2026-07-16.sql.gz // ls sorts these chronologically backup-2026-07-17.sql.gz // dd/mm/yyyy fails the same test: ['16/07/2026', '05/01/2026'].sort() // → ['05/01/2026', '16/07/2026'] only by luck — compare // ['02/01/2026', '15/12/2025'] which sorts WRONG
Storage vs Display: Never Store dd/mm/yyyy
The golden rule: store in one canonical machine format, format for humans only at the last moment. Locale formats like 16/07/2026 (India, UK) and 7/16/2026 (US) are display concerns. Stored, they are ambiguous — is 03/04/2026 the 3rd of April or the 4th of March? — and unsortable.
- Store: UTC ISO 8601 strings, or native
TIMESTAMP/DATETIMEcolumns, or Unix epoch integers. - Transmit: RFC 3339 strings in JSON (
"2026-07-16T09:00:00Z"). - Display: convert to the user's locale and timezone in the UI layer.
// JavaScript: locale display with Intl (no library needed)
const d = new Date('2026-07-16T09:00:00Z');
new Intl.DateTimeFormat('en-IN', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Asia/Kolkata',
}).format(d);
// → "16 Jul 2026, 2:30 pm"
new Intl.DateTimeFormat('en-US', { dateStyle: 'short' }).format(d);
// → "7/16/26" (same stored value, different display)Format Token Cheat Sheet: JavaScript vs Python vs SQL
Every ecosystem invented its own placeholder tokens, and mixing them up is a classic bug (Python's %m is month, but many JS libraries use mmfor minutes). Here is the mapping for the most common pieces:
Part Python MySQL PostgreSQL Example
strftime DATE_FORMAT to_char
------------------------------------------------------------------
Year (4) %Y %Y YYYY 2026
Year (2) %y %y YY 26
Month (01) %m %m MM 07
Month name %B %M Month July
Day (01) %d %d DD 16
Hour 24h %H %H HH24 14
Hour 12h %I %h HH12 02
Minute %M %i MI 30
Second %S %s SS 05
AM/PM %p %p AM PM
Weekday %A %W Day Thursday
Python: dt.strftime('%Y-%m-%d %H:%M:%S')
MySQL: DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s')
PostgreSQL: to_char(created_at, 'YYYY-MM-DD HH24:MI:SS')
JavaScript: no tokens built in — use date.toISOString() for
machines and Intl.DateTimeFormat for humansWatch the traps: MySQL uses %i for minutes (not %M, which is month name), and PostgreSQL's MM is month while MI is minutes. When output looks right but values are subtly wrong — minutes showing as months — a token mix-up is almost always the cause. To sanity-check a conversion, run the same instant through the timestamp converter and compare.
Quick Rules to Remember
- Default to UTC ISO 8601 / RFC 3339 for storage, logs, filenames, and APIs.
- Always include the Z or offset — a timestamp without one is a bug waiting to happen.
- Format for locale only at display time, using
Intl.DateTimeFormatin JS orbabel/ICU in Python. - Never parse dd/mm/yyyy or mm/dd/yyyy from user input without knowing the locale explicitly.
- Zero-pad everything —
2026-7-16is not valid ISO 8601 and breaks string sorting.
Frequently Asked Questions
ISO 8601 is the international standard for writing dates and times: YYYY-MM-DD for dates and YYYY-MM-DDTHH:mm:ssZ for timestamps. Components go from largest to smallest (year first), the letter T separates date from time, and Z or a numeric offset like +05:30 indicates the timezone.
RFC 3339 is a stricter internet profile of ISO 8601. It requires the full date and time with an explicit timezone offset, and disallows loose ISO variants like week dates, ordinal dates, or omitting the offset. Every valid RFC 3339 timestamp is valid ISO 8601, but not the reverse.
dd/mm/yyyy is ambiguous (03/04/2026 is 3 April in India but 4 March in the US), and it does not sort correctly as a string. Store dates in ISO 8601 (YYYY-MM-DD) or as UTC timestamps, then format them per locale only at display time using Intl.DateTimeFormat or equivalent.