The highest-rated solution is less verbose, but uses a loop to add the days one at a time, rather than calculating the number of calendar days to add ahead of time.
Other solutions that have been posted try to calculate, but all of them either do not work, or have edge cases they don't handle (e.g., what if the original date happens to fall on a weekend?)
The suggestion to use moment-business-days is good if you want to handle holidays across different locales and leverage other features of the library, but (IMO) it is overkill if we are just sticking to what OP asks for, which is "add X days, skipping Saturdays and Sundays."
Anyway, I had to do this on a project recently, and this is the solution I came up with:
const addBusinessDaysToDate = (date, businessDays) => {
// bit of type checking, and making sure not to mutate inputs ::
const momentDate = date instanceof moment ? date.clone() : moment(date);
if (!Number.isSafeInteger(businessDays) || businessDays <= 0) {
// handle these situations as appropriate for your program; here I'm just returning the moment instance ::
return momentDate;
} else {
// for each full set of five business days, we know we want to add 7 calendar days ::
const calendarDaysToAdd = Math.floor(businessDays / 5) * 7;
momentDate.add(calendarDaysToAdd, "days");
// ...and we calculate the additional business days that didn't fit neatly into groups of five ::
const remainingDays = businessDays % 5;
// if the date is currently on a weekend, we need to adjust it back to the most recent Friday ::
const dayOfWeekNumber = momentDate.day();
if (dayOfWeekNumber === 6) {
// Saturday -- subtract one day ::
momentDate.subtract(1, "days");
} else if (dayOfWeekNumber === 0) {
// Sunday -- subtract two days ::
momentDate.subtract(2, "days");
}
// now we need to deal with any of the remaining days calculated above ::
if ((momentDate.day() + remainingDays) > 5) {
// this means that adding the remaining days has caused us to hit another weekend;
// we must account for this by adding two extra calendar days ::
return momentDate.add(remainingDays + 2, "days");
} else {
// we can just add the remaining days ::
return momentDate.add(remainingDays, "days");
}
}
};
And here's the result of a quick little test script:
_________________________________________
Original Date :: 2023-10-28
Plus 3 Business Days :: 2023-11-01
Plus 10 Business Days :: 2023-11-10
Plus 14 Business Days :: 2023-11-16
Plus 15 Business Days :: 2023-11-17
Plus 22 Business Days :: 2023-11-28
_________________________________________
Original Date :: 2023-10-29
Plus 3 Business Days :: 2023-11-01
Plus 10 Business Days :: 2023-11-10
Plus 14 Business Days :: 2023-11-16
Plus 15 Business Days :: 2023-11-17
Plus 22 Business Days :: 2023-11-28
_________________________________________
Original Date :: 2023-10-30
Plus 3 Business Days :: 2023-11-02
Plus 10 Business Days :: 2023-11-13
Plus 14 Business Days :: 2023-11-17
Plus 15 Business Days :: 2023-11-20
Plus 22 Business Days :: 2023-11-29
_________________________________________
Original Date :: 2023-10-31
Plus 3 Business Days :: 2023-11-03
Plus 10 Business Days :: 2023-11-14
Plus 14 Business Days :: 2023-11-20
Plus 15 Business Days :: 2023-11-21
Plus 22 Business Days :: 2023-11-30
_________________________________________
Original Date :: 2023-11-01
Plus 3 Business Days :: 2023-11-06
Plus 10 Business Days :: 2023-11-15
Plus 14 Business Days :: 2023-11-21
Plus 15 Business Days :: 2023-11-22
Plus 22 Business Days :: 2023-12-01
_________________________________________
Original Date :: 2023-11-02
Plus 3 Business Days :: 2023-11-07
Plus 10 Business Days :: 2023-11-16
Plus 14 Business Days :: 2023-11-22
Plus 15 Business Days :: 2023-11-23
Plus 22 Business Days :: 2023-12-04
_________________________________________
Original Date :: 2023-11-03
Plus 3 Business Days :: 2023-11-08
Plus 10 Business Days :: 2023-11-17
Plus 14 Business Days :: 2023-11-23
Plus 15 Business Days :: 2023-11-24
Plus 22 Business Days :: 2023-12-05