Published
Date arithmetic that goes wrong — inclusive days, leap years and ages
A contract runs from 1 March to 31 March. How many days is that? Thirty, if you subtract the dates. Thirty-one, if you count the days you turn up. Both are defensible, and if the two parties to the contract pick differently, someone is billing a day they are not being paid for. Date arithmetic looks like subtraction and behaves like a convention.
The off-by-one is a definition, not a bug
Subtracting two dates gives you the number of intervals between them. Counting days on a calendar gives you the number of days touched. These differ by exactly one, always:
31 March − 1 March = 30 (elapsed days) 1, 2, 3 … 31 = 31 (inclusive days)
Neither is wrong. What matters is which one the thing you are counting uses, and the conventions are not consistent:
Elapsed (exclusive) counting is what you want for durations — how long something lasted, how many nights in a hotel, how many days until a deadline, interest accruing on a loan. A stay from Friday to Sunday is two nights, and two is the subtraction.
Inclusive counting is what you want when each day is itself the unit being counted — days worked, days of annual leave, days of a rental where you are charged per calendar day, notice periods in most legal drafting, and the length of a festival. A three-day conference running Monday to Wednesday is three days, and three is the subtraction plus one.
The safest habit in writing is to name the endpoints rather than the count: "from 1 March to 31 March inclusive" cannot be misread, and "30 days from 1 March" cannot either. "For 30 days from 1 March" without "inclusive" is the sentence that ends up in dispute.
Leap years, and why February breaks month arithmetic
The Gregorian rule is three tests, applied in order:
A year is a leap year if it is divisible by 4, unless it is divisible by 100, unless it is also divisible by 400.
So 2024 is a leap year, 1900 was not, 2000 was. The rule exists because the solar year is about 365.2422 days: adding a day every four years overshoots slightly, skipping three of those additions per four centuries corrects most of the overshoot, and what remains is about one day per 3,000 years.
The consequence for arithmetic is that adding months is not associative with adding days. Add one month to 31 January and you land on a date that does not exist. Every system resolves this by clamping to the last valid day — 28 or 29 February — and once clamped, the information is gone. Add one month five times from 31 January and you get 30 June, not 31 June; add one month to 28 February and you get 28 March, even though you arrived at 28 February from 31 January.
This bites hardest on monthly schedules. A subscription started on the 31st bills on the 28th, 29th or 30th in short months, and whether it returns to the 31st afterwards depends entirely on whether the system remembers the original day or walks forward from the last billing date. Both behaviours ship in real software.
What an age in years actually means
An age is not a day count divided by 365.25. It is a count of anniversaries that have already occurred — which is why it is a whole number that jumps once a year rather than creeping up daily.
The correct method is to compare the month and day, not the totals:
years = this year − birth year, minus 1 if the birthday has not yet occurred this year
Somebody born on 1 October 1990 is 35 on 30 September 2026 and 36 the next morning. Divide days by 365.25 and you get 35.997, which rounds to 36 and is wrong by a day. For anything with a legal threshold — a driving licence, a pension date, a school year cut-off — the anniversary method is the only one that agrees with the authority checking it.
Leap-day birthdays are the edge case, and jurisdictions genuinely differ. Someone born on 29 February 2000 has a legal birthday of 28 February in some places and 1 March in others for the purposes of turning 18. There is no arithmetic that resolves this; you have to know which rule applies.
Working days are a separate problem
Subtracting dates cannot tell you about working days, because weekends and holidays are not a function of the calendar arithmetic. Counting business days means walking the range and testing each date — and public holidays vary by country, by region within a country, and by year, since Easter moves and some holidays shift when they fall on a weekend.
The related trap is "five working days" as a deadline. Does the clock start the day the request arrives or the next working day? Almost every service desk means the next working day, and almost every customer counts from today. It is the inclusive-versus-elapsed argument again, wearing different clothes.
The habits that prevent all of this
State the convention in the same sentence as the number. Store dates as dates, not as timestamps, when the thing you mean is a calendar day — a timestamp carries a timezone and a date near midnight will shift across it. Compare month and day for ages rather than dividing day counts. And when adding months, decide explicitly what 31 January plus one month means in your case before something else decides for you.
The date difference calculator gives both the elapsed and inclusive counts side by side, so the distinction is visible instead of assumed, and the age calculator uses the anniversary method rather than dividing by 365.25.