/**
* @fileOverview Provides helper functions to play with working days
*
* @author Yatish Balaji
* @author Darshit Vora
* @author Anish Lushte
*
* @requires NPM:moment
*/
const moment = require('moment');
/**
* This is a Moment.js plugin. You can customize the week off days, and also declare custom dates for
* holidays (eg: public holidays) to exclude them from being counted as working day(s)
*/
class WorkingDays {
/**
* Working days Calculator
* @class
* @constructor
* @param {object=} config - Configure includeToday, verbose, weekOffDays, dateFormat, customHolidays
* @example
*
* const WorkingDays = require('moment-working-days');
*
* const momentWorkingdays = new WorkingDays({
* includeToday: true, // optional. Default true
* verbose: true, // optional. Default false
* weekOffDays: [0, 6], // optional. Default [0, 6]
* dateFormat: 'DD-MM-YYYY', // optional. Default 'YYYY-MM-DD'
* customHolidays: ['02-12-2019'], // optional
* customWorkingDays: ['07-12-2019'] // optional
* })
*/
constructor(config = {}) {
const {
includeToday = true,
verbose = false,
weekOffDays = [0, 6],
customHolidays = [],
customWorkingDays = [],
dateFormat = 'YYYY-MM-DD'
} = config;
this.includeToday = includeToday;
this.verbose = verbose;
this.weekOffDays = weekOffDays;
this.customHolidays = customHolidays;
this.customWorkingDays = customWorkingDays;
this.dateFormat = dateFormat;
}
/**
* Util to calculate working days considering sequence
* of date(s) - similar to start-stop timer sequence
* @example
* momentWorkingdays.isWorkingday("02-12-2019") // Monday
* // returns 6
* @example
* momentWorkingdays.isWorkingday("01-12-2019") // Sunday
* // returns false
* @example
* momentWorkingdays.isWorkingday("06-12-2019") // Friday
* // returns true
* @memberof WorkingDays
* @param {string} date Date string
*
* @returns {boolean} returns if its a working day or not
*/
isWorkingday(date) {
date = moment.isMoment(date) ? date : moment(date, this.dateFormat);
if (
this.customWorkingDays.some(holiday => moment(holiday, this.dateFormat).isSame(date))
) {
if (this.verbose)
console.log(date.format(this.dateFormat), 'is a Custom Working Day');
return true;
} else if (
this.weekOffDays.includes(+date.format('d'))
) {
if (this.verbose)
console.log(date.format(this.dateFormat), 'is a', date.format('ddd'));
return false;
} else if (
this.customHolidays.some(holiday => moment(holiday, this.dateFormat).isSame(date))
) {
if (this.verbose)
console.log(date.format(this.dateFormat), 'is a Custom Holiday');
return false;
}
return true;
}
/**
* Takes array of dates.
* @example
* momentWorkingdays.getWorkingDays([
* "29-11-2019", "03-12-2019",
* "07-12-2019", "12-12-2019"
* ])
* // returns 6
* @example
* momentWorkingdays.getWorkingDays([
* "29-11-2019", "03-12-2019",
* "07-12-2019"
* ])
* // returns 3
* @example
* momentWorkingdays.getWorkingDays([
* "29-11-2019"
* ])
* // returns 6
* @memberof WorkingDays
* @param {string[]} data Array of Date string(s)
*
* @returns {number} returns number of working days
*/
getWorkingDays(data) {
try {
let tatDays = 0;
let previousEndDate = null;
if (data.length % 2 !== 0) data.push(new Date());
for (let i = 0; i < data.length - 1; i = i + 2) {
let start = moment(data[i], this.dateFormat).startOf('day');
const end = moment(data[i + 1], this.dateFormat).startOf('day');
if (start.isSame(end)) {
if (!previousEndDate || previousEndDate.isBefore(start)) {
tatDays = tatDays + 1;
}
} else {
if (previousEndDate && start.isSameOrBefore(previousEndDate)) {
start = moment(previousEndDate).add(1, 'days');
}
if (start.isSame(end)) {
if (!previousEndDate || previousEndDate.isBefore(start)) {
tatDays = tatDays + 1;
}
} else {
let from = start;
while (from.isSameOrBefore(end)) {
if (this.isWorkingday(from)) {
tatDays++;
}
from = moment(from, this.dateFormat).add(1, 'days');
}
}
}
previousEndDate = end;
}
if (!this.includeToday && tatDays > 0) tatDays = tatDays - 1;
if (this.verbose) console.log(`Working Days: ${tatDays} day(s)`);
return tatDays;
} catch (err) {
if (this.verbose) console.log(err);
return 0;
}
}
/**
* Takes date string and number of days.
* @example
* momentWorkingdays.addWorkingDays("06-12-2019", 2) // Friday
* // returns 10-12-2019
* // Tuesday
* @memberof WorkingDays
* @param {string} date Date string
* @param {number} noOfDays Number of days
*
* @returns {string} returns date string after adding noOfDays working days
*/
addWorkingDays(date, noOfDays = 1) {
try {
let data = moment(date, this.dateFormat);
while (noOfDays > 0) {
const nextDate = data.add(1, 'days');
data = nextDate;
if (this.isWorkingday(data)) {
noOfDays = noOfDays - 1;
}
}
return data.format(this.dateFormat);
} catch (err) {
if (this.verbose) console.error(err);
return moment().format(this.dateFormat);
}
}
/**
* Takes date string.
* @example
* momentWorkingdays.nextWorkingDay("06-12-2019") // Friday
* // returns 09-12-2019
* // Monday
* @memberof WorkingDays
* @param {string} date Date string
*
* @returns {string} returns next working day
*/
nextWorkingDay(date) {
return this.addWorkingDays(date, 1)
}
/**
* Takes date string and number of days.
* @example
* momentWorkingdays.subtractWorkingDays("09-12-2019", 2) // Monday
* // returns 05-12-2019
* // Thursday
* @memberof WorkingDays
* @param {string} date Date string
* @param {number} noOfDays Number of days
*
* @returns {string} returns date string after subtracting noOfDays working days
*/
subtractWorkingDays(date, noOfDays = 1) {
try {
let data = moment(date, this.dateFormat);
while (noOfDays > 0) {
const nextDate = data.subtract(1, 'days');
data = nextDate;
if (this.isWorkingday(nextDate)) {
noOfDays = noOfDays - 1;
}
}
return data.format(this.dateFormat);
} catch (err) {
if (this.verbose) console.error(err);
return moment().format(this.dateFormat);
}
}
/**
* Takes date string.
* @example
* momentWorkingdays.prevWorkingDay("09-12-2019") // Monday
* // returns 06-12-2019
* // Friday
* @memberof WorkingDays
* @param {string} date Date string
*
* @returns {string} returns previous working day
*/
prevWorkingDay(date) {
return this.subtractWorkingDays(date, 1)
}
/**
* Sets weekOffDays.
* @example
* momentWorkingdays.setWeekOffDays([0, 1, 2, 3, 4, 6])
* @memberof WorkingDays
* @param {string[]} weekOffDays
* @returns {WorkingDays} returns WorkingDays object
*/
setWeekOffDays(weekOffDays) {
this.weekOffDays = weekOffDays;
return this;
}
/**
* Sets customWorkingDays.
* @example
* momentWorkingdays.customWorkingDays(['07-12-2019'])
* @memberof WorkingDays
* @param {string[]} customWorkingDays
* @returns {WorkingDays} returns WorkingDays object
*/
setCustomWorkingDays(customWorkingDays) {
this.customWorkingDays = customWorkingDays;
return this;
}
}
module.exports = WorkingDays;