Skip to content
All posts

Timezone conversion: the edge cases that bite

DST, half-hour offsets, timezones with historical changes, and the midnight that doesn't exist. If your code assumes 24 hours a day, it's wrong.

DDDev DeskDeveloper Tools EditorPublished April 26, 20266 min readintermediate

# The problem in one sentence

A calendar day is not always 24 hours long, not always starts at midnight, and may not contain all the hours you expect.

# The five edge cases

# 1. DST transitions: the missing hour

In New York, at 2am on the second Sunday of March, clocks jump to 3am. The hour between 2:00 and 3:00 does not exist locally that day.


2026-03-08 01:59:59 EST  →  2026-03-08 03:00:00 EDT

If your app schedules a job for 2:30am local on that day, it either runs twice (once in EST, once in EDT) or never (the time doesn't exist).

# 2. DST transitions: the repeated hour

In November, the reverse. At 2am, clocks fall back to 1am. 01:30:00 local happens twice — once in EDT, once in EST.

If you're timestamping log events as local time, 1:30am on that day is ambiguous.

<div class="callout callout-tip" role="note"><div class="callout-title">Tip</div><div class="callout-body"><p>Store everything as UTC. Convert to local only at display. This makes DST invisible to your code — the clock has 23 or 25 "local hours" twice a year, but UTC always has 86,400 seconds per day.</p></div></div>

# 3. Half- and quarter-hour offsets

Not every timezone is an integer number of hours off UTC:

  • India is UTC+5:30
  • Nepal is UTC+5:45
  • Iran is UTC+3:30
  • Afghanistan is UTC+4:30
  • Myanmar is UTC+6:30
  • Newfoundland is UTC-3:30

A code that stores offsets as integer hours will misprint every time in these places.

# 4. Timezones with historical changes

Samoa skipped December 30, 2011 entirely — one minute it was Dec 29 23:59, the next it was Dec 31 00:00. Venezuela has changed offset twice in recent memory. Morocco pauses DST during Ramadan.

If your app computes "how many hours between two dates in Samoa" by treating time as 24 hours × days, you get 24 hours too many for any pair that straddles Dec 30, 2011.

Libraries like date-fns-tz, Luxon, and Temporal use the IANA tz database to get these right automatically.

# 5. Midnight that doesn't exist

In Brazil, for many years, DST transitions happened at midnight — meaning the local clock jumped from 23:59 to 01:00 on certain days. Midnight on those dates doesn't exist in the local timezone.

If your cron job fires at "00:00 America/Sao_Paulo" on the DST transition day, it might run at 01:00 local instead. Or twice. Or not at all. Depends on the library.

# What "timezone" actually means

"EST" is not a timezone. It's an offset (-05:00). Timezones are regions with a shared history of offsets — rules that change over time.

  • IANA zone: America/New_York — a region. Includes DST rules, historical changes, future planned changes.
  • Offset: -05:00 — a number. Valid only at a specific instant.

Always use IANA zone names in storage. Never store "EST" or "PST" as authoritative.

# Scheduling meetings across timezones

The three hard problems:

1. Someone is on the DST transition day when nobody else is.

2. Two participants are in timezones that don't share the same DST rules (US switches before Europe).

3. A flight or remote worker changes timezone between when the meeting is scheduled and when it happens.

The only safe pattern:

  • Store the meeting as a UTC instant.
  • Let each participant's calendar render it in their current zone.
  • On display, clearly label both the absolute time and the participant's local time.

# Our timezone converter

Our Timezone Converter uses the browser's Intl.DateTimeFormat API, which reads the IANA tz database shipped with the browser. It handles DST, half-hour offsets, and historical changes correctly.

Paste a date in one zone, pick another, read the result. Works offline — no server round-trip.

# The golden rules

1. Store UTC. Display local.

2. Use IANA zone names (Europe/London), not abbreviations (BST).

3. Don't assume days are 24 hours long. For "5 days from now," use calendar math, not + 5 86400 1000.

4. Don't assume a day starts at midnight. For "start of day," use your library's startOfDay — it knows about the Brazilian exception.

5. Test against a timezone different from your dev machine. US-based teams break subtly in Australia.

Frequently asked questions

How do I store a date in a database without timezone bugs?

Store UTC. Convert to the user's timezone only for display. Never store 'Asia/Kolkata 9:00am' — store '2026-04-26T03:30:00Z' and let the display layer show '9:00am IST'.

Can a timezone really have a non-whole-hour offset?

Yes. India is UTC+5:30. Nepal is UTC+5:45. Newfoundland is UTC-3:30. If your code stores offsets as an integer number of hours, these all break.

How many timezones exist?

About 40 in active use. The IANA tz database tracks ~600 names (including historical aliases). Use IANA names like `America/New_York`, not abbreviations like `EST` — abbreviations are ambiguous and non-unique.

New posts, once a week.

Practical developer guides. No spam. Unsubscribe any time.

Tools mentioned

Keep reading