DEV Community

Dev Nestio
Dev Nestio

Posted on • Originally published at devnestio.pages.dev

I Built a Browser-Only Timestamp Converter — Epoch ↔ Date, 30+ Timezones, DST-Aware, 124 Tests

Unix timestamps are everywhere: API responses, log files, database columns, JWT iat/exp fields. But 1735689600 does not tell you much until you decode it. I built a Timestamp Converter to do that instantly — in the browser, no server, no libraries.


What it does

  • Timestamp → Date: paste any Unix timestamp (seconds or milliseconds, auto-detected) and get it formatted in 10+ representations
  • Date → Timestamp: type a date string (2025-01-01 09:00:00, ISO 8601, YYYY/MM/DD) and get the epoch back
  • Live clock: current Unix time in seconds, milliseconds, and ISO 8601, updated every second
  • 30+ timezones: pick any IANA timezone for both input and output
  • DST-aware: New York gives -05:00 in January and -04:00 in July, automatically
  • Relative time: "2 years ago", "in 3 months", plus the exact breakdown (730d 12h 30m)
  • Reference grid: click Y2K, Y2K38, Jan 1 2025, or "1 day from now" to analyze instantly
  • Click-to-copy: every output field copies to clipboard on click

Output formats for every conversion

Format Example
Unix (seconds) 1735689600
Unix (milliseconds) 1735689600000
ISO 8601 (UTC) 2025-01-01T00:00:00.000Z
ISO 8601 (local tz) 2025-01-01T09:00:00+09:00
RFC 2822 Wed, 01 Jan 2025 00:00:00 GMT
Local (readable) Wednesday, January 1, 2025 at 09:00:00
Short UTC 2025-01-01 00:00:00
Short (local tz) 2025-01-01 09:00:00
Day of Week Wednesday
Week Number W01 of 2025

The DST problem (and how it is solved)

The hardest part of a timestamp tool is not the easy direction (timestamp → UTC date — that is just new Date(ts * 1000)). The hard part is local time → UTC when the local time is in a specific timezone.

JavaScript's Date constructor does not accept a timezone argument. new Date('2025-01-01T09:00:00') always parses in the browser's local timezone, not the one the user selected.

The solution: the Intl.DateTimeFormat API, which does understand IANA timezones.

function getTZOffsetMs(date, tz) {
  const fmt = {
    timeZone: 'UTC',
    year: 'numeric', month: '2-digit', day: '2-digit',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
    hour12: false
  };
  const utcStr = new Intl.DateTimeFormat('en-CA', fmt).format(date);
  const localStr = new Intl.DateTimeFormat('en-CA', { ...fmt, timeZone: tz }).format(date);
  return parseIntlDate(localStr) - parseIntlDate(utcStr);
}
Enter fullscreen mode Exit fullscreen mode

This gives us the offset in milliseconds for any timezone at any point in time — DST transitions included. Then to convert a local time to UTC:

function localToUTC(isoLocal, tz) {
  const tentative = new Date(isoLocal + 'Z'); // parse as UTC first
  const offset = getTZOffsetMs(tentative, tz);
  return new Date(tentative.getTime() - offset);
}
Enter fullscreen mode Exit fullscreen mode

The result: Tokyo 09:00 → UTC 00:00. New York 00:00 in January → UTC 05:00. New York 00:00 in July → UTC 04:00.


Auto-detecting seconds vs. milliseconds

A common source of confusion: is 1735689600 seconds or milliseconds? The heuristic: if |value| > 1e10, treat it as milliseconds; otherwise seconds.

function detectUnit(raw) {
  if (raw.trim() === '\) return null;
  const n = Number(raw);
  if (isNaN(n)) return null;
  return Math.abs(n) > 1e10 ? 'ms' : 's';
}
Enter fullscreen mode Exit fullscreen mode

Relative time without a library

No moment.js, no date-fns. Just math:

function relativeTime(ms) {
  const diff = ms - Date.now();
  const abs = Math.abs(diff);
  const future = diff > 0;
  const units = [
    { name: 'year',   ms: 365.25 * 24 * 3600 * 1000 },
    { name: 'month',  ms: 30.44  * 24 * 3600 * 1000 },
    { name: 'week',   ms: 7      * 24 * 3600 * 1000 },
    { name: 'day',    ms: 24     * 3600 * 1000 },
    { name: 'hour',   ms: 3600   * 1000 },
    { name: 'minute', ms: 60     * 1000 },
    { name: 'second', ms: 1000 }
  ];
  for (const u of units) {
    const n = Math.floor(abs / u.ms);
    if (n >= 1) {
      const label = n === 1 ? u.name : u.name + 's';
      return future ? `in ${n} ${label}` : `${n} ${label} ago`;
    }
  }
  return 'just now';
}
Enter fullscreen mode Exit fullscreen mode

124 tests, no framework

All tests use Node.js assert. Coverage includes DST transitions, half-hour offsets (Kolkata +05:30), negative timestamps (before 1970), ISO 8601 week numbers, and date↔timestamp round-trips.

$ node test/test.js

Results: 124 passed, 0 failed
Total: 124 tests
Enter fullscreen mode Exit fullscreen mode

Try it

Live tool: https://devnestio.pages.dev/timestamp-converter/

All tools: https://devnestio.pages.dev/


Built with vanilla JS. 124 tests. Zero dependencies. DST-aware.

Top comments (0)