Two ways to measure elapsed time
There are two legitimate answers to “how long between these dates”, and they disagree.
Absolute duration counts elapsed seconds and converts them: total days, hours, minutes. This is unambiguous and is what you want for billing, timeouts, and performance measurement.
Calendar duration counts years, months, and days as humans do. From 31 January to 28 February is “one month” even though it is 28 days, while March to April is a month of 31 days. This is what you want for ages, anniversaries, and contract terms.
This tool shows both, because using the wrong one is a common source of off-by-a-day disputes.
Why month arithmetic is genuinely hard
Adding a month to 31 January is undefined. There is no 31 February, so the result depends on a policy decision: clamp to 28 February, roll over to 3 March, or throw an error. Different libraries choose differently, and the same code can produce different results across ecosystems.
The compounding problem is worse. Adding one month twice to 31 January gives 28 February then 28 March under clamping — but adding two months directly gives 31 March. Month arithmetic is not associative, which surprises people who assume it behaves like addition.
The practical advice is to avoid month arithmetic where you can. Express durations in days when precision matters, and where months are genuinely required, document which convention you use.
Daylight saving and the missing hour
Not every day has 24 hours. On spring-forward days a local day has 23 hours; on autumn days, 25. Code that adds 86,400 seconds to get “tomorrow at the same time” is wrong twice a year.
This produces real bugs: recurring 9am meetings that drift to 8am or 10am, daily jobs that run twice or not at all, and subscription renewals landing on the wrong day.
The fix is to do date arithmetic in a calendar-aware way rather than by adding seconds. Add one day to a local date, then resolve the time zone — do not add 86,400 seconds to a timestamp and assume the local clock reading is unchanged.
Business days and working hours
Elapsed time is rarely what a service-level agreement means. “Two business days” requires knowing which days are working days, which public holidays apply, and in which jurisdiction.
None of this is derivable from the dates alone. Holiday calendars vary by country, by region within a country, and by year — some holidays move with the lunar calendar or shift when they fall on a weekend.
If you are building this, use a maintained holiday library or data source rather than hard-coding a list. Hard-coded holiday tables are among the most reliably wrong pieces of code in any system, because they silently expire.
Practical advice for handling dates in code
Use ISO 8601 for any date you store or transmit: 2025-01-01T09:00:00Z. It sorts correctly as a string, is unambiguous about ordering, and is parsed consistently everywhere. Formats like 01/02/2025 mean different dates on either side of the Atlantic.
Store UTC, display local. Convert only at the presentation layer.
Use a library. Date handling has more edge cases than almost any other common task. The Temporal API is arriving in JavaScript to address the long-standing weaknesses of Date; until it is widely available, a maintained library is a better choice than hand-rolled arithmetic.
Frequently asked questions
Why do date calculators disagree about the number of months?
Because month arithmetic is ambiguous. Adding a month to 31 January has no correct answer, so different tools clamp to 28 February, roll over to March, or error. There is no universal convention.
Is every day 24 hours long?
No. On daylight saving transition days a local day has 23 or 25 hours. Code that adds 86,400 seconds to get the same time tomorrow is wrong twice a year.
How do I calculate business days?
You need a holiday calendar for the specific jurisdiction and year, since holidays vary by country, by region, and can move between years. Use a maintained data source rather than a hard-coded list.
What date format should I use in an API?
ISO 8601, such as 2025-01-01T09:00:00Z. It sorts correctly as a string and is parsed consistently. Formats like 01/02/2025 are ambiguous between regions.