Add N months to YYYY-MM date in JavaScript

I’m writing a function that adds N months to the YYYY-MM date.

Wonder if it could be written better, cleaner, without then need to handle the special case when months == 0? I’m not looking for some JS library function that would do that, I’m interested in the algorithm itself.

function add_months(time, months_to_add) {
  const mcount = time.month + months_to_add
  let years    = time.year + Math.floor(mcount / 12)
  let months   = mcount % 12
  if (months == 0) {
    years  -= 1
    months = 12
  }
  return { years, months }
}

console.log([
  add_months({ year: 2001, month: 1 }, 2),
  add_months({ year: 2001, month: 11 }, 1)
])

Answer

The main challenge is due to the fact that we count months between 1 and 12, while a modulo of 12 have a valid range of 0 to 11.

We can solve this by first subtracting 1 from the month count so that its range can fit within the modulo’s range, and add the 1 back after the modulo calculation.

function addMonths(time, monthsToAdd) {
  const offsetCycles = Math.ceil(Math.abs(monthsToAdd) / 12);
  const zeroBasedMonth = time.month + monthsToAdd - 1 + offsetCycles * 12;
  const additionalYears = Math.floor(zeroBasedMonth / 12) - offsetCycles;

  return {
    year: time.year + additionalYears,
    month: (zeroBasedMonth % 12) + 1,
  };
}

console.log([
  addMonths({ year: 2001, month: 1 }, 2),
  addMonths({ year: 2001, month: 11 }, 2),
  addMonths({ year: 2001, month: 11 }, 1),
  addMonths({ year: 2001, month: 11 }, 12),
  addMonths({ year: 2001, month: 11 }, 14),
  addMonths({ year: 2001, month: 1 }, -1),
  addMonths({ year: 2001, month: 1 }, -13),
]);

However, I am not sure whether it is worthwhile to eliminate the if block like this.