How to calculate Number of 'Working days' between two dates in Javascript using moment.js?
Asked Answered
C

8

22

How to calculate the number of working days between two dates in JavaScript using moment.js. I have a working formula that calculate these days, but the formula does not satisfies all conditions:

here is my code:

var start= moment(data[x].start_date);
                var end= moment(data[x].est_end_date);
                var difference= end.diff(start, 'days');
                var workingDays= Math.round((difference/7)*5);
//data[x] is for iterating over a loop

I get five days per 7 days here because saturday and sunday are considered as NON-Working days, but this formula will fail if the count of the days were started from sunday or saturday.

Please any one could help in this regard that what necessary changes must be done in order to avoid this problem.

Choosey answered 10/2, 2015 at 6:1 Comment(0)
D
26

Just divide it into 3 parts.. first week, last week and the in between, something like this:

function workday_count(start,end) {
  var first = start.clone().endOf('week'); // end of first week
  var last = end.clone().startOf('week'); // start of last week
  var days = last.diff(first,'days') * 5 / 7; // this will always multiply of 7
  var wfirst = first.day() - start.day(); // check first week
  if(start.day() == 0) --wfirst; // -1 if start with sunday 
  var wlast = end.day() - last.day(); // check last week
  if(end.day() == 6) --wlast; // -1 if end with saturday
  return wfirst + Math.floor(days) + wlast; // get the total
} //              ^ EDIT: if days count less than 7 so no decimal point

The test code

var ftest = {date:'2015-02-0',start:1,end:7};
var ltest = {date:'2015-02-2',start:2,end:8};
var f = 'YYYY-MM-DD';
for(var z=ftest.start; z<=ftest.end; ++z) {
  var start = moment(ftest.date + z);
  for(var y=ltest.start; y<=ltest.end; ++y) {
    var end = moment(ltest.date + y);
    var wd = workday_count(start,end);
    console.log('from: '+start.format(f),'to: '+end.format(f),'is '+wd+' workday(s)');
  }
}

Output of test code:

from: 2015-02-01 to: 2015-02-22 is 15 workday(s)
from: 2015-02-01 to: 2015-02-23 is 16 workday(s)
from: 2015-02-01 to: 2015-02-24 is 17 workday(s)
from: 2015-02-01 to: 2015-02-25 is 18 workday(s)
from: 2015-02-01 to: 2015-02-26 is 19 workday(s)
from: 2015-02-01 to: 2015-02-27 is 20 workday(s)
from: 2015-02-01 to: 2015-02-28 is 20 workday(s)
from: 2015-02-02 to: 2015-02-22 is 15 workday(s)
from: 2015-02-02 to: 2015-02-23 is 16 workday(s)
from: 2015-02-02 to: 2015-02-24 is 17 workday(s)
from: 2015-02-02 to: 2015-02-25 is 18 workday(s)
from: 2015-02-02 to: 2015-02-26 is 19 workday(s)
from: 2015-02-02 to: 2015-02-27 is 20 workday(s)
from: 2015-02-02 to: 2015-02-28 is 20 workday(s)
from: 2015-02-03 to: 2015-02-22 is 14 workday(s)
from: 2015-02-03 to: 2015-02-23 is 15 workday(s)
from: 2015-02-03 to: 2015-02-24 is 16 workday(s)
from: 2015-02-03 to: 2015-02-25 is 17 workday(s)
from: 2015-02-03 to: 2015-02-26 is 18 workday(s)
from: 2015-02-03 to: 2015-02-27 is 19 workday(s)
from: 2015-02-03 to: 2015-02-28 is 19 workday(s)
from: 2015-02-04 to: 2015-02-22 is 13 workday(s)
from: 2015-02-04 to: 2015-02-23 is 14 workday(s)
from: 2015-02-04 to: 2015-02-24 is 15 workday(s)
from: 2015-02-04 to: 2015-02-25 is 16 workday(s)
from: 2015-02-04 to: 2015-02-26 is 17 workday(s)
from: 2015-02-04 to: 2015-02-27 is 18 workday(s)
from: 2015-02-04 to: 2015-02-28 is 18 workday(s)
from: 2015-02-05 to: 2015-02-22 is 12 workday(s)
from: 2015-02-05 to: 2015-02-23 is 13 workday(s)
from: 2015-02-05 to: 2015-02-24 is 14 workday(s)
from: 2015-02-05 to: 2015-02-25 is 15 workday(s)
from: 2015-02-05 to: 2015-02-26 is 16 workday(s)
from: 2015-02-05 to: 2015-02-27 is 17 workday(s)
from: 2015-02-05 to: 2015-02-28 is 17 workday(s)
from: 2015-02-06 to: 2015-02-22 is 11 workday(s)
from: 2015-02-06 to: 2015-02-23 is 12 workday(s)
from: 2015-02-06 to: 2015-02-24 is 13 workday(s)
from: 2015-02-06 to: 2015-02-25 is 14 workday(s)
from: 2015-02-06 to: 2015-02-26 is 15 workday(s)
from: 2015-02-06 to: 2015-02-27 is 16 workday(s)
from: 2015-02-06 to: 2015-02-28 is 16 workday(s)
from: 2015-02-07 to: 2015-02-22 is 10 workday(s)
from: 2015-02-07 to: 2015-02-23 is 11 workday(s)
from: 2015-02-07 to: 2015-02-24 is 12 workday(s)
from: 2015-02-07 to: 2015-02-25 is 13 workday(s)
from: 2015-02-07 to: 2015-02-26 is 14 workday(s)
from: 2015-02-07 to: 2015-02-27 is 15 workday(s)
from: 2015-02-07 to: 2015-02-28 is 15 workday(s)
Discretionary answered 10/2, 2015 at 6:45 Comment(4)
For a date range of 5 days, it gives 5.7 days.Syzygy
In fact, for a date range of less than 7 days, the value is in decimal figures.Syzygy
I had the same problem. It was due to Summer Time differences between the beginning and the end of the week. The fix is below.Kunming
Just add Math.floor(days) would solve the problem '__') thanks for spotting that out guysDiscretionary
P
17

