JavaScript Time Zones Without Surprises: UTC, IANA Names and Daylight Saving Time

A user in Paris schedules a video call for 9 a.m. A colleague in New York sees 3 a.m. rather than the expected 4 a.m. A reminder that worked last month fires an hour early. A server log contains a number that looks like a Unix timestamp but displays a date in 1970.

These problems come from confusing local clock readings, UTC instants or timestamp units. A date and time are not necessarily an unambiguous instant. Web applications need to distinguish a moment on the timeline from the local clock reading displayed to a person. This guide shows how to do that in JavaScript, how daylight saving time changes the answer, and what to test before publishing a scheduling feature.

For a visual companion, try Web2Generators’ free Universal Time Converter. It lets you compare named zones, investigate repeated or skipped local hours, inspect timestamps and share a conversion. Its launch article explains the tool’s key use cases.

An instant and a wall-clock time are different things

An instant is one point in time. For example, 2026-11-01T05:30:00Z is an ISO 8601 representation of a precise UTC instant. It does not depend on the viewer’s location.

A local time is what a clock says in a place, such as “9:00 a.m. in Paris on Thursday.” That description requires a calendar date and a time zone before you can determine its corresponding instant. The IANA identifier Europe/Paris is much more useful than an assumed offset of UTC+1, because Paris changes offset during the year.

Use the model that matches the product:

Product requirementRecommended data
“This webinar starts at one fixed moment.”UTC timestamp, optionally with an IANA zone for display
“Show the date when this order was placed.”UTC timestamp, formatted in the viewer’s zone
“This store opens at 09:00 every Monday in Paris.”Local time, day or recurrence rule, and Europe/Paris
“Schedule the first of next month at 08:00 in New York.”Calendar date and wall time, IANA zone, and an explicit rule for DST ambiguities

The last two cases cannot safely be implemented by adding a fixed number of milliseconds to a UTC timestamp. The interval from one local 9 a.m. to the next may be 23 or 25 hours near a daylight saving transition.

Store fixed events as UTC instants

For an appointment already resolved to a precise instant, send an ISO UTC timestamp through your API:

{
  "eventId": "demo-42",
  "startsAt": "2026-11-01T05:30:00Z",
  "eventTimeZone": "America/New_York"
}

The timestamp drives reminders, ordering and comparisons. The zone records how the event was organized, or which local time is meaningful to the organizer. It is not a substitute for the timestamp.

In JavaScript, parse the UTC timestamp and display it in an explicitly named zone:

const startsAt = new Date("2026-11-01T05:30:00Z");

const formatter = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  year: "numeric",
  month: "short",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short"
});

console.log(formatter.format(startsAt));
// Nov 1, 2026, 01:30 AM EDT (format varies by runtime/locale)

Intl.DateTimeFormat uses time-zone rules available in the JavaScript environment. Labels, punctuation and abbreviations can vary by locale and runtime. If you need a machine-readable value, keep the original ISO timestamp rather than parsing the localized display string back into a date.

To display the event in the user’s own system zone, omit timeZone or pass a user-selected IANA zone. An explicit preference is preferable when an account travels frequently or works across locations.

Why daylight saving creates two 1:30 a.m. times

New York moved its clocks back on November 1, 2026. On that day, the local time 1:30 a.m. occurs twice. Using the formatter defined above, compare these two UTC instants:

const first = new Date("2026-11-01T05:30:00Z");
const second = new Date("2026-11-01T06:30:00Z");

for (const instant of [first, second]) {
  console.log(formatter.format(instant));
}
// 1:30 a.m. EDT
// 1:30 a.m. EST

Both clock readings look like 1:30 a.m., but the instants are one hour apart. If the interface accepts “November 1 at 1:30 a.m. in New York,” it needs a policy: ask the user whether they mean the first or second occurrence, or apply a clearly documented default.

The reverse happens when clocks move forward. On March 8, 2026, 2:30 a.m. in New York did not exist. A scheduling form should reject that local input or explain its documented adjustment rather than silently guessing a different hour.

The Universal Time Converter is a quick way to inspect these exact cases before writing tests.

Do not parse a zone-free datetime as if it were UTC

This code is risky:

const appointment = new Date("2026-11-01T01:30:00");

As the MDN Date reference explains, without a Z or explicit offset, this date-time string is interpreted in the runtime’s local time zone. It does not specify America/New_York, and the ambiguous hour may be resolved according to local rules that your application’s users never selected.

Date-only strings behave differently: new Date("2026-11-01") is interpreted as midnight UTC. Do not use that parsing rule to infer how a string containing a time will behave.

Another tempting mistake is to append Z:

const wrong = new Date("2026-11-01T01:30:00Z");

Appending Z says that the input was already UTC. It does not convert a New York wall time to UTC. The result would refer to an entirely different instant.

For a wall-time scheduling form, store its calendar fields and IANA zone separately. Resolve them using a maintained time-zone-aware library or an appropriate Temporal implementation, and deliberately choose how to handle ambiguous and nonexistent local times. Browser and Node.js support for Temporal should be checked in your deployment targets rather than assumed universally available.

Compare two cities on the actual date

Suppose a New York coworker proposes 9:00 a.m. The corresponding Paris clock time is not always the same:

  • On January 15, 2026, 9:00 a.m. New York is 3:00 p.m. Paris.
  • On March 15, 2026, 9:00 a.m. New York is 2:00 p.m. Paris.

The countries change clocks on different weekends. Hardcoding a six-hour difference makes an apparently correct winter schedule wrong in March.

For team communications, send the UTC instant and the intended time-zone name, not only an ambiguous sentence such as “The release is at 9.”

Understand Unix seconds and JavaScript milliseconds

JavaScript’s Date.getTime() uses milliseconds since the Unix epoch. Many APIs document their timestamp fields in seconds. Mixing those units is a common source of dates around January 1970 or far in the future.

const instant = new Date("2026-11-01T05:30:00Z");
const unixMilliseconds = instant.getTime();
const unixSeconds = Math.floor(unixMilliseconds / 1000);

const fromSeconds = new Date(unixSeconds * 1000);
console.log(fromSeconds.toISOString());
// 2026-11-01T05:30:00.000Z

When documenting an API, use names such as createdAtIso, expiresAtUnixSeconds, or timestampMs instead of an ambiguous time field. Make your schema’s units explicit. A repeatable request prepared with the cURL Command Line Builder can help compare what an endpoint actually returns against its documentation.

Time zones are for dates, not performance benchmarks

Use Date to represent civil times and timestamps. Do not calculate the duration of a short operation by assuming that two displayed clock readings are reliable monotonic measurements.

For measuring how long code takes, use performance.now() rather than formatting calendar dates or subtracting wall-clock strings. The site’s article on the W3C High Resolution Time draft explains monotonic clocks, timeOrigin, and timer privacy considerations.

Test dates that are designed to break your assumptions

A timezone feature needs more than a test with today’s date. Add cases for:

  1. A normal winter date and a normal summer date in two named zones.
  2. A week when different regions switch daylight saving at different times.
  3. A skipped local hour during spring transition.
  4. Both occurrences of an ambiguous autumn hour.
  5. An event that crosses midnight or changes the calendar day for a participant.
  6. Unix seconds versus milliseconds, including malformed input.
  7. Different runtime time-zone databases, locale formats and server configurations.
  8. A recurring schedule that spans a change in UTC offset.

Use explicit expected instants in tests. Avoid assertions against one human-readable locale string when punctuation and abbreviations are not part of your product’s contract.

Premium is coming soon

An optional premium subscription to remove ads is planned. Subscriptions are not available yet.

You can keep using our free tools without an account.