Daylight Saving Time: The Developer's Nightmare

Why DST breaks more software than almost any other date/time concept — how it works, when it changes, the edge cases that will bite you, and how to actually handle it correctly in code.


Why DST Breaks Everything

We built this converter because we kept running into the same timezone headaches at work — and daylight saving time was responsible for most of them. One Monday in March, a batch job that had run perfectly for months suddenly started producing timestamps that were off by an hour. Nobody had changed the code. The clocks had changed.

Daylight saving time (DST) is the practice of advancing clocks by one hour during warmer months so that evenings have more daylight. In theory, it's simple: "spring forward, fall back." In practice, it's one of the most reliable sources of bugs in software that deals with time. The reason isn't that DST is inherently complex — it's that it interacts with almost every assumption developers make about how time works.

Here's the assumption that breaks most often: every day has 24 hours. On the day clocks spring forward, a day has 23 hours. On the day clocks fall back, a day has 25 hours. If your code calculates "tomorrow" by adding 86,400 seconds (24 × 60 × 60), it'll be wrong twice a year.

How DST Actually Works

The basic mechanics are straightforward. At a designated time (typically 2:00 AM local time), clocks either jump forward one hour (spring) or fall back one hour (autumn). This creates two kinds of anomalies:

The Spring Gap. When clocks jump from 1:59 AM to 3:00 AM, the hour from 2:00 AM to 2:59 AM doesn't exist. The timestamp 2024-03-10T02:30:00 in US Eastern time is not a real time. If your system creates or accepts this timestamp, it needs a strategy for handling it — because that moment never occurred.

The Fall Overlap. When clocks fall back from 2:00 AM to 1:00 AM, the hour from 1:00 AM to 1:59 AM happens twice. The timestamp 2024-11-03T01:30:00 Eastern is ambiguous — it could refer to 1:30 AM EDT (before the change) or 1:30 AM EST (after the change). These are two different instants in time, one hour apart, represented by the same wall-clock time.

This is why our converter auto-detects timezone offsets from the input rather than relying on abbreviations alone. If someone pastes 01:30 EST vs 01:30 EDT, those are actually different times — and the offset resolves the ambiguity where the abbreviation alone can't.