I use a simple function to accomplish that. Maybe is not the most efficient but it works. It doesn't require Moment.js. it's just Javascript.

function getNumWorkDays(startDate, endDate) {
    var numWorkDays = 0;
    var currentDate = new Date(startDate);
    while (currentDate <= endDate) {
        // Skips Sunday and Saturday
        if (currentDate.getDay() !== 0 && currentDate.getDay() !== 6) {
            numWorkDays++;
        }
        currentDate = currentDate.addDays(1);
    }
    return numWorkDays;
}

To addDays, I use the following function:

Date.prototype.addDays = function (days) {
    var date = new Date(this.valueOf());
    date.setDate(date.getDate() + days);
    return date;
};
Pinkard answered 22/2, 2018 at 22:34 Comment(0)
O
7

I found kokizzu's answer didn't work if days were in the same week (e.g. sun to sat), so settled for this instead :

    function calcBusinessDays(startDate, endDate) { 
      var day = moment(startDate);
      var businessDays = 0;

      while (day.isSameOrBefore(endDate,'day')) {
        if (day.day()!=0 && day.day()!=6) businessDays++;
        day.add(1,'d');
      }
      return businessDays;
    }
Overlying answered 3/8, 2017 at 12:3 Comment(1)
works fine across multiple months for me... perhaps you can share your code / error ?Overlying
T
6

I've made an adaptation to Kokizzu answer, in order to fix a Summer Time problem. In Brazilian Timezone (GMT -3), the difference between 17/10/2017 and 18/10/2017 was -2.71 instead of 2.

The start of the week was 15/10/2017 00:00 UTC or 14/10/2017 21:00-03:00

The end of the week was 22/10/2017 00:00 UTC or 21/10/2017 22:00-02:00 (Summer Time)

Therefore, instead of 7, the difference between "first" and "last" variable in days was 6 (or 8, depending on your Timezone).

The code fixed is below:

start = moment(start).utc().add(start.utcOffset(), 'm'); // Ignore timezones
end = moment(end).utc().add(end.utcOffset(), 'm'); // Ignore timezones

var first = start.clone().endOf('week'); // end of first week
var last = end.clone().startOf('week'); // start of last week

// Fixing Summer Time problems
firstCorrection = moment(first).utc().add(60, 'm').toDate(); //
var days = last.diff(firstCorrection,'days') * 5 / 7; // this will always multiply of 7

var wfirst = first.day() - start.day(); // check first week
if(start.day() == 0) --wfirst; // -1 if start with sunday
var wlast = end.day() - last.day(); // check last week
if(end.day() == 6) --wlast; // -1 if end with saturday
return wfirst + days + wlast; // get the total (subtract holidays if needed)
Tumefaction answered 17/10, 2017 at 13:12 Comment(0)
N
2

By using momentjs, it's possible this way:

private calculateWorkdays(startDate: Date, endDate: Date): number {
// + 1 cause diff returns the difference between two moments, in this case the day itself should be included.

const totalDays: number = moment(endDate).diff(moment(startDate), 'days') + 1;
const dayOfWeek = moment(startDate).isoWeekday();
let totalWorkdays = 0;

for (let i = dayOfWeek; i < totalDays + dayOfWeek; i++) {
    if (i % 7 !== 6 && i % 7 !== 0) {
      totalWorkdays++;
    }
  }
  return totalWorkdays;
}
Nautical answered 14/8, 2019 at 13:20 Comment(1)
Welcome to SO! Answers are only permitted in English on stackoverflow.com. For more information, read this postTalbert
H
1

you can try this(without loop):

  function getBusinessDays(endDate, startDate) {
    var lastDay = moment(endDate);
    var firstDay = moment(startDate);
    let calcBusinessDays = 1 + (lastDay.diff(firstDay, 'days') * 5 -
      (firstDay.day() - lastDay.day()) * 2) / 7;

    if (lastDay.day() == 6) calcBusinessDays--;//SAT
    if (firstDay.day() == 0) calcBusinessDays--;//SUN

    return calcBusinessDays;
  }

Original Source

Hospodar answered 14/1, 2019 at 7:1 Comment(0)
D
1

I have been used a useful library moment-business-days coupled with moment

NPM Link of Moment-business-days

NPM Link of Moment

Sample of how many business days between

const diff = moment('05-15-2017', 'MM-DD-YYYY').businessDiff(moment('05-08-2017','MM-DD-YYYY'));
// diff = 5
Defensible answered 6/8, 2020 at 13:44 Comment(0)
M
0

Business days should be stored in a table in a database and served thru an API, there is no way to calculate business days. Holidays vary in countries, even in the USA each state may have different holidays, each company may have different business days

Melcher answered 23/3, 2022 at 21:7 Comment(1)
this is probably the best and most accurate answer and should be at the very top, at least as a statement to make people like me aware of possible shortcomings..Wastebasket

© 2022 - 2024 — McMap. All rights reserved.