JavaScript add months to a date by custom handling edge cases

Easiest method to add X months to a date object in JavaScript is by using Date.prototype.setMonth() method. This method has two parameters, month and day. First parameter represent the number of month to add to the date, and the second one (optional) represents the day of the month. More details about the parameters can be found here or here.

The problem with .setMonth() built-in method is that, the way it handles edge cased might not be suited in all projects. To return the final date, the method will add the number of days given by the current day of the month to the 1st day of the new month specified as the parameter, to return the new date .

Let’s see some examples to better understand this behavior:

let date = new Date(2020,00,01);
let finalDate = new Date(date.setMonth(date.getMonth() + 1)); // 1 Jan 2020 + 1 month: 1 February 2020

date = new Date(2020,00,01);
finalDate = new Date(date.setMonth(date.getMonth() + 13)); // 1 Jan 2020 + 13 months: 1 February 2021

date = new Date(2019,00,31);
finalDate = new Date(date.setMonth(date.getMonth() + 1)); // 31 Jan 2019 + 1 month: 3 March 2019

date = new Date(2020,00,31);
finalDate = new Date(date.setMonth(date.getMonth() + 1)); // 31 Jan 2020 + 1 month: 2 March 2020

date = new Date(2020,00,31);
finalDate = new Date(date.setMonth(date.getMonth() + 13)); // 31 Jan 2020 + 13 months: 3 March 2021

date = new Date(2020,01,29);
finalDate = new Date(date.setMonth(date.getMonth() + 1)); // 29 Feb 2020 + 1 month: 29 March 2021

date = new Date(2020,01,29);
finalDate = new Date(date.setMonth(date.getMonth() + 12)); // 29 Feb 2020 + 12 month: 1 March 2021

In the third example, the current value is 31st January 2019 and calling .setMonth() with a value of 1 will return 3rd March 2019. In the fourth example the year is changed to 2020 and .setMonth() will return 2nd March 2020 because it’s a leap year and February has 29 days. Since 31 February does not exists, the month will be incremented by 1 and remaining number of days will be added. This behavior is very common, even Google has a similar way of calculating dates.

How Google calculates one month after 31 January

In case final date needs to be more strict and month not incremented by one when final day number is greater than last day of the month a custom implementation is needed. Here is my idea:

function addMonths(date, amount) {
    const endDate = new Date(date.getTime());
    const originalTimeZoneOffset = endDate.getTimezoneOffset();

    endDate.setMonth(endDate.getMonth() + amount);

    while (monthDiff(date, endDate) > amount) {
        endDate.setDate(endDate.getDate() - 1);
    }

    const endTimeZoneOffset = endDate.getTimezoneOffset();
    const diff = endTimeZoneOffset - originalTimeZoneOffset;
    const finalDate = diff ? endDate.setMinutes(endDate.getMinutes() - diff) : endDate;

    return new Date(finalDate);
}

function monthDiff(from, to) {
    const years = to.getFullYear() - from.getFullYear();
    const months = to.getMonth() - from.getMonth();

    return 12 * years + months;
}

This custom implementation takes int account leap years and daylight saving time (if time part is important). It sets month using build-in method discussed above and if the final month is incremented it subtracts one day at a time until final month is the desired one and day will be the last day from that month.

Bellow are some examples with the output of the custom function. It uses the same dates as those used in the first set of examples.

addMonths(new Date(2020,00,01),1) // 1 Jan 2020 + 1 month: 1 February 2020
addMonths(new Date(2020,00,01),13) // 1 Jan 2020 + 13 months: 1 February 2021
addMonths(new Date(2019,00,31),1) // 31 Jan 2019 + 1 month: 28 February 2019
addMonths(new Date(2020,00,31),1) // 31 Jan 2020 + 1 month: 29 February 2020
addMonths(new Date(2020,00,31),13) // 31 Jan 2020 + 13 months: 28 February 2021
addMonths(new Date(2020,01,29),1) // 29 Feb 2020 + 1 month: 29 March 2020
addMonths(new Date(2020,01,29),12) // 29 Feb 2020 + 12 month: 28 February 2021

On the third line the current value is 31st January 2019 and calling addMonths with a value of 1 will return 28th February 2019 (setMonth would have returned 3rd March 2019). On the next line the year is changed to 2020 and addMonths will return 29th February 2020 because it’s a leap year and February has 29 days (setMonth would have returned 2nd March 2019).

Let me know if this custom implementation for adding months to a JavaScript date helped you ? .

One Comment

  1. FB said:

    Exactly what I was searching for – thanks a lot!

    February 26, 2021
    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *