Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

shiftDate

Shifts a date by a specified number of days, months, or years.

TypeScript
Copied!
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

TypeScript
Copied!
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.

Codebase: Utilities -> Dates -> shiftDate | Yevhen Klymentiev