Daylight Saving Time: Why Your Countdowns and Meetings Keep Breaking
Every year, around the second Sunday of March in the United States, something quietly breaks. A Zoom invite lands at the wrong hour. A countdown timer hits zero forty minutes early. A carefully automated email goes out while the recipient is still asleep. Nobody changed anything. The code didn't crash. But the clock moved, and everything downstream moved with it — or didn't, depending on how you built it.
Daylight Saving Time (DST) is the peculiar practice of shifting clocks forward by one hour in spring and back in autumn, nominally to squeeze more usable daylight into waking hours. In practice, for anyone building scheduling software, running recurring meetings across borders, or simply setting up a countdown to a product launch, DST is a landmine you step on twice a year without warning.
The Problem Is Subtler Than You Think
Most people assume the issue is obvious — clocks change, so adjust by an hour. But the actual bugs tend to be much slippier than a simple one-hour offset.
Consider a countdown timer you built in JavaScript for a product launch at midnight on the 10th of November, New York time. You store the target as a Unix timestamp converted from "2025-11-10 00:00:00 America/New_York." That's fine. But then a user in London opens the page. Their browser converts that timestamp to their local time correctly. No problem yet.
Now flip the scenario: you're not using Unix timestamps. You're storing "November 10, midnight" as a naive datetime string — no timezone attached — and doing your countdown math on the client side. The user's browser arithmetic is now running against whatever timezone the system clock reports. If the server is in UTC, the database has UTC midnight, and the user is in New York on Eastern Standard Time (UTC-5), they see the countdown end at 7 PM on November 9th. You've lost five hours.
DST compounds this. During the spring transition in the US, clocks jump forward from 2:00 AM to 3:00 AM. That means 2:30 AM simply does not exist on that day. If you've scheduled a cron job, a meeting reminder, or a batch process for 2:30 AM, your scheduler either skips it entirely or fires at the wrong moment depending on how it handles the gap.
In autumn, the reverse happens: 1:30 AM occurs twice. A midnight reminder that should fire once might fire twice if your system isn't storing events in UTC and your scheduler isn't smart enough to disambiguate.
Why Recurring Meetings Are Especially Fragile
Imagine a standing weekly team meeting set for "every Monday at 10 AM London time." You create the event in March, when London is on British Summer Time (BST, UTC+1). Six months later in October, the UK clocks fall back to GMT (UTC+0). If your calendar application stored the recurrence rule as "10:00 BST" and rigidly converts that to "11:00 UTC" for all future instances, suddenly your Monday meeting is happening at 11 AM GMT — an hour later than anyone expected.
Google Calendar handles this correctly by storing recurring rules in the named timezone ("Europe/London") and recomputing the UTC equivalent for each occurrence. But plenty of smaller scheduling tools, home-grown booking systems, and legacy enterprise software store events as fixed UTC offsets. That one-character shortcut — storing "+01:00" instead of "Europe/London" — causes one hour of drift twice per year for every affected event.
The sting is worse for teams spread across countries where DST transitions happen on different dates. The US and Europe don't change clocks on the same weekend. So for two or three weeks every spring and autumn, a "9 AM New York, 2 PM London" slot is actually "9 AM New York, 3 PM London." Automated invites generated before the transition don't know this. They fire at the original UTC time and confuse half the attendees.
The Fix: Think in Named Timezones, Store in UTC
The most durable approach to DST-proof scheduling follows a two-rule system that sounds simple but requires deliberate implementation:
- Store all datetimes in UTC. Never store a local time without also recording the timezone that produced it.
- Express recurrence rules and display logic in named IANA timezones, not fixed offsets.
The IANA timezone database (also called the tz database or Olson database) is the canonical list of named timezones — things like America/New_York, Europe/London, Asia/Kolkata. Named timezones include the full historical record of DST rules, so a library using them can correctly compute "the third occurrence of this Monday 10 AM rule in October" even after the clocks have changed.
In JavaScript, the native Intl API now handles named timezones well, but for serious scheduling work the Temporal API (available in modern browsers and via polyfill) is purpose-built for this. In Python, the zoneinfo module (Python 3.9+) or pytz handles it. In Java, use ZonedDateTime with a ZoneId, not OffsetDateTime.
A concrete example in Python:
from datetime import datetime
from zoneinfo import ZoneInfo
# Naive — dangerous
launch = datetime(2025, 3, 9, 2, 30) # This time won't exist in New York
# Aware — correct
launch = datetime(2025, 3, 9, 10, 0, tzinfo=ZoneInfo("America/New_York"))
launch_utc = launch.astimezone(ZoneInfo("UTC"))
The second form correctly handles the fact that on March 9, 2025, the US springs forward. If you had passed 2:30 AM, a well-behaved library raises an error or folds to 3:30 AM; a naive datetime silently stores a timestamp that means nothing.
Building a Countdown That Survives DST
For a countdown timer, the core rule is simple: calculate everything from Unix timestamps (milliseconds since epoch). Unix time doesn't have timezones. It's always UTC. The display is where you apply the local timezone.
// JavaScript — DST-safe countdown
const targetISO = "2025-11-15T09:00:00-05:00"; // explicit offset at time of creation
const targetMs = new Date(targetISO).getTime();
function updateCountdown() {
const nowMs = Date.now();
const remaining = targetMs - nowMs;
// render remaining as days/hours/minutes/seconds
}
If you're displaying "the event is at 9 AM New York time" and the user's browser is in a different zone, use Intl.DateTimeFormat with the target timezone to render the label, not the countdown math. The countdown runs on fixed milliseconds; the display adapts to whoever's reading it.
Where things go wrong is when developers compute the target as "today's date plus N days" and format it as a date string without a timezone, then parse it back in a different environment. That string-round-trip step is where UTC offsets silently vanish.
Scheduling Jobs Across DST: The Cron Trap
Traditional cron uses wall-clock time on the server. If your server is in America/Chicago and you run a job at 30 2 * * 0 (2:30 AM every Sunday), the spring-forward Sunday will skip the job. In autumn it may run twice.
The cleanest solutions:
- Run your server in UTC. Most Linux servers default to this.
30 7 * * 0in UTC is 2:30 AM Central Standard Time but 3:30 AM Central Daylight Time — at least it fires once and reliably. - Use a DST-aware scheduler. Tools like AWS EventBridge, Google Cloud Scheduler, and Celery with
beat_schedulein a named timezone handle DST transitions correctly. They calculate the next run time in the named zone after each execution, so the UTC equivalent shifts appropriately across DST boundaries. - Audit your jobs twice a year. Even with UTC servers, jobs intended to fire at a specific local time (say, "before the business day starts in London") will drift by an hour across DST. Build monitoring that alerts if a critical job fires outside an expected UTC window.
The Meeting-Scheduler's Checklist
If you're building or using any tool that schedules meetings across timezones, run through these before you ship:
- Does your event storage use named IANA timezones, not raw offsets like "+05:30"?
- Are recurring event rules computed fresh for each occurrence, not pre-computed to a fixed list of UTC times?
- When a user books a slot at "3 PM their time," are you storing the IANA timezone they're in, not just the UTC equivalent?
- Do you test across DST boundaries — specifically the weekend of the US spring-forward and the weekend of the EU autumn fallback?
- If you send reminders N hours before an event, are those computed against the stored UTC timestamp, not a locally formatted date string?
Closing Thought
DST is not going away soon, despite growing legislative pressure in the EU and some US states to abolish it. Until it does, the gap between "what the clock on your wall says" and "what a UTC timestamp encodes" will keep catching people off guard twice a year.
The good news is that the fix is well-understood and the tooling is mature. Named timezones, UTC storage, and a healthy suspicion of naive datetime strings are all you need. The countdowns that survive spring and autumn are the ones that were never really counting in local time to begin with — they were always counting in seconds, and just wearing your timezone as a costume for display.
Build it that way once, and DST becomes someone else's problem.