How DST breaks software (and how engineers handle it)
Daylight saving time has caused some of the most expensive software bugs in history. Microsoft outage. Apple alarm clock failure. Reddit downtime. Cloudflare outage. The pattern is consistent: developers underestimate how much edge-case behavior DST shifts create, and the bugs surface twice a year on schedule.
For a clean way to handle DST in your own code, the epoch converter shows the principle: store everything in UTC, convert at the display layer.
The four classes of DST bugs
DST creates four common bug patterns:
1. Missing-hour bugs. Things scheduled to happen during the spring-forward gap (e.g., 2:30 AM on March 8 in US zones) don't happen because the time doesn't exist. Cron jobs skip. Alarms don't fire. Schedulers fail.
2. Duplicate-hour bugs. Things scheduled during the fall-back duplicate hour (1:00–1:59 AM happens twice) might happen twice or ambiguously. Notifications, automated actions, log entries.
3. Wrong-rule bugs. Software hardcodes the old DST rule. When a country changes the rule (e.g., the US 2007 extension), the software is off by an hour for the affected period.
4. Local-time-as-storage bugs. Storing local timestamps and trying to compute durations or compare them across DST shifts. Two events on adjacent calendar days might have the same wall-clock time but different UTC times — comparing wall-clock times gives wrong answers.
Famous DST bugs
Apple iOS alarms (2010). A bug in iOS made alarm clocks fire an hour early in the days after the Australian autumn DST shift. People missed flights, meetings, school. Apple released a fix within days.
Apple iOS again (2018). Different bug, similar effect. Calendar entries on November 4, 2018 showed at the wrong time in some configurations.
Microsoft Outlook (2007). When the US extended DST by 4 weeks in March 2007, Outlook calendar entries created during the extended period shifted by an hour. Companies spent weeks fixing meeting times. Microsoft released a tzdata update that resolved the issue, but the manual fix-up was extensive.
Linux kernel (2012). A leap second on June 30, 2012 (a different bug class but related to time handling) caused crashes on Reddit, LinkedIn, Yelp, Foursquare, and several airlines. Multiple Linux subsystems hung when the leap second was inserted.
Cloudflare (2017). A leap second handling bug in DNS resolution code caused a partial Cloudflare outage. Affected several major sites that route through Cloudflare.
Microsoft Azure (2018). A DST-related bug caused some scheduled tasks to run 1 hour early after a DST shift, including some critical infrastructure jobs.
Various e-commerce platforms (multiple shifts). Order timestamps, scheduled deliveries, and time-based promotions have failed across DST shifts.
Why these bugs happen
Several reasons:
1. Date math is hard. "Add 1 day to a timestamp" is not actually adding 86,400 seconds. The local-time interpretation might shift by an hour due to DST. Many libraries handle this correctly; many older or hand-rolled implementations don't.
2. Scheduling is hard. Cron-style schedulers express times in local time but actually need to track UTC. Without explicit DST handling, "every day at 2:30 AM" gets ambiguous twice a year.
3. Hardcoding rules is tempting. Developers sometimes hardcode "DST starts on the second Sunday of March" without realizing this rule has changed before and could change again. Software then becomes wrong when the rule changes.
4. Test environments don't always include DST. Engineers test against current time, not a date during a DST shift. Bugs surface in production.
5. Edge cases compound. A cron job scheduled for 2:30 AM, in a zone that observes DST, on the day of spring-forward, might or might not fire. Combine that with retry logic, locking, idempotency assumptions — small bugs become outages.
Patterns engineers use to avoid DST bugs
1. Store time as UTC. Always. UTC has no DST, so all the issues disappear at the storage layer. Convert to local time only when displaying to a human.
2. Use an IANA-aware library. Python's `zoneinfo`, JavaScript's `Intl.DateTimeFormat`, Java's `java.time`. Avoid hand-rolled time-zone code.
3. Never use 24-hour multiples for date math. Don't write `start + 7 24 3600`. Use proper date arithmetic that respects DST: `start.add(7, 'days')` in most libraries.
4. Schedule cron jobs in UTC. Or specify the time zone explicitly and accept that the scheduler will skip/repeat hours during DST shifts.
5. Test with DST shifts. Include test cases for the spring-forward missing hour, the fall-back duplicate hour, and DST transitions. Your CI should know about DST.
6. Update tzdata regularly. OS updates ship new tzdata. Make sure production servers update. Old tzdata is a common source of subtle bugs.
7. Anchor recurring meetings to UTC. For application code that handles "every Monday at 9 AM," store the rule as "every Monday at 14:00 UTC" rather than local time. Convert for display only.
DST in specific languages
Python (3.9+).
```python
from datetime import datetime
from zoneinfo import ZoneInfo
Correct: local time with explicit zone
localdt = datetime(2026, 3, 8, 10, 0, tzinfo=ZoneInfo("America/NewYork"))
utcdt = localdt.astimezone(ZoneInfo("UTC"))
Avoid: naive datetime + DST math
bad_dt = datetime(2026, 3, 8, 10, 0) # naive, ambiguous
```
JavaScript.
```javascript
// Use Intl.DateTimeFormat for display
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'medium',
timeStyle: 'short',
});
// For arithmetic, the new Temporal API is recommended (still in proposal as of 2026)
// Or use date-fns, luxon, or moment-timezone.
```
Java 8+.
```java
ZoneId zone = ZoneId.of("America/New_York");
ZonedDateTime localTime = ZonedDateTime.now(zone);
Instant utcMoment = localTime.toInstant();
```
SQL (PostgreSQL).
```sql
-- Storing in UTC (timestamp without time zone, UTC by convention):
INSERT INTO events (timestamp_utc) VALUES (NOW() AT TIME ZONE 'UTC');
-- Or use timestamptz which handles conversion automatically:
SELECT createdat AT TIME ZONE 'America/NewYork' FROM events;
```
What's a "leap second"?
Slightly different bug class but worth mentioning: leap seconds are 1-second adjustments to UTC, added occasionally to keep UTC aligned with Earth's rotation. They've caused outages similar to DST bugs (Reddit 2012, Cloudflare 2017). Modern systems use "smearing" (slowly adjusting clocks over hours) to avoid the spike. Leap seconds are being phased out by 2035.
Modern tooling that helps
- Google's smear NTP — slowly adjusts clocks over hours, avoiding leap-second spikes
- AWS Time Sync Service — same approach, on AWS
- Cloudflare time.cloudflare.com — public NTP with smearing
- The IANA tz database — updated 6-12 times per year, propagated through OS updates
FAQ
Why is my cron job running twice on fall-back day?
Because the duplicate hour (1-2 AM, twice) means a job scheduled for 1:30 AM gets a second chance to run. Most modern schedulers handle this; Linux's `cron` with default config does not. Use `at` for one-time jobs, or anchor cron jobs to UTC.
My calendar showed the wrong time after DST. Why?
Probably the calendar entry was created without an explicit time zone. Many calendars default to "device's current zone," which can shift. Fix by editing the entry with explicit zone.
Do I need to do anything special during DST shifts?
If your code uses good libraries (zoneinfo, java.time, etc.) and stores times in UTC, no. If you've hand-rolled time-zone code or hardcoded DST rules, audit before each shift.
Are there languages that handle DST better than others?
Modern Python (zoneinfo), modern Java (java.time), and Go's time package all handle DST correctly. JavaScript was historically weak but is improving with the Temporal proposal. Older languages and legacy code (PHP, older Python, COBOL, etc.) often have hand-rolled DST code with subtle bugs.
What about embedded systems and IoT?
Many embedded systems don't update tzdata. They have hardcoded rules from when the firmware was compiled. When countries change DST rules (Mexico abolishing in 2022, etc.), these systems become wrong. Updating requires firmware updates, which often don't happen.
How do I test DST handling in my app?
Set the system clock to a date right before a DST shift, advance it through the shift, and verify behavior. Or use a mocked time library that simulates the shift. Major frameworks have utilities for this.
Bottom line
DST creates predictable, repeating software bugs. The pattern is: store time as UTC, use modern timezone-aware libraries, anchor recurring schedules to UTC where possible, and test with DST shifts in mind. Engineers who follow these patterns avoid the worst bugs; engineers who don't get to debug at 2 AM in the morning twice a year.
For converting UTC times to local zones with proper DST handling, the epoch converter and time converter demonstrate the right approach.