Skip to content

Instantly share code, notes, and snippets.

@chanakaDe
Created January 21, 2019 09:20
Show Gist options
  • Save chanakaDe/dc316d1488c179f41df1acbca8b1ce65 to your computer and use it in GitHub Desktop.
Save chanakaDe/dc316d1488c179f41df1acbca8b1ce65 to your computer and use it in GitHub Desktop.
/**
* Weekdays calculator
* @author Andrei Kondratev [[email protected]]
*/
;if ( typeof require !== 'undefined' )
{
moment = require('moment');
}
(function(moment) {
var week_day_array = [];
function WeekDayCalc (rangeStart,rangeEnd,weekdays,exclusions,inclusions,useIsoWeekday) {
this.rangeStart = moment(rangeStart);
this.rangeEnd = moment(rangeEnd);
this.exclusions = exclusions;
this.inclusions = inclusions;
this.useIsoWeekday = (useIsoWeekday==true);
if(this.rangeStart.isAfter(this.rangeEnd)) {
throw new WeekDayCalcException('rangeStart is after rangeEnd');
}
this.weekdays = parseWeekdays(weekdays, this.useIsoWeekday);
}
WeekDayCalc.prototype.calculate = function() {
var weekDaysCount = 0;
var rangeStartWeekEnd = this.rangeStart.clone().endOf('week');
var rangeEndWeekStart = this.rangeEnd.clone().startOf('week');
if (rangeEndWeekStart.diff(rangeStartWeekEnd,'days')<30 || this.exclusions || this.inclusions) {
weekDaysCount = this.calculateIterative(this.rangeStart,this.rangeEnd,this.weekdays,this.exclusions, this.inclusions);
} else {
/* a little optimisation for longer time intervals - it works faster with intervals longer than one year */
var wholeWeeksDiff = Math.round(rangeEndWeekStart.diff(rangeStartWeekEnd,'weeks',true));
weekDaysCount += wholeWeeksDiff*this.weekdays.length;
weekDaysCount += this.calculateIterative(this.rangeStart,rangeStartWeekEnd,this.weekdays);
weekDaysCount += this.calculateIterative(rangeEndWeekStart,this.rangeEnd,this.weekdays);
}
// return weekDaysCount;
// return week_day_array;
return [weekDaysCount , week_day_array];
};
WeekDayCalc.prototype.calculateIterative = function(rangeStart,rangeEnd,weekdays,exclusions, inclusions) {
var weekDaysCount = 0, day = rangeStart.clone();
var str_exclusions = parseSet(exclusions);
var str_inclusions = parseSet(inclusions);
while(day.valueOf()<=rangeEnd.valueOf()) {
var weekdayFunc = this.useIsoWeekday?'isoWeekday':'weekday';
var dayString = day.format("YYYY-MM-DD");
var included = str_inclusions.length != 0 && str_inclusions.indexOf(dayString)>=0;
if (included || ( (weekdays.indexOf(day[weekdayFunc]())>=0) && (str_exclusions.length==0 || str_exclusions.indexOf(dayString)<0) )) {
console.log("output : ",dayString);
week_day_array.push(dayString);
weekDaysCount++;
}
day.add(1, 'day');
}
return weekDaysCount;
};
Function.prototype.construct = function(aArgs) {
/*Monkey patching Function prototype to have construct method*/
var fConstructor = this, fNewConstr = function() { fConstructor.apply(this, aArgs); };
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
function WeekDayCalcException (message) {
this.message = message;
this.name = 'WeekDayCalcException';
}
WeekDayCalcException.prototype = new Error;
WeekDayCalc.prototype.WeekDayCalcException = WeekDayCalcException;
function DaysSetConverter (rangeStart, weekdays, exclusions, inclusions, useIsoWeekday) {
this.rangeStart = moment(rangeStart);
this.useIsoWeekday = (useIsoWeekday==true);
this.exclusions = exclusions;
this.inclusions = inclusions;
this.weekdays = parseWeekdays(weekdays, this.useIsoWeekday);
}
/**
* Calculates the date of {workdays} from now excluding today
* @param daysToAdd
* @returns {Suite|*}
*/
DaysSetConverter.prototype.calculate = function(daysToAdd) {
var daysLeft = daysToAdd;
var resultDate = this.rangeStart.clone();
var str_exclusions = parseSet(this.exclusions);
var str_inclusions = parseSet(this.inclusions);
var weekdayFunc = this.useIsoWeekday?'isoWeekday':'weekday';
if (daysLeft>=0){
/* positive value - add days */
while (daysLeft > 0) {
resultDate.add(1, 'day');
var included = str_inclusions.length != 0 && str_inclusions.indexOf(resultDate.format("YYYY-MM-DD"))>=0;
if (included || ((this.weekdays.indexOf(resultDate[weekdayFunc]()) >= 0) && (str_exclusions.length == 0 || str_exclusions.indexOf(resultDate.format("YYYY-MM-DD")) < 0))) {
daysLeft--;
}
}
} else {
/* negative value - subtract days */
while (daysLeft < 0) {
resultDate.subtract(1, 'day');
var included = str_inclusions.length != 0 && str_inclusions.indexOf(resultDate.format("YYYY-MM-DD"))>=0;
if (included || ((this.weekdays.indexOf(resultDate[weekdayFunc]()) >= 0) && (str_exclusions.length == 0 || str_exclusions.indexOf(resultDate.format("YYYY-MM-DD")) < 0))) {
daysLeft++;
}
}
}
return resultDate;
};
function DaysSetConverterException (message) {
this.message = message;
this.name = 'DaysSetConverterException';
}
DaysSetConverterException.prototype = new Error;
DaysSetConverter.prototype.DaysSetConverterException = DaysSetConverterException;
var parseWeekdays = function(weekdays, useIsoWeekday) {
var validWeekdays = [];
if (!weekdays) {
throw new WeekDayCalcException('weekdays must be defined');
}
if (weekdays.length > 7) {
throw new WeekDayCalcException("Weekdays array exceeding week length of 7 days");
}
for (var i=0;i<weekdays.length;i++) {
var weekday = weekdays[i];
if (useIsoWeekday) {
if (isNaN(weekday)) throw new WeekDayCalcException("isoWeekDayCalc accepts weekdays as numbers only, try using weekdayCalc if you need a locale aware behaviour");
if (weekday<1 || weekday>7) throw new WeekDayCalcException("The weekday is out of 1 to 7 range");
} else if(!isNaN(weekday)){
if (weekday<0 || weekday>6) throw new WeekDayCalcException("The weekday is out of 0 to 6 range");
} else {
weekday = moment().day(weekday).weekday();
}
if (validWeekdays.indexOf(weekday)>=0) {
throw new WeekDayCalcException("Weekdays set contains duplicate weekday");
}
validWeekdays.push(weekday);
}
return validWeekdays;
};
var parseSet = function(set) {
var str_exclusions = [];
if (set) {
var i=0, l = set.length;
for (;i<l;i++) {
str_exclusions.push(moment(set[i]).format("YYYY-MM-DD"));
}
}
return str_exclusions;
};
WeekDayCalc.calculateWeekdays = function(that, inArgs, useIsoWeekday) {
var rangeStart, rangeEnd, weekdays, exclusions, inclusions;
useIsoWeekday = useIsoWeekday?true:false;
switch (inArgs.length) {
case 5:
exclusions = inArgs[3];
inclusions = inArgs[4];
case 4:
exclusions = inArgs[3];
/* Fall-through to three args */
case 3:
rangeStart = moment(inArgs[0]).startOf('day');
rangeEnd = moment(inArgs[1]).endOf('day');
weekdays = inArgs[2];
break;
case 2:
rangeStart = that;
rangeEnd = inArgs[0];
weekdays = inArgs[1];
break;
case 1:
var arg = inArgs[0];
if (arg && arg.rangeEnd && arg.weekdays) {
rangeStart = arg.rangeStart ? moment(arg.rangeStart).startOf('day') : that;
rangeEnd = moment(arg.rangeEnd).endOf('day');
weekdays = arg.weekdays;
exclusions = arg.exclusions;
inclusions = arg.inclusions;
} else {
rangeStart = that.clone().startOf('year');
rangeEnd = that.clone().endOf('year');
weekdays = arg;
}
break;
default:
new WeekDayCalcException('unexpected arguments length '+inArgs.length+'. Expecting 1 to 4 args');
}
if(rangeStart.isAfter(rangeEnd)) {
var trueEnd = rangeStart.clone();
rangeStart = rangeEnd.clone();
rangeEnd = trueEnd;
}
var calc = WeekDayCalc.construct([rangeStart, rangeEnd, weekdays, exclusions, inclusions, useIsoWeekday]);
return calc.calculate();
};
DaysSetConverter.calculateDate = function(that, inArgs, useIsoWeekday) {
var days, exclusions, inclusions, weekdaysSet;
useIsoWeekday = useIsoWeekday?true:false;
var rangeStart = that;
switch (inArgs.length) {
case 4:
exclusions = inArgs[2];
inclusions = inArgs[3];
case 3:
exclusions = inArgs[2];
/* Fall-through to two args*/
case 2:
days = inArgs[0];
weekdaysSet = inArgs[1];
break;
case 1:
var arg = inArgs[0];
if (arg && (arg.days!=undefined || arg.workdays!=undefined) ) {
if (arg.days!=undefined && arg.workdays!=undefined) throw new DaysSetConverterException("days and weekdays args should not be used together, because weekdays is an alias of days");
days = arg.days?arg.days:arg.workdays;
weekdaysSet = arg.weekdays?arg.weekdays:[1,2,3,4,5];
exclusions = arg.exclusions;
inclusions = arg.inclusions;
} else {
days = arg;
}
break;
default:
new DaysSetConverterException('unexpected arguments length '+inArgs.length+'. Expecting 1 to 3 args');
}
var calc = DaysSetConverter.construct([that, weekdaysSet, exclusions, inclusions, useIsoWeekday]);
return calc.calculate(days);
};
/**
* Calculate weekdays with locale aware weekdays
*/
moment.fn.weekdayCalc = function(){
return WeekDayCalc.calculateWeekdays(this, arguments);
};
/**
* Calculate weekdays with moment#isoWeekdays function, where 1 is always monday and 7 is always Sunday
*/
moment.fn.isoWeekdayCalc = function() {
return WeekDayCalc.calculateWeekdays(this, arguments, true);
};
/**
* Calculates the date of {workdays} from now excluding today
* For example 4 workdays from Wed 19 Aug 2015 is a Tue 25 Aug 2015
* workdays set is Mon-Fri, please use addSetWeekdays if you have a different set
*/
moment.fn.addWorkdays = function(days, exclusions, inclusions) {
return DaysSetConverter.calculateDate(this, [days, [1,2,3,4,5], exclusions, inclusions]);
};
/**
* Calculates how many calendar days within {workdays}
* For example 4 workdays from Wed 19 Aug 2015 is 6 calendar days
* workdays set is Mon-Fri, please use setWeekdaysToCalendarDays if you have a different set
*/
moment.fn.workdaysToCalendarDays = function(days, exclusions, inclusions) {
var date = DaysSetConverter.calculateDate(this, [days, [1,2,3,4,5], exclusions, inclusions]);
return date.diff(this,'days');
};
moment.fn.addWeekdaysFromSet = function() {
return DaysSetConverter.calculateDate(this, arguments);
};
moment.fn.weekdaysFromSetToCalendarDays = function() {
var date = DaysSetConverter.calculateDate(this, arguments);
return date.diff(this,'days');
};
moment.fn.isoAddWeekdaysFromSet = function() {
return DaysSetConverter.calculateDate(this, arguments, true);
};
moment.fn.isoWeekdaysFromSetToCalendarDays = function() {
var date = DaysSetConverter.calculateDate(this, arguments, true);
return date.diff(this,'days');
};
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = WeekDayCalc :
typeof define === 'function' && define.amd ? define(WeekDayCalc) :
this.WeekDayCalc = WeekDayCalc;
})(moment);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment