How to Handle Timezones and UTC in JavaScript

Timezones trip up almost every developer at some point, mostly because Date objects don't actually "have" a timezone — internally, every Date is just a millisecond count since the Unix epoch (UTC). Timezones only enter the picture when you format or parse a date. Once you internalize that, most of the confusion goes away.

1. Detect the visitor's timezone

No library needed — the browser already knows:

const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; // e.g. "America/Argentina/Buenos_Aires"

2. Format a date in any timezone

Pass a timeZone to Intl.DateTimeFormat and it handles the conversion, including daylight saving, for you:

new Intl.DateTimeFormat("en-US", { timeZone: "America/New_York", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: true, }).format(new Date()); // "3:45:12 PM"

3. Get a timezone's current UTC offset

Useful for labels like "UTC-5" or converting between two zones:

function getOffsetLabel(timeZone, date) { const parts = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "shortOffset", }).formatToParts(date); return parts.find(p => p.type === "timeZoneName").value; // "GMT-5" }

4. Convert a wall-clock time from one zone to another

This is the part people usually reach for a library for, but it's doable with just the offset above: build a UTC timestamp from the target date/time, then subtract the source zone's offset.

function convert(zoneA, hh, mm, refDate) { // Y/M/D "today" as seen in zoneA const parts = new Intl.DateTimeFormat("en-CA", { timeZone: zoneA, year: "numeric", month: "2-digit", day: "2-digit", }).formatToParts(refDate); const get = t => +parts.find(p => p.type === t).value; const offsetMin = /* parse "-300" from getOffsetLabel(zoneA, refDate) */ -300; const utcMs = Date.UTC(get("year"), get("month") - 1, get("day"), hh, mm) - offsetMin * 60000; return new Date(utcMs); // format this in zoneB to get the converted time }

This is exactly the technique behind our own Meeting Planner tool — worth a look if you'd rather not write the conversion logic yourself.

5. The one rule that prevents most bugs

Store and transmit dates in UTC (ISO 8601, e.g. 2026-07-22T14:30:00Z), and only convert to a local timezone at the last moment — when you render it for a human. Databases, APIs, and logs should never store "local time" without an offset attached; that ambiguity is where most timezone bugs come from.

Do you need a library at all?

For most apps, no — Intl and Date cover formatting, conversion, and offset lookups natively in every modern browser and in Node.js. Reach for a library like date-fns-tz or Luxon only if you need heavier date-math (adding business days, recurring events, etc.) — not just timezone display.

More Tools & Guides