TL;DR: Adding 24 hours to a timestamp does not reliably move a clock forward by one calendar day. Because of Daylight Saving Time transitions, a calendar day can span 23, 24, or 25 hours. To prevent brittle tests and production bugs, always perform date math using calendar intervals rather than fixed durations of hours or seconds.
There is nothing quite as humbling as watching an entire CI pipeline turn red on code nobody has touched in months.
A test suite runs cleanly for weeks, pull requests merge without incident, and then—out of nowhere on a random Sunday night—builds start failing across unrelated branches. When code breaks without anyone modifying it, the culprit is almost always an external assumption leaking into your test suite.
Why did our CI build fail without any code changes?
The CI pipeline failed because an automated test relied on a hidden environmental dependency: the calendar. When Daylight Saving Time occurred, a unit test assuming every day contains exactly 24 hours broke across all builds.
In our test suite, someone wrote an assertion verifying scheduled events. It looked innocent enough:
- Capture the current timestamp (
now). - Add 24 hours to that timestamp.
- Assert that the resulting time equals the exact same wall-clock time tomorrow.
For roughly 363 days a year, that test passes without complaint. But on the morning the clocks moved for Daylight Saving Time, the assertion completely collapsed. The test failed on every single deploy because the system was doing exactly what the engineer told it to do—add 24 hours—rather than what they meant: advance the calendar by one day.
Why is a day not always 24 hours?
A calendar day is a human convention tied to local civil time, whereas 24 hours is a fixed physical duration of 86,400 seconds. When Daylight Saving Time begins or ends, local clocks shift forward or backward by an hour, producing calendar days that are actually 23 or 25 hours long.
Think of it like an egg timer versus a hotel reservation. If you set an egg timer for 24 hours, it counts down raw elapsed physical seconds. It does not care what timezone you are in or what the sun is doing. A hotel reservation, however, cares about civil calendar days. When you book a room for "tomorrow," you expect to arrive at the same local hour, regardless of whether civil authorities stole or added an hour to the night.
When we hardcode 24 * 60 * 60 * 1000 milliseconds into our logic, we are using an egg timer to schedule a hotel stay.
// Fragile: Assumes every day has 86,400,000 milliseconds
const tomorrowDuration = new Date(Date.now() + 24 * 60 * 60 * 1000);
// Resilient: Adds a calendar day according to local civil rules
const tomorrowCalendar = new Date();
tomorrowCalendar.setDate(tomorrowCalendar.getDate() + 1);
How should developers handle date arithmetic across Daylight Saving Time?
Developers should separate duration math from calendar math. When an operation needs to land on the same local time tomorrow, manipulate semantic calendar units like days and months instead of adding fixed blocks of hours or seconds.
Here is how fixed duration math compares against calendar arithmetic across different points in the year:
| Scenario | Fixed Duration Math (+24 Hours) | Calendar Unit Math (+1 Day) |
|---|---|---|
| Standard Day | Advances exactly 24 hours to the same time. | Advances 1 calendar day to the same time. |
| Spring Forward (DST Start) | Skips ahead: lands 1 hour later than current local time. | Preserves local wall-clock time (23-hour day). |
| Fall Back (DST End) | Lags behind: lands 1 hour earlier than current local time. | Preserves local wall-clock time (25-hour day). |
| Primary Use Case | Rate limiting, session timeouts, token expiry. | Daily reminders, billing cycles, calendar events. |
If you want an event to happen 86,400 seconds from right now—like an authentication token expiration—use fixed durations. But if you want something to run "at the same time tomorrow," tell your runtime to add one day, not 24 hours.
Frequently Asked Questions
Why does adding 86,400 seconds cause bugs during Daylight Saving Time?
Because 86,400 seconds is strictly 24 hours of elapsed physical time. During DST transitions, a civil day is either 23 hours (82,800 seconds) or 25 hours (90,000 seconds), meaning an 86,400-second offset will cause your timestamp to shift by an hour relative to local wall-clock time.
Does storing timestamps in UTC prevent date math bugs?
No, UTC only eliminates ambiguity when storing and logging past events. If you take a UTC timestamp and add 24 hours to schedule a recurring 9:00 AM meeting, that meeting will still display at 8:00 AM or 10:00 AM locally once the user's timezone enters or exits Daylight Saving Time.
How do modern date libraries solve this problem?
Modern libraries distinguish between physical durations (like durations of elapsed seconds) and calendar periods (like days, months, and years). When you ask libraries like date-fns, Java's java.time, or the ECMAScript Temporal proposal to add a day, they resolve the operation against civil calendar rules rather than raw elapsed seconds.
Top comments (0)