skip to main content

-- Convert a Unix timestamp into a readable date, both ways and across seven time zones --

How to use it

Paste whatever you have: a timestamp in seconds, in milliseconds, or an ISO 8601 date. The tool works out which one it is and shows all three forms, plus the local time in seven zones.

Seconds or milliseconds?

Nothing distinguishes the two formats — they are both integers. The only reliable heuristic is the number of digits.

ValueDigitsRead asResulting date
170000000010seconds14 November 2023
170000000012313milliseconds14 November 2023
170000000010milliseconds?20 January 1970

A timestamp in seconds will not reach twelve digits before the year 33,658, so the threshold is unambiguous in practice.

This is the most common mistake with Date in JavaScript, whose constructor expects milliseconds:

// an API returns seconds, like most Unix APIs
const seconds = 1_700_000_000;
 
new Date(seconds); // 20 January 1970 — 1.7 million seconds after the epoch
new Date(seconds * 1000); // 14 November 2023

A zone offset is not a constant

Europe/Paris is +01:00 in winter and +02:00 in summer. Storing an offset in the database instead of the zone name therefore gives the wrong time for half the year.

// wrong: the offset is frozen
const offsetHours = 1;
 
// right: the zone, and the library applies that year's rule
new Intl.DateTimeFormat("en-GB", {
  dateStyle: "short",
  timeStyle: "long",
  timeZone: "Europe/Paris",
}).format(date);

What to store

Always an absolute instant — a Unix timestamp, or ISO 8601 in UTC (…Z). A local date with no zone is ambiguous, and becomes irrecoverably so the moment it crosses a border.

// ambiguous: 14 November at 22:13 in which zone?
"2023-11-14T22:13:20";
 
// unambiguous
"2023-11-14T22:13:20Z";
"2023-11-14T23:13:20+01:00"; // the same instant

Two legitimate exceptions: a birthday and an alarm time are local data. Converting them to UTC breaks their meaning as soon as the user changes zone.

Computing a distance

const parts = new Intl.RelativeTimeFormat("en-GB", {
  numeric: "auto",
}).format(-3, "day");
// "3 days ago"

Intl.RelativeTimeFormat handles the wording and the plural; it does not pick the unit. Deciding that a gap of 90,000 seconds should be expressed in days rather than seconds is up to you.

topics covered

related reading