Summary#
These shift a DateTime by an amount. AddDays, AddHours, AddMinutes, AddSeconds and AddMilliseconds
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.AddSeconds(<number> n) -> DateTime // DateTime only — not on DateOnly or TimeOnly
d.AddMilliseconds(<number> n) -> DateTime // DateTime only
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();
// a freshness window — the readings from the last ten minutes, also one filtered read
var recent = Reading.Where(r => r.TakenAt >= DateTime.UtcNow.AddSeconds(-600)).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 through AddMilliseconds 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, AddSeconds(-600) goes back ten minutes. The amount may be
fractional — AddDays(0.5) is twelve hours, AddMinutes(0.5) is thirty seconds, AddMilliseconds(0.25) is a
quarter of a millisecond — and it may be negative to go backward.
A fractional amount becomes whole ticks (a tick is 100 nanoseconds) by truncating toward zero, exactly as .NET
does — it is never rounded to a millisecond. One consequence is worth knowing: 1.2 is not exactly representable as a
double, so AddSeconds(1.2) lands one tick short of 1.2 seconds. Where that tick matters, say the amount in whole
units of a smaller one — AddMilliseconds(1200) is exact.
AddSeconds and AddMilliseconds belong to DateTime#
As in C#, they exist on a DateTime only. A DateOnly has no time of day to move by a second, and a TimeOnly
moves by AddHours or AddMinutes — so the compiler refuses the call on either, and its message shows the spelling
that works (t.AddMinutes(n / 60.0), or d.ToDateTime(new TimeOnly(0, 0)).AddSeconds(n)).
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.AddSeconds, Date.AddMilliseconds, 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 (Crossing the network). To add a duration instead of a fixed unit, add a TimeSpan (durations).
Examples#
DateTime DueNextMonth(DateTime placed) {
return placed.AddMonths(1);
}DateTime FreshSince() {
return DateTime.UtcNow.AddSeconds(-600);
}[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);
// seconds and milliseconds
Assert.Equal(35, d.AddSeconds(-600).Minute); // 13:45:30 → 13:35:30
Assert.Equal(47, d.AddSeconds(90).Minute); // → 13:47:00
Assert.Equal(31, d.AddMilliseconds(1500).Second); // → 13:45:31.5
// 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, DayOfYear, 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