算法分析:Datepicker 跳过工作日推算实际日期
由于工作原因,好久没有写故事了。
今天写个技术文章,记录下实际中遇到的一个需求点的解决思路。
Jquery Datepicker 是我们工作中经常用到的日期选择控件,其提供了丰富的API基本可以满足我们日常工作中的所有开发功能。然而在一些极为特殊的场景下API可能不能满足我们的开发需求。
在工作中我遇到 这样一个开发需求,抽象出来可以这样描述:
Datapicker 的可选日期往后推N个工作日,跳过周末及法定节假日。
第一步,查找官网API,看是否有相应的API支持。发现有如下Option设置可以禁用周末,代码如下:
<div style="width:100%;height:200px;">
<input type="text" id="datepicker" />
</div>
<script type="text/javascript">
$(function () {
$("#datepicker").datepicker({
beforeShowDay: $.datepicker.noWeekends
});
});
</script>
其运行效果如下:

然后我们再试试往后推10天(测试今天是2020/07/12),测试代码如下:
<script type="text/javascript">
$(function () {
$("#datepicker").datepicker({
beforeShowDay: $.datepicker.noWeekends,
minDate: 10
});
});
</script>

发现其往后推的10天包含了周末及工作日,可用日期为2020/07/22。而实际我们期望跳过周末,10 个工作日之后,应该是2020/07/24。
因此我们需要自己写一个函数来取得排除周末之后仅包含工作日的实际天数。
思路:假设向后推N天,那么可取得这N天的每一天的日期,依此可以判断其是否是周末,假设具有n1天是周末,那么我们应该在N天的基础上补偿n1天。而可以预见,当n1足够大的时候,n1天也会包含周末,假设为n2,那么我们又应该在N+n1的基础上再补偿n2天……同理n2也可能具有周末,于是又回到之前的逻辑,显然,其符合递归的模式,于是我们考虑用递归实现此功能。
代码如下:
/** * * param _curDate: current date * param _originMin: days to deley, not including weekends * param alreadyBackDays: day already delay, including weekends */ function MinDateSkipWeekend(_curDate, _originMin, alreadyBackDays) { var currentDate = _curDate; if (_originMin == 0) { return _originMin; } var backupDays = 0; for (i = 1; i <= _originMin; i++) { var tmpDate = new Date(currentDate.getTime() + 86400000 * i); var tmpDay = tmpDate.getDay(); if (tmpDay == 0 || tmpDay == 6) { backupDays++; } } if (backupDays == 0) { return alreadyBackDays + _originMin; } else { alreadyBackDays += _originMin; var curDay = new Date(currentDate.getTime() + 86400000 * _originMin); return MinDateSkipWeekend(curDay, backupDays, alreadyBackDays); } }
测试代码为:
$(function () {
$("#datepicker").datepicker({
beforeShowDay: $.datepicker.noWeekends,
minDate: MinDateSkipWeekend(new Date(), 6, 0)
});
});
今天是周末,测试结果如图:

如我们预期,今天是2020/07/12,其跳过周末,往后推6个工作日,到20号。算法正常工作。
进一步,假设要排除掉法定节假日等特殊日期呢,我们可对代码进行扩展,将特殊日期以参数的形式传入Function:
最终代码如下:
/**
*
* param _curDate: current date
* param _originMin: days to deley, not including weekends
* param alreadyBackDays: day already delay, including weekends
* param specialDaysArr: special days
*/
function MinDateSkipWeekend(_curDate, _originMin, alreadyBackDays, specialDaysArr) {
var currentDate = _curDate;
if (_originMin == 0) {
return _originMin;
}
var backupDays = 0;
for (i = 1; i <= _originMin; i++) {
var tmpDate = new Date(currentDate.getTime() + 86400000 * i);
var tmpDay = tmpDate.getDay();
if (tmpDay == 0 || tmpDay == 6) {
backupDays++;
}
if (specialDaysArr && specialDaysArr.length > 0) {
for (let it = 0; it < specialDaysArr.length; it++) {
if (specialDaysArr[it].Month == (tmpDate.getMonth() + 1)//For month is indexed from 0
&& specialDaysArr[it].Day == tmpDate.getDate()) {
backupDays++;
break;
}
}
}
}
if (backupDays == 0) {
return alreadyBackDays + _originMin;
} else {
alreadyBackDays += _originMin;
var curDay = new Date(currentDate.getTime() + 86400000 * _originMin);
return MinDateSkipWeekend(curDay, backupDays, alreadyBackDays,specialDaysArr);
}
}
调用方法:
var specialDayArr = [];
specialDayArr.push({ Month: 5, Day: 1 });
specialDayArr.push({ Month: 10, Day: 1 });
$("#datepicker").datepicker({
beforeShowDay: $.datepicker.noWeekends,
minDate: MinDateSkipWeekend(new Date(), 10, 0, specialDayArr)
});
此为个人实践,肯定有更优化的算法,欢迎讨论指正。
浙公网安备 33010602011771号