add/subtract business days in Javascript

Solution 1:

Datageek's solution helped me but I needed to augment it. This still doesn't do holidays but does do working days with the option of including Sat and/or Sun, and does support adding negative days:-

function AddWorkingDays(datStartDate, lngNumberOfWorkingDays, blnIncSat, blnIncSun) {
    var intWorkingDays = 5;
    var intNonWorkingDays = 2;
    var intStartDay = datStartDate.getDay(); // 0=Sunday ... 6=Saturday
    var intOffset;
    var intModifier = 0;

    if (blnIncSat) { intWorkingDays++; intNonWorkingDays--; }
    if (blnIncSun) { intWorkingDays++; intNonWorkingDays--; }
    var newDate = new Date(datStartDate)
    if (lngNumberOfWorkingDays >= 0) {
        // Moving Forward
        if (!blnIncSat && blnIncSun) {
            intOffset = intStartDay;
        } else {
            intOffset = intStartDay - 1;
        }
        // Special start Saturday rule for 5 day week
        if (intStartDay == 6 && !blnIncSat && !blnIncSun) {
            intOffset -= 6;
            intModifier = 1;
        }
    } else {
        // Moving Backward
        if (blnIncSat && !blnIncSun) {
            intOffset = intStartDay - 6;
        } else {
            intOffset = intStartDay - 5;
        }
        // Special start Sunday rule for 5 day week
        if (intStartDay == 0 && !blnIncSat && !blnIncSun) {
            intOffset++;
            intModifier = 1;
        }
    }
    // ~~ is used to achieve integer division for both positive and negative numbers
    newDate.setTime(datStartDate.getTime() + (new Number((~~((lngNumberOfWorkingDays + intOffset) / intWorkingDays) * intNonWorkingDays) + lngNumberOfWorkingDays + intModifier)*86400000));
    return newDate;
}

Solution 2:

Have a look at the following implementation. Sourced from about.com

addWeekdays = function(date, dd) {
  var wks = Math.floor(dd/5);
  var dys = dd.mod(5);
  var dy = this.getDay();
  if (dy === 6 && dys > -1) {
     if (dys === 0) {dys-=2; dy+=2;}
     dys++; dy -= 6;
  }
  if (dy === 0 && dys < 1) {
    if (dys === 0) {dys+=2; dy-=2;}
    dys--; dy += 6;
  }
  if (dy + dys > 5) dys += 2;
  if (dy + dys < 1) dys -= 2;
  date.setDate(date.getDate()+wks*7+dys);
}

var date = new Date();
addWeekdays(date, 9);

Solution 3:

(Updated) I've put this algorithm through its paces and it seems stable, though it does use recursion for holiday processing:

holidays = [new Date("2/13/2019"), new Date("2/19/2019")];

function addWorkdays(workdays, startDate) {
  //Make adjustments if the start date is on a weekend
  let dayOfWeek = startDate.getDay();
  let adjustedWorkdays = Math.abs(workdays);
  if (0 == dayOfWeek || 6 == dayOfWeek) {
    adjustedWorkdays += (Math.abs((dayOfWeek % 5) + Math.sign(workdays)) % 2) + 1;
    dayOfWeek = (dayOfWeek - 6) * -1;
  }
  let endDate = new Date(startDate);
  endDate.setDate(endDate.getDate() + (((Math.floor(((workdays >= 0 ? dayOfWeek - 1 : 6 - dayOfWeek) + adjustedWorkdays) / 5) * 2) + adjustedWorkdays) * (workdays < 0 ? -1 : 1)));
  //If we cross holidays, recompute our end date accordingly
  let numHolidays = holidays.reduce(function(total, holiday) { return (holiday >= Math.min(startDate, endDate) && holiday <= Math.max(startDate, endDate)) ? total + 1 : total; }, 0);
  if (numHolidays > 0) {
    endDate.setDate(endDate.getDate() + Math.sign(workdays));
    return addWorkdays((numHolidays - 1) * Math.sign(workdays), endDate);
  } else return endDate;
}