Unix Timestamp in TypeScript

TypeScript shares JavaScript's runtime behaviour, but the type system lets you stop seconds and milliseconds from being confused at compile time — worth doing in any codebase that touches both.

Need to convert one value rather than write code? Use the interactive converter — it handles every unit and time zone without leaving your browser.

Branded types for units

// Make the unit part of the type so they cannot be mixed up.
type EpochSeconds = number & { readonly __brand: 'EpochSeconds' };
type EpochMillis  = number & { readonly __brand: 'EpochMillis' };

const nowMillis = (): EpochMillis => Date.now() as EpochMillis;
const toSeconds = (ms: EpochMillis): EpochSeconds =>
  Math.floor(ms / 1000) as EpochSeconds;

// const bad: EpochSeconds = nowMillis();   // compile error — good.

Timestamp to date

const fromEpochSeconds = (ts: EpochSeconds): Date => new Date(ts * 1000);

const format = (d: Date, timeZone: string): string =>
  new Intl.DateTimeFormat('en-GB', {
    timeZone, dateStyle: 'medium', timeStyle: 'long',
  }).format(d);

Parsing API responses safely

interface ApiEvent {
  readonly created_at_ms: number;   // name the unit in the field itself
}

function eventDate(e: ApiEvent): Date {
  const d = new Date(e.created_at_ms);
  if (Number.isNaN(d.getTime())) throw new Error('invalid timestamp');
  return d;
}

Nanoseconds with BigInt

const ns = 1710050400000000000n;
const ms = Number(ns / 1_000_000n);      // narrow only after dividing
new Date(ms).toISOString();

Pitfalls specific to TypeScript

  • A bare number type gives you no protection at all — the branded-type pattern above costs nothing at runtime and catches the classic bug at compile time.
  • new Date(invalid) does not throw; it produces an Invalid Date whose getTime() is NaN. Check explicitly.

Rules that apply in every language

  1. Store UTC, display local. Keep the instant in UTC everywhere in your system and convert only at the point a human reads it.
  2. Name the unit in the identifier. expires_at_ms rather than expires_at costs nothing and prevents the single most common timestamp bug.
  3. Never trust a client clock. Stamp anything security-relevant on the server. See the note on clock skew.
  4. Use 64-bit time. Anything still storing seconds in a signed 32-bit field breaks in January 2038 — see the Year 2038 problem.

The same task in other languages

Frequently asked questions

How do I get the current Unix timestamp in TypeScript?

Use the snippet in the "Current Unix timestamp" section above. TypeScript works in integer milliseconds natively, so converting to another unit is a multiplication or an integer division away.

How do I convert a Unix timestamp to a date in TypeScript?

The "Timestamp to date" snippet above shows the idiomatic approach, including how to render the result in a specific time zone rather than whatever zone the machine happens to be set to.

Does TypeScript handle time zones and daylight saving correctly?

Yes, provided you pass an explicit zone rather than relying on the system default. The gotchas listed on this page cover the specific ways TypeScript makes that easy to get wrong.