Summary#
These shift a DateTime by an amount. AddDays, AddHours and AddMinutes take a fractional amount
and are exact time arithmetic. AddMonths and AddYears are calendar-aware: they land on the same
day-of-month where it exists and clamp to the last day of the month where it does not. A negative amount
moves backward.
Signature#
d.AddDays(<number> n) -> DateTime // n may be fractional (0.5 = 12 hours)
d.AddHours(<number> n) -> DateTime
d.AddMinutes(<number> n) -> DateTime
d.AddMonths(<int> n) -> DateTime // calendar month, day CLAMPED
d.AddYears(<int> n) -> DateTime // calendar year, Feb 29 CLAMPEDDescription#
They work INSIDE a query — the database does the arithmetic#
AddDays and its siblings are not client-only helpers. Written inside a Where, each lowers to a real SQL
interval, so the comparison happens in the database and only the matching rows come back:
// the whole due-list, computed by the database — one filtered read
live var due = Plant.Where(p => p.LastWateredAt.AddDays(p.WaterEveryDays) <= DateTime.UtcNow).ToList();⚠ The alternative is a full table read. Plant.ToList() followed by an in-memory Where fetches every row on
every page load and filters them in the browser — correct on ten rows, and a table scan on ten thousand. If the
predicate can be written over the entity, write it there: see Where / Single / Count.
AddDays / AddHours / AddMinutes are exact time arithmetic#
They add a fixed length of time and roll components over: AddHours(11) on 13:45 crosses midnight into the
next day, AddMinutes(90) adds an hour and a half. The amount may be fractional — AddDays(0.5) is
twelve hours, AddMinutes(0.5) is thirty seconds — and it may be negative to go backward.
AddMonths / AddYears CLAMP the day — the rule that surprises people#
A calendar month is not a fixed number of days, so "one month after January 31st" has no exact answer.
AddMonths and AddYears resolve it by clamping the day to the last valid day of the target month:
| Call | Result | Why |
|---|---|---|
Jan 31 . AddMonths(1) | Feb 29 (2024) | Feb has no 31st; clamp to the last day — and 2024 is a leap year |
Jan 31 . AddMonths(13) | Feb 28 (2025) | thirteen months on, into a non-leap year |
Feb 29 . AddYears(1) | Feb 28 | the target year has no Feb 29 |
It never spills over into the next month — you will not get "March 2nd" from adding a month to January 31st. The time of day is preserved across all of these.
The member syntax d.AddMonths(1) is the everyday spelling; the compiler knows these as Date.AddDays,
Date.AddHours, Date.AddMinutes, Date.AddMonths and Date.AddYears, and they can also be written in that
call form (Date.AddMonths(d, 1)).
Every one is a pure function of the value, so it runs in the browser with no round trip (execution side). To add a duration instead of a fixed unit, add a TimeSpan (durations).
Examples#
DateTime DueNextMonth(DateTime placed) {
return placed.AddMonths(1);
}[Test]
void Date_arithmetic() {
var d = DateTime.New(2024, 3, 15, 13, 45, 30);
// exact, fractional, and reversible
Assert.Equal(16, d.AddDays(1).Day);
Assert.Equal(1, d.AddDays(0.5).Hour); // +12h → 01:45 next day
Assert.Equal(16, d.AddHours(11).Day); // crosses midnight
Assert.Equal(0, d.AddHours(11).Hour);
// AddMonths / AddYears clamp the day to the month end
var jan31 = DateTime.New(2024, 1, 31);
Assert.Equal(2, jan31.AddMonths(1).Month);
Assert.Equal(29, jan31.AddMonths(1).Day); // Feb 29 in a leap year, NOT March 2nd
Assert.Equal(28, jan31.AddMonths(13).Day); // Feb 28 in 2025
var feb29 = DateTime.New(2024, 2, 29);
Assert.Equal(28, feb29.AddYears(1).Day); // 2025 has no Feb 29
}See also#
- Reading a date — Year, Month, Day, Hour, Minute, Second, DayOfWeek, Date — reading the year/month/day back off the result
- TimeSpan (durations) — adding a duration (
d + TimeSpan.FromHours(2)) rather than a fixed unit - Current time (DateTime.UtcNow, DurableClock.Now) — the current instant to do arithmetic from