When DST Changes (It's Not the Same Everywhere)

One of the worst misconceptions about DST is that it happens on the same date worldwide. It doesn't. Not even close.

RegionSpring ForwardFall BackNotes
United States, Canada (most)2nd Sunday in March1st Sunday in NovemberChanged in 2007 (Energy Policy Act of 2005)
European UnionLast Sunday in MarchLast Sunday in OctoberEU voted to abolish DST in 2019, still hasn't taken effect
Australia (SE states)1st Sunday in October1st Sunday in AprilSouthern hemisphere — reversed seasons
New ZealandLast Sunday in September1st Sunday in AprilAlso southern hemisphere
Japan, China, India, KoreaNo DST at all
Arizona (US), HawaiiUS states that don't observe DST
Most of AfricaMorocco is the notable exception

This means there are periods each year where the UTC offset between two cities changes even though neither city "did anything." In the three weeks between the US spring-forward (mid-March) and the EU spring-forward (late March), London is 4 hours ahead of New York instead of the usual 5. If your system hardcodes a timezone difference, it'll be wrong during these transition windows.

The IANA Timezone Database (Why You Should Never Hardcode Offsets)

The correct way to handle DST in software is to use the IANA timezone database (also called the Olson database or tzdata). This database maps timezone identifiers like America/New_York or Europe/London to their complete history of UTC offset changes, including all past and future DST transitions.

When you use America/New_York, the system knows that EST is UTC-5 and EDT is UTC-4, and it knows exactly when the switch happens each year. This is what our converter does under the hood — all 98 timezone options map to IANA zones, not fixed offsets. That way America/New_York automatically resolves to -5 in January and -4 in July without any manual logic.

The database is maintained by a volunteer community and updated several times a year. Countries occasionally change their DST rules (sometimes with very little notice), and the database tracks these changes. If your system pins to an old version of tzdata, it can produce wrong results for recent rule changes.

# Check your system's tzdata version
# Linux
cat /usr/share/zoneinfo/tzdata.zi | head -1

# Python
import importlib.metadata
importlib.metadata.version('tzdata')

# Node.js (uses the OS timezone data by default)
# Update via OS package manager

Handling DST in Code

JavaScript

// DON'T: Add 24 hours to get "tomorrow"
const tomorrow = new Date(today.getTime() + 86400000);
// Breaks on DST transition days

// DO: Use calendar arithmetic
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
// Correctly handles 23 and 25-hour days

// Check if a date is in DST for a given timezone
function isDST(date, timeZone) {
  const jan = new Date(date.getFullYear(), 0, 1);
  const jul = new Date(date.getFullYear(), 6, 1);
  const stdOffset = Math.max(
    jan.getTimezoneOffset(),
    jul.getTimezoneOffset()
  );
  return date.getTimezoneOffset() < stdOffset;
}

// Get the offset for any IANA timezone at a specific instant
function getOffset(date, timeZone) {
  const utc = date.toLocaleString('en-US', { timeZone: 'UTC' });
  const local = date.toLocaleString('en-US', { timeZone });
  return (new Date(utc) - new Date(local)) / 60000; // minutes
}

Python

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo  # Python 3.9+

# DON'T: Add timedelta for "tomorrow" then assume same time
eastern = ZoneInfo("America/New_York")
now_et = datetime(2024, 3, 10, 1, 30, tzinfo=eastern)
tomorrow_wrong = now_et + timedelta(hours=24)
# On spring-forward day, this lands at a different wall-clock time

# DO: Normalize after arithmetic
tomorrow = (now_et + timedelta(days=1))
# timedelta(days=1) adds exactly 24h, but the wall clock adjusts correctly
# because the tzinfo stays attached and Python resolves it

# Check if a datetime is in DST
def is_dst(dt):
    """Returns True if the datetime is in daylight saving time."""
    if dt.tzinfo is None:
        raise ValueError("Datetime must be timezone-aware")
    return bool(dt.dst())

SQL (PostgreSQL)

-- TIMESTAMP WITH TIME ZONE handles DST automatically
SELECT '2024-03-10 01:30:00'::timestamptz AT TIME ZONE 'America/New_York';
-- → 2024-03-10 06:30:00+00 (EST, UTC-5)

SELECT '2024-03-10 03:30:00'::timestamptz AT TIME ZONE 'America/New_York';
-- → 2024-03-10 07:30:00+00 (EDT, UTC-4)

-- DON'T use TIMESTAMP WITHOUT TIME ZONE for cross-timezone work
-- It strips timezone info and you lose DST context

Edge Cases That Will Bite You

Scheduled events at 2:30 AM on spring-forward day. The time doesn't exist. Most systems will either skip the event, run it at 3:00 AM, or run it at 1:30 AM. The behavior varies by implementation and is rarely documented well. If you schedule cron jobs or recurring tasks, avoid the 2:00–3:00 AM window entirely — schedule them at 3:30 AM or later.

Duration calculations across DST boundaries. "How many hours between 1:00 AM EST and 3:00 AM EDT on spring-forward day?" The answer is one hour, not two, even though the wall clock shows a two-hour gap. Always calculate durations in UTC or using epoch timestamps, never by subtracting wall-clock times.

Recurring events ("every day at 2:30 PM"). If you store this as a UTC time and convert to local, the local time will shift by an hour after DST transitions. If you store it as a local time, you need to resolve it against the IANA database every time. Neither approach is "right" — it depends on whether the intent is "same UTC instant every day" or "same wall-clock time every day." Calendar applications generally use local time (wall clock), while backend systems generally use UTC.

Countries that change their DST rules. Russia abolished DST in 2011 (permanently staying on "winter time" after a brief stint of permanent "summer time"). Turkey abandoned DST in 2016. Egypt has toggled DST on and off multiple times. Morocco's DST schedule is modified annually around Ramadan. If you hardcode rules, they will break.

The Abolition Movement

There's a growing push to eliminate DST entirely. The EU voted to abolish it in 2019, though member states haven't agreed on whether to stay on permanent standard time or permanent summer time. In the US, the Sunshine Protection Act (which would make DST permanent year-round) passed the Senate unanimously in 2022 but stalled in the House.

From a software perspective, abolishing DST wouldn't eliminate timezone complexity — we'd still have different UTC offsets around the world. But it would eliminate the two most treacherous days of the year and make "one day = 24 hours" true everywhere that adopts it.

Practical Rules for Developers

Store timestamps in UTC. Always. Convert to local time only for display. This single rule prevents most DST bugs.

Use IANA timezone identifiers, not abbreviations or offsets. America/New_York is unambiguous and DST-aware. EST is not (it only refers to the standard-time half of the year). -05:00 is correct for a specific instant but wrong six months later.

Never add or subtract fixed seconds to change dates. Use calendar arithmetic (add 1 day, add 1 month) and let the datetime library handle the DST math.

Test your code on DST transition days. Use 2024-03-10 (US spring forward) and 2024-11-03 (US fall back) as test fixtures. If your code survives those days, it'll survive most edge cases.

Keep your timezone database updated. This is as important as patching security vulnerabilities. Stale tzdata produces wrong results silently.

Related Guides

For a complete list of timezone abbreviations and their UTC offsets, see the Timezone Abbreviations Reference. To understand how ISO 8601 handles timezone designators (the Z and +05:30 suffixes), see ISO 8601 Explained. For converting timestamps across timezones in bulk, see How to Convert Dates in CSV Files.

Try It Yourself

Convert any date or timestamp instantly — free, no sign-up required.

Open the Converter