3.1 KiB
calendar-duration
Purpose
calendar-duration provides duration arithmetic for @suaveplan/datetime: adding and subtracting Duration bags from dates with correct month-end clamping and overflow normalisation. It is the arithmetic engine that powers use cases such as "one month from January 31" and "subtract 90 days".
The module separates year/month arithmetic (done in calendar coordinates to avoid day-count ambiguity) from sub-day arithmetic (done as millisecond offsets on a local-midnight base date).
Features
addDuration— add aDurationbag to a date with month-end clampingsubtractDuration— subtract aDurationbag (negates fields, delegates toaddDuration)- Handles all
DurationUnitfields:years,months,weeks,days,hours,minutes,seconds,milliseconds - Month overflow/underflow normalisation (e.g. month 13 → January of next year)
- Day clamping prevents invalid dates (e.g. Jan 31 + 1 month = Feb 28/29)
Usage: Basic Example
import { addDuration, subtractDuration } from "@suaveplan/datetime";
const jan31 = new Date(2024, 0, 31);
const feb29 = addDuration(jan31, { months: 1 });
console.log(feb29.toDateString()); // Thu Feb 29 2024 (leap year)
const yesterday = subtractDuration(new Date(), { days: 1 });
Usage: Advanced Example
import { addDuration } from "@suaveplan/datetime";
// Combined duration: 1 year, 2 months, 3 days, 4 hours
const base = new Date(2023, 10, 30, 12, 0, 0, 0); // Nov 30 2023 12:00
const result = addDuration(base, {
years: 1,
months: 2,
days: 3,
hours: 4,
});
// Feb 2, 2025 16:00 — Nov 30 + 1y2m = Jan 30 2025, then +3d = Feb 2, +4h = 16:00
// Month underflow normalisation: subtracting months past January
const dec1 = new Date(2024, 11, 1);
const oct = addDuration(dec1, { months: -2 });
console.log(oct.getMonth()); // 9 (October)
API Reference
addDuration(date: DateInput, duration: Duration): Date
Add duration to date. Year/month fields are applied first with overflow/underflow normalisation. Day/week/hour/minute/second/millisecond fields are added as a millisecond offset to the clamped local-midnight date.
Params:
date—Date, ISO string, or epoch numberduration—Durationbag; all fields are optional and default to0
Returns: New Date (does not mutate input).
subtractDuration(date: DateInput, duration: Duration): Date
Subtract duration from date. Internally negates all fields and delegates to addDuration.
Params: same as addDuration.
Returns: New Date.
Implementation Notes
Year and month arithmetic is performed in integer calendar coordinates rather than millisecond arithmetic to avoid the 28–31 day ambiguity. After clamping the day to the last valid day of the resulting month, the remaining duration components (weeks, days, hours, minutes, seconds, milliseconds) are totalled as a single millisecond value and added to a local-midnight Date object. This approach avoids DST-induced errors that would occur if each component were added sequentially in timestamp space.