Reputation: 132
I'm setting a default follow-up date two days from current date, which currently works:
const Notify = moment().add(2, 'days').toDate();
However, I would like to exclude weekends. So I installed moment WeekDay, but I can't seem to get it to work with adding days to the current date. The documentation calls for:
moment().weekday(0)
But I can't get that to work with adding in two days forward. Any ideas?
Upvotes: 13
Views: 28590
Reputation: 44
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
Upvotes: 1
Reputation: 1
// using pure JS
function addBusinessDays(originalDate, numDaysToAdd) {
const Sunday = 0;
const Saturday = 6;
let daysRemaining = numDaysToAdd;
const newDate = originalDate;
while (daysRemaining > 0) {
newDate.setDate(newDate.getDate() + 1);
if (newDate.getDay() !== 0 && newDate.getDay() !== 6) {
// skip sunday & saturday
daysRemaining--;
}
}
return newDate;
}
var dt = new Date(); // get date
var business_days = 8;
newDate = addBusinessDays(dt, business_days);
console.log(newDate.toString());
Upvotes: 0
Reputation: 1588
const addWorkingDays = (date: Moment, days: number) => {
let newDate = date.clone();
for (let i = 0; i < days; i++) {
if (newDate.isoWeekday() !== 6 && newDate.isoWeekday() !== 7) {
newDate = newDate.add(1, "days");
} else {
newDate = newDate.add(1, "days");
i--;
}
}
return newDate.format("YYYY/MM/DD");
};
Upvotes: 2
Reputation: 31
var moment = require("moment")
function addWorkingDay(date, days){
let daysToAdd = days
const today = moment(date);
const nextWeekStart = today.clone().add(1, 'week').weekday(1);
const weekEnd = today.clone().weekday(5);
const daysTillWeekEnd = Math.max(0, weekEnd.diff(today, 'days'));
if(daysTillWeekEnd >= daysToAdd) return today.clone().add(daysToAdd, 'days');
daysToAdd = daysToAdd - daysTillWeekEnd - 1;
return nextWeekStart.add(Math.floor(daysToAdd/5), 'week').add(daysToAdd % 5, 'days')
}
Upvotes: 1
Reputation: 3314
This will do it based on any starting date, and without a costly loop. You calculate the number of weekend days you need to skip over, then just offset by the number of weekdays and weekends, together.
function addWeekdays(year, month, day, numberOfWeekdays) {
var originalDate = year + '-' + month + '-' + day;
var futureDate = moment(originalDate);
var currentDayOfWeek = futureDate.day(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
var numberOfWeekends = Math.floor((currentDayOfWeek + numberOfWeekdays - 1) / 5); // calculate the number of weekends to skip over
futureDate.add(numberOfWeekdays + numberOfWeekends * 2, 'days'); // account for the 2 days per weekend
return futureDate;
}
Upvotes: 1
Reputation: 94
I think this code will be faster:
var businessDays = 10;
var days = businessDays + Math.floor((Math.min(moment().day(),5)+businessDays)/6)*2;
moment.add(days, 'days');
Upvotes: 0
Reputation: 16163
This solution is simple, easy to follow, and works well for me:
function addBusinessDays(originalDate, numDaysToAdd) {
const Sunday = 0;
const Saturday = 6;
let daysRemaining = numDaysToAdd;
const newDate = originalDate.clone();
while (daysRemaining > 0) {
newDate.add(1, 'days');
if (newDate.day() !== Sunday && newDate.day() !== Saturday) {
daysRemaining--;
}
}
return newDate;
}
Upvotes: 19
Reputation: 18515
You could also not use external lib and do a simple function like one of these two:
const WEEKEND = [moment().day("Saturday").weekday(), moment().day("Sunday").weekday()]
const addBusinessDays1 = (date, daysToAdd) => {
var daysAdded = 0,
momentDate = moment(new Date(date));
while (daysAdded < daysToAdd) {
momentDate = momentDate.add(1, 'days');
if (!WEEKEND.includes(momentDate.weekday())) {
daysAdded++
}
}
return momentDate;
}
console.log(addBusinessDays1(new Date(), 7).format('MM/DD/YYYY'))
console.log(addBusinessDays1('09-20-2018', 3).format('MM/DD/YYYY'))
// This is the somewhat faster version
const addBusinessDays2 = (date, days) => {
var d = moment(new Date(date)).add(Math.floor(days / 5) * 7, 'd');
var remaining = days % 5;
while (remaining) {
d.add(1, 'd');
if (d.day() !== 0 && d.day() !== 6)
remaining--;
}
return d;
};
console.log(addBusinessDays2(new Date(), 7).format('MM/DD/YYYY'))
console.log(addBusinessDays2('09-20-2018', 3).format('MM/DD/YYYY'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
They are slightly modified from this post and I think are a good alternative to external library you have to carry/deal with (assuming this is the only part you need and not other features of that lib).
Upvotes: 7
Reputation: 4983
Try: moment-business-days
It should help you.
Example:
var momentBusinessDays = require("moment-business-days")
momentBusinessDays('20-09-2018', 'DD-MM-YYYY').businessAdd(3)._d
Result:
Tue Sep 25 2018 00:00:00 GMT+0530 (IST)
Upvotes: 11