shiftDate
Shifts a date by a specified number of days, months, or years.
1/**
2 * Shifts a date by a specified number of days, months, or years.
3 *
4 * @param date - The original date.
5 * @param shift - Object with optional shift amounts (can be negative).
6 * @returns A new shifted Date object.
7 *
8 * @example
9 * shiftDate(new Date('2025-01-15'), { days: 5 }) // Jan 20
10 * shiftDate(new Date('2025-01-31'), { months: 1 }) // Feb 28 (safe shift)
11 * shiftDate(new Date('2025-01-01'), { days: -7, years: 1 }) // Dec 25, 2025
12 */
13export function shiftDate(
14 date: Date,
15 shift: { days?: number; months?: number; years?: number }
16): Date {
17 const d = new Date(date);
18
19 if (shift.years) {
20 d.setFullYear(d.getFullYear() + shift.years);
21 }
22
23 if (shift.months) {
24 const currentDay = d.getDate();
25
26 d.setDate(1); // Avoid overflow issues (e.g., Jan 31 → Feb 31 invalid)
27 d.setMonth(d.getMonth() + shift.months);
28
29 const maxDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
30
31 d.setDate(Math.min(currentDay, maxDay));
32 }
33
34 if (shift.days) {
35 d.setDate(d.getDate() + shift.days);
36 }
37
38 return d;
39}
Flexible Time Unit Support
Allows shifting by days, months, and years independently or in combination, supporting a wide range of date calculations.
Safe Month Shifting
Prevents invalid dates by adjusting for end-of-month overflows (e.g., shifting Jan 31 by one month yields Feb 28 or 29).
Immutable Input Handling
Returns a new
Date
instance instead of mutating the input, preserving original data integrity.Handles Negative Shifts
Supports both forward and backward shifts, making it suitable for calculating past and future dates.
Granular Update Order
Updates year → month → day in a way that minimizes edge case bugs (e.g., leap years, month length mismatches).
Tests | Examples
1test('shifts by days forward', () => {
2 expect(shiftDate(new Date('2025-06-27'), { days: 3 }))
3 .toEqual(new Date('2025-06-30'));
4});
5
6test('shifts by days backward', () => {
7 expect(shiftDate(new Date('2025-06-27'), { days: -7 }))
8 .toEqual(new Date('2025-06-20'));
9});
10
11test('shifts by months forward with overflow protection', () => {
12 expect(shiftDate(new Date('2025-01-31'), { months: 1 }))
13 .toEqual(new Date('2025-02-28'));
14});
15
16test('shifts by years forward', () => {
17 expect(shiftDate(new Date('2020-02-29'), { years: 1 }))
18 .toEqual(new Date('2021-02-28'));
19});
20
21test('shifts by combined units', () => {
22 expect(shiftDate(new Date('2025-01-01'), { years: 1, days: -7 }))
23 .toEqual(new Date('2025-12-25'));
24});
25
26test('zero shift returns the same date', () => {
27 const date = new Date('2025-06-27');
28 expect(shiftDate(date, {})).toEqual(date);
29});
Common Use Cases
Date Arithmetic in Scheduling Systems
Add or subtract time from dates for recurring events, deadlines, or reminders.
Generating Valid Future/Past Dates
Compute ranges for UI filters like “30 days ago” or “3 months from now.”
Payroll or Billing Logic
Shift billing cycles or payment dates by calendar intervals.
Data Aggregation Windows
Define time windows for reporting: “shift from current date by 1 year and 1 month.”
Testing and Simulations
Easily simulate future or past date states in unit tests or mock environments.