Chronia provides a comprehensive suite of date arithmetic functions that allow you to add, subtract, and calculate differences between dates with precision and flexibility. All functions support both Date objects and numeric timestamps, maintain immutability, and handle invalid inputs gracefully by returning Invalid Date or NaN.
| Function | Description |
|---|---|
addYears |
Adds a specified number of years to a date |
addMonths |
Adds a specified number of months to a date |
addDays |
Adds a specified number of days to a date |
addHours |
Adds a specified number of hours to a date |
addMinutes |
Adds a specified number of minutes to a date |
addSeconds |
Adds a specified number of seconds to a date |
addMilliseconds |
Adds a specified number of milliseconds to a date |
| Function | Description |
|---|---|
subYears |
Subtracts a specified number of years from a date |
subMonths |
Subtracts a specified number of months from a date |
subDays |
Subtracts a specified number of days from a date |
subHours |
Subtracts a specified number of hours from a date |
subMinutes |
Subtracts a specified number of minutes from a date |
subSeconds |
Subtracts a specified number of seconds from a date |
subMilliseconds |
Subtracts a specified number of milliseconds from a date |
| Function | Description | Return Value |
|---|---|---|
diffYears |
Calculates the difference in complete years between two dates | Integer or NaN |
diffMonths |
Calculates the difference in complete months between two dates | Integer or NaN |
diffDays |
Calculates the difference in complete days between two dates | Integer or NaN |
diffHours |
Calculates the difference in complete hours between two dates | Integer or NaN |
diffMinutes |
Calculates the difference in complete minutes between two dates | Integer or NaN |
diffSeconds |
Calculates the difference in complete seconds between two dates | Integer or NaN |
diffMilliseconds |
Calculates the difference in milliseconds between two dates | Integer or NaN |
All arithmetic functions in this category share the following characteristics:
All functions accept both Date objects and numeric timestamps:
import { addDays, diffHours } from "chronia";
// Date objects
addDays(new Date(2025, 0, 1), 7); // Returns: Date for January 8, 2025
diffHours(new Date(2025, 0, 1, 14, 0), new Date(2025, 0, 1, 10, 0)); // Returns: 4
// Timestamps
addDays(1704067200000, 7); // Returns: Date for January 8, 2025
diffHours(1704117600000, 1704103200000); // Returns: 4
// Mixed types
addDays(new Date(2025, 0, 1), 7);
diffHours(new Date(2025, 0, 1, 14, 0), 1704103200000);
Add and subtract functions always return new Date objects without modifying the original:
import { addMonths } from "chronia";
const original = new Date(2025, 0, 15);
const result = addMonths(original, 3);
console.log(original); // Date: January 15, 2025 (unchanged)
console.log(result); // Date: April 15, 2025 (new object)
All functions validate inputs and return Invalid Date (for add/sub) or NaN (for diff) for invalid inputs without throwing exceptions:
import { addDays, diffDays } from "chronia";
// Invalid date input
addDays(new Date("invalid"), 5); // Returns: Invalid Date
diffDays(new Date("invalid"), new Date(2025, 0, 1)); // Returns: NaN
// Invalid amount input
addDays(new Date(2025, 0, 1), NaN); // Returns: Invalid Date
diffDays(new Date(2025, 0, 1), NaN); // Returns: NaN
// Infinity
addDays(new Date(2025, 0, 1), Infinity); // Returns: Invalid Date
diffDays(Infinity, new Date(2025, 0, 1)); // Returns: NaN
Add and subtract functions truncate fractional amounts toward zero using Math.trunc():
import { addDays, addHours } from "chronia";
const baseDate = new Date(2025, 0, 1);
addDays(baseDate, 2.9); // Adds 2 days (2.9 → 2)
addDays(baseDate, -2.9); // Subtracts 2 days (-2.9 → -2)
addDays(baseDate, 0.5); // Adds 0 days (0.5 → 0, no change)
addHours(baseDate, 3.7); // Adds 3 hours (3.7 → 3)
Add functions accept negative amounts (effectively subtracting), and subtract functions accept negative amounts (effectively adding):
import { addMonths, subMonths } from "chronia";
const date = new Date(2025, 3, 15); // April 15, 2025
addMonths(date, -3); // Returns: January 15, 2025 (same as subMonths(date, 3))
subMonths(date, -3); // Returns: July 15, 2025 (same as addMonths(date, 3))
Month and year arithmetic functions handle month-end overflow by adjusting to the last valid day of the target month:
import { addMonths, subMonths } from "chronia";
// March 31 + 1 month → April 30 (April has 30 days)
const march31 = new Date(2025, 2, 31);
addMonths(march31, 1); // Returns: April 30, 2025
// March 31 - 1 month → February 28 (non-leap year)
subMonths(march31, 1); // Returns: February 28, 2025
// March 31, 2024 - 1 month → February 29, 2024 (leap year)
const march31_2024 = new Date(2024, 2, 31);
subMonths(march31_2024, 1); // Returns: February 29, 2024
Addition Functions (addYears, addMonths, addDays, etc.):
Subtraction Functions (subYears, subMonths, subDays, etc.):
Difference Functions (diffYears, diffMonths, diffDays, etc.):
| Scenario | Recommended Function | Reason |
|---|---|---|
| Schedule event 7 days from now | addDays(now, 7) |
Day-level precision |
| Calculate age in years | diffYears(now, birthdate) |
Year-level granularity |
| Billing cycle (monthly) | addMonths(startDate, 1) |
Month-based periods |
| Meeting reminder (15 minutes before) | subMinutes(meetingTime, 15) |
Minute-level precision |
| Session timeout (30 seconds) | addSeconds(sessionStart, 30) |
Second-level precision |
| Performance measurement | diffMilliseconds(endTime, startTime) |
Millisecond accuracy |
| Quarterly report dates | addMonths(startDate, 3) |
Quarter = 3 months |
| Time-sensitive cache expiry | addMinutes(now, 5) |
Minute-based expiry |
| Historical data (1 year ago) | subYears(now, 1) |
Year-based lookback |
| Event duration in hours | diffHours(endTime, startTime) |
Hour-based duration |
import { addDays, addHours, subMinutes } from "chronia";
// Schedule a task 7 days from now
const taskDeadline = addDays(new Date(), 7);
// Set meeting 2 hours from now
const meetingTime = addHours(new Date(), 2);
// Send reminder 15 minutes before meeting
const reminderTime = subMinutes(meetingTime, 15);
import { addMonths, subMonths, diffMonths } from "chronia";
// Next billing date (monthly subscription)
const nextBilling = addMonths(lastBillingDate, 1);
// Calculate months subscribed
const monthsSubscribed = diffMonths(new Date(), subscriptionStartDate);
// Find billing date 3 months ago
const pastBilling = subMonths(new Date(), 3);
import { addDays, subDays } from "chronia";
// Generate a 7-day range for a weekly report
function generateWeekRange(endDate: Date): Date[] {
const range: Date[] = [];
for (let i = 6; i >= 0; i--) {
range.push(subDays(endDate, i));
}
return range;
}
const weekRange = generateWeekRange(new Date());
// Returns: [7 days ago, 6 days ago, ..., today]
import { diffHours, diffMinutes } from "chronia";
// Calculate work hours
const workStart = new Date(2025, 0, 15, 9, 0);
const workEnd = new Date(2025, 0, 15, 17, 30);
const hoursWorked = diffHours(workEnd, workStart); // Returns: 8
// Calculate meeting duration in minutes
const meetingStart = new Date(2025, 0, 15, 14, 0);
const meetingEnd = new Date(2025, 0, 15, 15, 30);
const duration = diffMinutes(meetingEnd, meetingStart); // Returns: 90
import { subMonths, subYears, diffDays } from "chronia";
// Get data from the last 6 months
const sixMonthsAgo = subMonths(new Date(), 6);
// Year-over-year comparison
const lastYear = subYears(new Date(), 1);
// Calculate days since event
const daysSinceEvent = diffDays(new Date(), eventDate);
import { diffYears } from "chronia";
// Calculate age in years
function calculateAge(birthdate: Date): number {
return diffYears(new Date(), birthdate);
}
const birthdate = new Date(1990, 5, 15);
const age = calculateAge(birthdate); // Returns: 34 (as of 2025)
import { diffMilliseconds } from "chronia";
// Measure execution time
const startTime = new Date();
// ... perform operation ...
const endTime = new Date();
const executionTime = diffMilliseconds(endTime, startTime);
console.log(`Operation took ${executionTime}ms`);
import { diffMinutes, diffHours, diffDays } from "chronia";
function formatRelativeTime(date: Date): string {
const now = new Date();
const minutes = diffMinutes(now, date);
const hours = diffHours(now, date);
const days = diffDays(now, date);
if (minutes < 1) return "Just now";
if (minutes < 60) return `${minutes} minute${minutes > 1 ? "s" : ""} ago`;
if (hours < 24) return `${hours} hour${hours > 1 ? "s" : ""} ago`;
return `${days} day${days > 1 ? "s" : ""} ago`;
}
const posted = new Date(2025, 0, 15, 10, 30);
console.log(formatRelativeTime(posted)); // "3 hours ago"
import { addDays, isValid } from "chronia";
function safeAddDays(date: Date | number, days: number): Date | null {
const result = addDays(date, days);
return isValid(result) ? result : null;
}
const validResult = safeAddDays(new Date(), 7); // Returns: Date 7 days from now
const invalidResult = safeAddDays(new Date("invalid"), 7); // Returns: null
import { diffDays, addDays } from "chronia";
// Check if date is within a range
function isWithinDays(
date: Date,
referenceDate: Date,
maxDays: number,
): boolean {
const diff = Math.abs(diffDays(date, referenceDate));
return diff <= maxDays;
}
const today = new Date(2025, 0, 15);
const eventDate = new Date(2025, 0, 20);
isWithinDays(eventDate, today, 7); // Returns: true (5 days difference)
import { addMonths, addDays } from "chronia";
// Calculate date 3 months and 15 days from now
const baseDate = new Date(2025, 0, 1);
const targetDate = addDays(addMonths(baseDate, 3), 15);
// Returns: April 16, 2025 (Jan 1 + 3 months + 15 days)
import { addDays, diffDays } from "chronia";
// Iterate through each day in a range
function forEachDay(
startDate: Date,
endDate: Date,
callback: (date: Date) => void,
): void {
const totalDays = diffDays(endDate, startDate);
for (let i = 0; i <= totalDays; i++) {
callback(addDays(startDate, i));
}
}
const start = new Date(2025, 0, 1);
const end = new Date(2025, 0, 7);
forEachDay(start, end, (date) => {
console.log(date.toISOString());
});
// Logs each day from January 1-7, 2025
import { diffDays, diffHours, diffMinutes } from "chronia";
function formatDuration(startDate: Date, endDate: Date): string {
const days = diffDays(endDate, startDate);
const hours = diffHours(endDate, startDate) % 24;
const minutes = diffMinutes(endDate, startDate) % 60;
const parts = [];
if (days > 0) parts.push(`${days}d`);
if (hours > 0) parts.push(`${hours}h`);
if (minutes > 0) parts.push(`${minutes}m`);
return parts.join(" ") || "0m";
}
const start = new Date(2025, 0, 1, 10, 0);
const end = new Date(2025, 0, 3, 14, 30);
console.log(formatDuration(start, end)); // "2d 4h 30m"
addMilliseconds, diffMilliseconds) are the fastest as they involve simple addition/subtraction// All add/sub functions follow this signature pattern:
function add<Unit>(date: Date | number, amount: number): Date;
function sub<Unit>(date: Date | number, amount: number): Date;
// All diff functions follow this signature pattern:
function diff<Unit>(dateLeft: Date | number, dateRight: Date | number): number;
All arithmetic functions follow a consistent error handling pattern:
Math.trunc()NaN instead of throwing errorsNaNNaNNaNdateLeft is after dateRightdateLeft is before dateRight