// stored: "2024-01-15T20:30:00Z" (UTC)
const fmt = new Intl.DateTimeFormat("en-GB", {
timeZone: "Asia/Tokyo",
dateStyle: "medium",
timeStyle: "short",
});
fmt.format(new Date("2024-01-15T20:30:00Z")); // "16 Jan 2024, 05:30"
For moments in time, store UTC and convert to the user's time zone only when you display it.
Storing:
- Save timestamps in UTC as ISO 8601 strings (
2024-01-15T20:30:00Z) or as epoch milliseconds. TheZmeans UTC. - Never store a local time without its offset or zone. "2024-01-15 20:30" alone is ambiguous.
Displaying:
- Format with
Intl.DateTimeFormatand the user'stimeZone, as in the code. The same instant is the evening of 15 January in London and the morning of 16 January in Tokyo. - Get the browser's zone with
Intl.DateTimeFormat().resolvedOptions().timeZone, or use a zone the user picked in settings. - Use IANA zone names like
Africa/Cairo, not fixed offsets like+02:00. Offsets change with daylight saving time.
Exceptions to "just store UTC":
- Future local events, like "the meeting is at 9:00 in Cairo every Monday". Store the local time and the zone name. Governments change DST rules, and a saved UTC value would then show the wrong local time.
- Dates without a time, like a birthday. Store
"1990-05-20"as a plain date. Turned into a UTC midnight, it can show as the day before in the Americas.
- Store moments in time in UTC, as ISO 8601 strings or epoch milliseconds.
- Convert to the user's time zone only for display, with
Intl.DateTimeFormatandtimeZone. - Use IANA zone names like
Africa/Cairo, not fixed offsets. - For future local events, store the local time and the zone name.
- Store dates without a time, like birthdays, as plain dates.