Timestamps and ISO 8601 Explained
How to handle dates and times across systems without timezone disasters.
Timezone bugs cost engineering teams thousands of hours per year. The fix is consistent representation, and ISO 8601 is the standard.
ISO 8601 Format
2026-04-30T15:32:18Z UTC
2026-04-30T15:32:18+09:00 With offset
2026-04-30T15:32:18.123Z With ms
20260430T153218Z Compact (avoid)
The T separates date and time. Z means Zulu = UTC. Always use the extended form with hyphens and colons.
Unix Timestamps
1745939938 seconds since 1970-01-01 UTC
1745939938123 milliseconds (JavaScript)
1745939938123456 microseconds (databases)
Unix time is timezone-free by definition — it counts seconds, not clock ticks. Display in any zone afterward.
The Three Sins
1. Storing local time without offset: 2026-04-30 15:32:18 is ambiguous.
2. Mixing local and UTC: pick one for storage; convert at the edges.
3. Trusting client clocks: laptops drift; assume the server clock is truth.
Storing Times
- Database: TIMESTAMP WITH TIME ZONE (Postgres) or BIGINT for Unix ms (cross-DB).
- JSON: ISO 8601 string with explicit offset or Z.
- Logs: ISO 8601 with milliseconds, always Zulu.
Date-Only vs Datetime
For dates without times (birthdays, holidays), store as plain YYYY-MM-DD and never apply timezone. Adding a default time of midnight UTC has caused January 1 to display as December 31 in negative offsets too many times to count.
Time Math
Use a library: date-fns, Luxon, day.js. Native Date arithmetic is full of edge cases (DST transitions, month boundaries).
Display
Convert to user's local timezone in the UI layer, never in storage. Use Intl.DateTimeFormat for locale-aware rendering:
new Intl.DateTimeFormat('ko-KR', { dateStyle: 'long', timeStyle: 'short' }).format(date);
For converting between formats see the [Timestamp Converter](https://sdk.is/timestamp-converter).