Skip to content
Spellkit

Timezones, DST, and the Hidden Off-By-One in Counting Days Between Dates

Subtracting two dates looks like plain arithmetic, but DST transitions, shifting timezone offsets, and inclusive-versus-exclusive endpoints can each quietly change the answer by a day.

"How many days between these two dates" sounds like it has exactly one right answer. It doesn't — the answer depends on whether you're counting calendar days or elapsed hours, whether both endpoints count, and whether a clock change happened to fall inside the range. Each of those is a separate, well-known source of off-by-one bugs.

A day isn't always 24 hours

Daylight saving time makes two specific calendar days a different length every year. In the US, the day clocks spring forward (the second Sunday of March) has only 23 hours, because 2:00 AM to 3:00 AM never happens. The day clocks fall back (the first Sunday of November) has 25 hours, because 1:00 AM to 2:00 AM happens twice. The EU does the same thing on the last Sundays of March and October.

This is exactly where payroll software gets caught out. If a system computes "hours worked" as wall-clock checkout time minus wall-clock check-in time, a night-shift worker who clocks in at 10:00 PM and out at 6:00 AM reads as 8 hours by the clock — but on fall-back night they actually worked 9 real hours, and on spring-forward night only 7. The clock arithmetic is correct as arithmetic; it's just answering a different question than "how much time actually passed."

Elapsed time and calendar days diverge

"Remind me in 3 days" can mean two things: advance the wall-clock time by exactly 72 hours, or advance the calendar date by 3 and keep the same time of day. These usually agree — until a DST change falls inside the window. Set a reminder for 9:00 PM and have the system add 72 literal hours, and if a fall-back happened in between, it fires at 8:00 PM local time instead of 9:00 PM. Date-based reasoning ("three days later, same clock time") and duration-based reasoning ("72 hours from now") are both valid, but they're not interchangeable, and code that silently picks one usually didn't mean to.

When the offset itself changes mid-range

A subtler version of the same problem: converting a local date to a UTC instant requires knowing that date's timezone offset, and the offset isn't constant if a DST transition sits between your two dates. A system that resolves the offset once — say, using today's offset — and applies it to a future date on the other side of a clock change will compute that future instant an hour off from where it should be. Most of the time this is invisible, but if you're checking "days remaining" close to midnight, that extra hour can push the count over a day boundary and flip the answer depending only on what time of day you happened to check.

Inclusive or exclusive — pick one

Separately from timezones, there's a plain counting ambiguity. A conference that "runs March 3–5" is 3 days if you count both endpoints, but the gap between March 3 and March 5 is only 2 days. A 14-day trial that "starts today" ends on day 14 if today counts as day 1, or day 15 if it doesn't. Neither convention is wrong; they're answering different questions, and the bug is treating one as obviously correct without checking which one the surrounding context actually needs. Spellkit's Date Difference calculator reports the gap — the exclusive count — precisely so this choice is explicit: if you need the inclusive version, you add 1 yourself.

Feb 29 anniversaries don't recur on a fixed schedule

A date that only exists once every four years breaks the assumption that "the same date next year" is well-defined. JavaScript's own Date doesn't clamp an overflowing day — new Date(2025, 1, 29) silently rolls forward into March 1, 2025, rather than throwing or clamping to February 28. Other languages and libraries make the opposite choice. Neither is a bug; it's a convention that has to be picked deliberately, and any "days until my next anniversary" calculation built on a Feb 29 date is only as correct as whichever convention it happens to inherit.

Why counting in local calendar days sidesteps most of this

None of the DST or offset problems above can occur if the underlying arithmetic never touches wall-clock time at all. Spellkit's Date Difference and Age Calculator both take plain year/month/day values straight from the browser's date picker — no time-of-day, no timezone offset attached to either input — and do the subtraction entirely in calendar-date space, anchoring to UTC internally only as a stable way to do the day-count math, not because the dates represent real-world instants. Nineteen calendar days between two dates is nineteen calendar days whether a clock changed in between or not, and whether you're in Seoul or São Paulo when you ask. That's a narrower guarantee than "correct elapsed time," but it's the one almost everyone actually wants when the question is "how many days."