Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Why Your Time Zone Conversion Is Off by an Hour (DST Edge Cases)

You convert 9:00 AM New York time to Karachi time. It works in July. In December, your users tell you it's an hour off. Nothing is wrong with your math. The bug is an assumption: that the gap between two time zones is c

You convert 9:00 AM New York time to Karachi time. It works in July. In December, your users tell you it's an hour off.

Nothing is wrong with your math. The bug is an assumption: that the gap between two time zones is constant. It isn't, and daylight saving time (DST) is why.

Here are the three DST bugs I see most often, with code that shows each one and how to avoid it.

Bug 1: "+24 hours" is not "tomorrow"

In 2026, the US clocks spring forward on March 8. Say an event starts at 1:30 AM New York time that day:

const fmt = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  dateStyle: "short",
  timeStyle: "short",
});

const start = new Date("2026-03-08T06:30:00Z"); // 1:30 AM EST
const plus24h = new Date(start.getTime() + 24 * 60 * 60 * 1000);

console.log(fmt.format(start));   // 3/8/26, 1:30 AM
console.log(fmt.format(plus24h)); // 3/9/26, 2:30 AM  <- not 1:30!
~~~{% endraw %}

Adding 24 hours moved the instant forward exactly 86,400 seconds. But that day only had 23 hours on the wall clock, so "the same time tomorrow" landed an hour late.

**The fix:** decide what you mean. If you want *elapsed time*, add milliseconds. If you want *the same wall-clock time tomorrow*, do calendar math in the target zone (a library like date-fns-tz or Luxon, or the Temporal API where available), not millisecond math.

## Bug 2: Times that don't exist (and times that happen twice)

DST creates two kinds of impossible local times:

- **The gap (spring forward):** on March 8, 2026, clocks in New York jump from 2:00 AM to 3:00 AM. 2:30 AM never happens.
- **The overlap (fall back):** on November 1, 2026, clocks go from 2:00 AM back to 1:00 AM. 1:30 AM happens twice.{% raw %}

~~~js
// In a runtime set to America/New_York
new Date(2026, 2, 8, 2, 30); // 2:30 AM doesn't exist
// Most engines silently shift it to 3:30 AM

new Date(2026, 10, 1, 1, 30); // 1:30 AM happens twice
// Which one did you get? Usually the first (EDT), but don't rely on it
~~~{% endraw %}

The overlap case is worse because it fails silently. Two different instants (05:30 UTC and 06:30 UTC) share the same wall-clock label, so a stored local time like {% raw %}`"2026-11-01 01:30"`{% endraw %} can't be turned back into one exact moment.

**The fix:** store instants in UTC (or as ISO strings with an offset), and convert to local time only for display. Never store a local time without its zone.

## Bug 3: Hardcoding offsets instead of zone names

This looks reasonable and is wrong half the year:{% raw %}

~~~js
const NEW_YORK_OFFSET = -5; // "UTC-5"
~~~

New York is UTC-5 in winter (EST) and UTC-4 in summer (EDT). A hardcoded offset is only correct for part of the year.

Use IANA zone names like `America/New_York`, and let the runtime look up the offset for the specific date:

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

console.log(offsetAt(new Date("2026-07-01T12:00:00Z"), "America/New_York")); // GMT-4
console.log(offsetAt(new Date("2026-12-01T12:00:00Z"), "America/New_York")); // GMT-5
~~~

## Why the gap between two zones moves

Karachi stays on UTC+5 all year. New York doesn't. So the difference between them changes:

| Period | New York | Gap to Karachi |
|---|---|---|
| Summer (EDT) | UTC-4 | 9 hours |
| Winter (EST) | UTC-5 | 10 hours |

It gets stranger when two zones both observe DST but switch on different dates. The US changes on the second Sunday of March and the first Sunday of November, while the EU changes on the last Sunday of March and October. For a few weeks each year, New York and London are 4 hours apart instead of 5. In 2026 that happens from March 8 to March 29, and again from October 25 to November 1.

If a scheduling bug "only happens in March and October," this is almost always the cause.

## A checklist you can reuse

1. Store timestamps in UTC. Store the user's zone name separately.
2. Use IANA names (`America/New_York`), never fixed offsets.
3. Use `Intl.DateTimeFormat` or a date library for conversion. Don't hand-roll offset math.
4. Separate "elapsed time" (add milliseconds) from "same time tomorrow" (calendar math).
5. Write tests around DST dates: the spring gap, the fall overlap, and the weeks where two regions are out of sync.

## Try it

If you want to sanity-check a conversion without writing code, I built a free [time zone converter](https://calculatorspan.com/time-zone-converter/) that handles DST for you. It's handy for verifying what your code should return.

What's the weirdest time zone bug you've hit? Tell me in the comments.
![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/9zvw64k8artm6gbws8dm.png)
📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.