How to exclude weekends between two dates using Moment.js

Viewed 76284

I am trying to exclude weekends in my JavaScript code. I use moment.js and having difficulty choosing the right variable for 'days'.

So far I have thought that I need to exclude day 6 (saturday) and day 0 (sunday) by changing the weekday variable to count from day 1 to day 5 only. But not sure how it changes.

My jsfiddle is shown here: FIDDLE

HTML:

<div id="myContent">
<input type="radio" value="types" class="syncTypes" name="syncTypes"> <td><label for="xshipping.xshipping1">Free Shipping: (<span id="fsv1" value="5">5</span> to <span id="fsv2" value="10">10</span> working days)</label> </td><br>
    <div id="contacts" style="display:none;border:1px #666 solid;padding:3px;top:15px;position:relative;margin-bottom:25px;">     
    Contacts
</div>
<input type="radio" value="groups" class="syncTypes" name="syncTypes"> <td><label for="xshipping.xshipping2">Express Shipping: (<span id="esv1" value="3">3</span> to <span id="esv2" value="4">4</span> working days)</label> </td>    
<div id="groups" style="display:none;border:1px #666 solid;padding:3px;top:15px;position:relative">     
    Groups
</div>
</div>

JavaScript:

var a = 5; //Free shipping between a
var b = 10;//and b
var c = 3;//Express shipping between c
var d = 4;//and d    
var now = moment();    
var f = "Your item will be delivered between " + now.add("days",a).format("Do MMMM") + " and " + now.add("days",b).format("Do MMMM");
var g = "Your item will be delivered between " + now.add("days".c).format("Do MMMM") + " and " + now.add("days",d).format("Do MMMM");

var h = document.getElementById('contacts');
h.innerHTML = g

var i = document.getElementById('groups');
i.innerHTML = f

$(function() {
    $types = $('.syncTypes');
    $contacts = $('#contacts');
    $groups = $('#groups');
    $types.change(function() {
        $this = $(this).val();
        if ($this == "types") {
            $groups.slideUp(300);
            $contacts.delay(200).slideDown(300);
        }
        else if ($this == "groups") {
            $contacts.slideUp(300);
            $groups.delay(200).slideDown(300);
        }
    });
});
11 Answers

I know this question was posted long ago, but in case somebody bump on this, here is optimized solution using moment.js:

function getBusinessDays(startDate, endDate){
  var startDateMoment = moment(startDate);
  var endDateMoment = moment(endDate)
  var days = Math.round(startDateMoment.diff(endDateMoment, 'days') - startDateMoment .diff(endDateMoment, 'days') / 7 * 2);
  if (endDateMoment.day() === 6) {
    days--;
  }
  if (startDateMoment.day() === 7) {
    days--;
  }
  return days;
}

d1 and d2 are moment dates passed as an argument to calculateBusinessDays

calculateBusinessDays(d1, d2) {
    const days = d2.diff(d1, "days") + 1;
    let newDay: any = d1.toDate(),
    workingDays: number = 0,
    sundays: number = 0,
    saturdays: number = 0;
    for (let i = 0; i < days; i++) {
        const day = newDay.getDay();
        newDay = d1.add(1, "days").toDate();
        const isWeekend = ((day % 6) === 0);
        if (!isWeekend) {
            workingDays++;
        } 
        else {
            if (day === 6) saturdays++;
            if (day === 0) sundays++;
        }
    }
        console.log("Total Days:", days, "workingDays", workingDays, "saturdays", saturdays, "sundays", sundays);
    return workingDays;
}
const calcBusinessDays = (d1, d2) => {
    // Calc all days used including last day ( the +1 )
    const days = d2.diff(d1, 'days') + 1;

    console.log('Days:', days);

    // how many full weekends occured in this time span
    const weekends = Math.floor( days / 7 );

    console.log('Full Weekends:', weekends);

    // Subtract all the weekend days
    let businessDays = days - ( weekends * 2);

    // Special case for weeks less than 7
    if( weekends === 0 ){
    const cur = d1.clone();
    for( let i =0; i < days; i++ ){
        if( cur.day() === 0 || cur.day() === 6 ){
        businessDays--;
        }
        cur.add(1, 'days')
    }
    } else {
    // If the last day is a saturday we need to account for it
    if (d2.day() === 6 ) {
        console.log('Extra weekend day (Saturday)');
        businessDays--;
    }
    // If the first day is a sunday we need to account for it
    if (d1.day() === 0) {
        console.log('Extra weekend day (Sunday)');
        businessDays--;
    }
    }

    console.log('Business days:', businessDays);
    return businessDays;
}

This can be done without looping between all dates in between.

// get nb of weekend days
var startDateMonday = startDate.clone().startOf('isoWeek');
var endDateMonday = endDate.clone().startOf('isoWeek');

var nbWeekEndDays = 2 * endDateMonday.diff(startDateMonday, 'days') / 7;
var isoDayStart = startDate.isoWeekday();
if (isoDayStart > 5) // starts during the weekend
{
    nbWeekEndDays -= (8 - isoDayStart);
}
var isoDayEnd = endDate.isoWeekday();
if (isoDayEnd > 5) // ends during the weekend
{
    nbWeekEndDays += (8 - isoDayEnd);
}

// if we want to also exlcude holidays
var startOfStartDate = startDate.clone().startOf('day');
var nbHolidays = holidays.filter(h => {
    return h.isSameOrAfter(startOfStartDate) && h.isSameOrBefore(endDate);
}).length;

var duration = moment.duration(endDate.diff(startDate));
duration = duration.subtract({ days: nbWeekEndDays + nbHolidays });

var nbWorkingDays = Math.floor(duration.asDays()); // get only nb of complete days

I am iterating from start date to end date and only counting days which are weekdays.

const calculateBusinessDays = (start_date, end_date) => {
        const d1 = start_date.clone();
        let num_days = 0;
        while(end_date.diff(d1.add(1, 'days')) > 0) {
                if ([0, 6].includes(d1.day())) {
                        // Don't count the days
                } else {
                        num_days++;
                }
        }
        return num_days;
}
Related