Calculate dates considering public holidays and weekends with moment.js and moment-business-days

Tags: , , ,



The aim of this application is always to indicate the 16th business day of each month (means taking into account working days and public holidays).

For this I use moment-business-days, which is a moment.js plugin. It calculates dates and considers working days and (preconfigured) public holidays. I use it as follows and for some dates it gives me the right result, but for some of them not. I can’t see the mistake in my code:

myHolidays = [
  { holidayDate: "2020-06-01", description: "holiday 1" },
  { holidayDate: "2020-06-02", description: "holiday 2" },
  { holidayDate: "2020-06-03", description: "holiday 3" },  
  { holidayDate: "2020-06-06", description: "weekend saturday" },
  { holidayDate: "2020-06-07", description: "weekend sunday" },
  { holidayDate: "2020-06-11", description: "holiday 6" },
  { holidayDate: "2020-06-13", description: "weekend saturday" },
  { holidayDate: "2020-06-14", description: "weekend sunday" },
  { holidayDate: "2020-06-20", description: "weekend saturday" },
  { holidayDate: "2020-06-21", description: "weekend sunday" },
  { holidayDate: "2020-06-27", description: "weekend saturday" },
  { holidayDate: "2020-06-28", description: "weekend sunday" },
];

moment.updateLocale('de', {
   holidays: myHolidays.map(i => i.holidayDate),
   holidayFormat: 'YYYY-MM-DD'
});

var startDate = moment("2020-01-01").startOf('month')
var endDate = moment("2020-12-01")
var eachMonthWith16 = [];

function generate16InEachMonth() {
  let monthsList = []
  // loop by months
  while (endDate > startDate) {
    monthsList.push(new Date(moment(this.startDate).businessAdd(15, 'days')))
    startDate = startDate.add(1, "month").startOf('month')
  }
  return monthsList
}

console.log(generate16InEachMonth())
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-business-days/1.2.0/index.js"></script>

In this example I get as a result the following dates, which sometimes is correct and sometimes not:

Month  Actual  Target  Correct
Jan    22      22      yes
Feb    24      21      no
March  23      20      no
April  22      22      yes
May    22      22      yes
June   25      26      no
July   22      22      yes
...

Is there a quick way to solve this or is there any comparable datetime processing library, which can work with business days and calculate them flawlessly?

Answer

The reason the code wouldn’t work for all months is that the first day of the month is counted as a business day by default.

A condition to distinguish whether the first day actually is a business day or not should suffice (adding 16 business days when the first one is not a business day itself).

function generate16InEachMonth() {
  let monthsList = []
  // loop by months
  while (endDate > startDate) {
    var daysToAdd = (startDate.isBusinessDay()) ? 15 : 16
    monthsList.push(new Date(moment(this.startDate).businessAdd(daysToAdd, 'days')))
    startDate = startDate.add(1, "month").startOf('month')
  }
  return monthsList
}



Source: stackoverflow