使用 JavaScript 计算工作日内的预期交货日期(考虑假期)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21297323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calculate an expected delivery date (accounting for holidays) in business days using JavaScript?
提问by Mark Giblin
After revisiting this script, and some modifications, the following is available to allow a user to add a feature that calculates the expected delivery date.
重新访问此脚本并进行一些修改后,以下内容可用于允许用户添加计算预期交货日期的功能。
// array of ISO YYYY-MM-DD format dates
publicHolidays = {
uk:["2020-01-01","2020-04-10","2020-04-13","2020-05-08","2020-05-25",
"2020-08-03","2020-08-31","2020-12-25","2020-12-28"],
usa:["2020-01-01","2020-01-20","2020-02-14","2020-02-17","2020-04-10",
"2020-04-12","2020-05-10","2020-05-25","2020-06-21","2020-07-03",
"2020-07-04","2020-09-07","2020-10-12","2020-10-31","2020,11,11",
"2020-11-26","2020-12-25"]
}
// check if there is a match in the array
Date.prototype.isPublicHoliday = function( data ){// we check for a public holiday
if(!data) return 1;
return data.indexOf(this.toISOString().slice(0,10))>-1? 0:1;
}
// calculation of business days
Date.prototype.businessDays = function( d, holidays ){
var holidays = holidays || false, t = new Date( this ); // copy date.
while( d ){ // we loop while d is not zero...
t.setDate( t.getDate() + 1 ); // set a date and test it
switch( t.getDay() ){ // switch is used to allow easier addition of other days of the week
case 0: case 6: break;// sunday & saturday
default: // check if we are a public holiday or not
d -= t.isPublicHoliday( holidays );
}
}
return t.toISOString().slice(0,10); // just the YYY-MM-DD
}
// dummy var, could be a form field input
OrderDate = "2020-02-12";
// test with a UK holiday date
var deliveryDate = new Date(OrderDate).businessDays(7, publicHolidays.usa);
// expected output 2020-02-25
console.log("Order date: %s, Delivery date: %s",OrderDate,deliveryDate );
Order date: 2020-02-12, Delivery date: 2020-02-25
订货日期:2020-02-12,交货日期:2020-02-25
The prototype is written to allow inputs from forms (HTML5 forms) of date type inputs as they are already in an ISO YYYY-MM-DD format and the output is formatted as such should that be needing to update a particular field.
该原型被编写为允许来自日期类型输入的表单(HTML5 表单)的输入,因为它们已经是 ISO YYYY-MM-DD 格式,并且如果需要更新特定字段,输出将被格式化。
The typical use would be...
典型的用途是...
var delDate = new Date( ISOdate ).businessDays( addBusinessDays, holidayData );
where the delDate is an ISO format date, eg, 2020-01-01
其中 delDate 是 ISO 格式的日期,例如 2020-01-01
采纳答案by Mark Giblin
Thanks for your input guys, I had a long hard re-think over the approach I was making for this and came up with this little number...
感谢您的投入,我花了很长时间重新思考我为此所做的方法并想出了这个小数字......
var businessDays = 7, counter = 0; // set to 1 to count from next business day
while( businessDays>0 ){
var tmp = new Date();
var startDate = new Date();
tmp.setDate( startDate .getDate() + counter++ );
switch( tmp.getDay() ){
case 0: case 6: break;// sunday & saturday
default:
businessDays--;
};
}
The idea was to start with the business days and count backwards to zero for each day encountered that fell in to the range of a business day. This use of switch would enable a person to declare a day in the week as a non-business day, for example someone may not work on a monday, therefore the addition of case:1 would include a monday.
我们的想法是从工作日开始,然后对落在工作日范围内的每一天倒数到零。使用 switch 可以让一个人将一周中的某一天声明为非工作日,例如某人可能在星期一不工作,因此添加 case:1 将包括星期一。
This is a simple script and does not take in to account public or bank holidays, that would be asking for a much more complex script to work with.
这是一个简单的脚本,不考虑公共假期或银行假期,这将要求使用更复杂的脚本。
The result is a date that is set to the date of shipping, the user can then extract the date info in any format that they please, eg.
结果是设置为发货日期的日期,然后用户可以以他们喜欢的任何格式提取日期信息,例如。
var shipDate = tmp.toUTCString().slice(1,15);
回答by Mike Godin
I've adapted Mark Giblin's revised code to better deal with end of year dates and also U.S. federal holidays. See below...
我已经修改了 Mark Giblin 的修订代码,以更好地处理年终日期和美国联邦假期。见下文...
function businessDaysFromDate(date,businessDays) {
var counter = 0, tmp = new Date(date);
while( businessDays>=0 ) {
tmp.setTime( date.getTime() + counter * 86400000 );
if(isBusinessDay (tmp)) {
--businessDays;
}
++counter;
}
return tmp;
}
function isBusinessDay (date) {
var dayOfWeek = date.getDay();
if(dayOfWeek === 0 || dayOfWeek === 6) {
// Weekend
return false;
}
holidays = [
'12/31+5', // New Year's Day on a saturday celebrated on previous friday
'1/1', // New Year's Day
'1/2+1', // New Year's Day on a sunday celebrated on next monday
'1-3/1', // Birthday of Martin Luther King, third Monday in January
'2-3/1', // Washington's Birthday, third Monday in February
'5~1/1', // Memorial Day, last Monday in May
'7/3+5', // Independence Day
'7/4', // Independence Day
'7/5+1', // Independence Day
'9-1/1', // Labor Day, first Monday in September
'10-2/1', // Columbus Day, second Monday in October
'11/10+5', // Veterans Day
'11/11', // Veterans Day
'11/12+1', // Veterans Day
'11-4/4', // Thanksgiving Day, fourth Thursday in November
'12/24+5', // Christmas Day
'12/25', // Christmas Day
'12/26+1', // Christmas Day
];
var dayOfMonth = date.getDate(),
month = date.getMonth() + 1,
monthDay = month + '/' + dayOfMonth;
if(holidays.indexOf(monthDay)>-1){
return false;
}
var monthDayDay = monthDay + '+' + dayOfWeek;
if(holidays.indexOf(monthDayDay)>-1){
return false;
}
var weekOfMonth = Math.floor((dayOfMonth - 1) / 7) + 1,
monthWeekDay = month + '-' + weekOfMonth + '/' + dayOfWeek;
if(holidays.indexOf(monthWeekDay)>-1){
return false;
}
var lastDayOfMonth = new Date(date);
lastDayOfMonth.setMonth(lastDayOfMonth.getMonth() + 1);
lastDayOfMonth.setDate(0);
var negWeekOfMonth = Math.floor((lastDayOfMonth.getDate() - dayOfMonth - 1) / 7) + 1,
monthNegWeekDay = month + '~' + negWeekOfMonth + '/' + dayOfWeek;
if(holidays.indexOf(monthNegWeekDay)>-1){
return false;
}
return true;
}
回答by stv
We have UI that defaults search inputs to last business day or a week-ago. Here's something that works both forward and backward.
我们的用户界面将搜索输入默认为上一个工作日或一周前。这是向前和向后都有效的东西。
// add (or subtract) business days to provided date
addBusinessDays = function (startingDate, daysToAdjust) {
var newDate = new Date(startingDate.valueOf()),
businessDaysLeft,
isWeekend,
direction;
// Timezones are scary, let's work with whole-days only
if (daysToAdjust !== parseInt(daysToAdjust, 10)) {
throw new TypeError('addBusinessDays can only adjust by whole days');
}
// short-circuit no work; make direction assignment simpler
if (daysToAdjust === 0) {
return startingDate;
}
direction = daysToAdjust > 0 ? 1 : -1;
// Move the date in the correct direction
// but only count business days toward movement
businessDaysLeft = Math.abs(daysToAdjust);
while (businessDaysLeft) {
newDate.setDate(newDate.getDate() + direction);
isWeekend = newDate.getDay() in {0: 'Sunday', 6: 'Saturday'};
if (!isWeekend) {
businessDaysLeft--;
}
}
return newDate;
};
It would be easy to pass in an optional holidays data structure and adjust for that as well.
传入可选的假期数据结构并为此进行调整会很容易。
However, generating a holidays data structure, well, that will take a little more effort and is specific not only to every country and region, but also to every organization.
但是,生成假期数据结构需要更多的努力,并且不仅针对每个国家和地区,而且针对每个组织。
回答by Rhyono
Your main problem was that adding safety each time meant you were adding multiple days each time it looped, instead of 1. So first loop = 1, second = 1+2, etc.
您的主要问题是,每次增加安全性意味着每次循环时都会增加多天,而不是 1。所以第一个循环 = 1,第二个 = 1+2,依此类推。
I believe this works as you'd like:
我相信这可以如您所愿:
var businessDays = 10; // this will come from a form
var counter = 0; // I have a counter
var safety = 0; // I have a safety variable
var ship = today = new Date(); // I have the current date and an initialized shipping variable but the buy date will come from a form
console.log(">>> today = " + today);
// now the loop...
while( ++safety <30 ){
ship.setDate(ship.getDate()+1 );
switch( ship.getDay() ){
case 0: // Sunday
case 6: // Saturday
break;
default:
counter++;
}
if( counter >= businessDays ) break;
}
// add a number of days
// the expected shipping date
console.log(">>> days = " + businessDays);
console.log(">>> ship = " + ship);
回答by s1c0j1
I needed something similar but a little different and this is what I came up with.
我需要一些类似但有点不同的东西,这就是我想出的。
Holidays are added in an object with one key for each month that has holidays. That key then has an array of days in that month that are considered holidays.
假期被添加到一个对象中,每个有假期的月份都有一个键。然后,该键在该月中具有一系列被视为假期的天数。
function getDueDate(date) {
var numBusinessDays = 20;
var saturday = 6;
var sunday = 0;
var holidays = {
/* Months are zero-based. 0 = Jan, 11 = Dec */
0: [1, 2],
1: [6],
3: [24],
11: [25, 26]
};
var dayOfWeek = null;
var dayOfMonth = null;
var month = null;
var isWeekday = null;
var monthHolidays = null;
var isHoliday = null;
while (numBusinessDays > 0) {
dayOfWeek = date.getDay();
dayOfMonth = date.getDate();
month = date.getMonth();
isWeekday = dayOfWeek !== saturday && dayOfWeek !== sunday;
monthHolidays = holidays[month];
isHoliday = monthHolidays
? monthHolidays.indexOf(dayOfMonth) > -1
: false;
if (isWeekday && !isHoliday) --numBusinessDays;
date.setDate(dayOfMonth + 1);
}
return date;
}
回答by anthonygore
ship.setDate( safety ); // add a number of days
Doesn't add days. It sets the day. More info
不加天数。它确定了这一天。 更多信息
The
setDate()
method sets the day of theDate
object relative to the beginning of the currently set month.
该
setDate()
方法Date
相对于当前设置的月份的开始设置对象的日期。
If you want to add days do something like this:
如果要添加天数,请执行以下操作:
ship.setDate(ship.getDate()+1);
回答by rvighne
Change line 7 from
将第 7 行从
ship.setDate( safety ); // add a number of days
to
到
ship.setDate( ship.getDate() + safety );
The problem was that you want to adddays, not set days.
问题是您想添加天数,而不是设置天数。
回答by RobrechtVM
I just found this script which is working nice, you can give an optional array for your country's holidays
我刚刚发现这个脚本运行良好,您可以为您所在国家/地区的假期提供一个可选数组
function addBusinessDays(date, days, holidays) {
var calendar = java.util.Calendar.getInstance();
calendar.setTimeInMillis(date.getTime());
var numberOfDaysToAdd = Math.abs(days);
var daysToAdd = days < 0 ? -1 : 1;
var businessDaysAdded = 0;
function isHoliday(dateToCheck) {
if (holidays && holidays.length > 0) {
for (var i = 0; i < holidays.length; i++) {
if (holidays[i].getFullYear() == dateToCheck.get(java.util.Calendar.YEAR) && holidays[i].getMonth() == dateToCheck.get(java.util.Calendar.MONTH) && holidays[i].getDate() == dateToCheck.get(java.util.Calendar.DAY_OF_MONTH)) {
return true;
}
}
}
return false;
}
while (businessDaysAdded < numberOfDaysToAdd) {
calendar.add(java.util.Calendar.DATE, daysToAdd);
if (calendar.get(java.util.Calendar.DAY_OF_WEEK) == java.util.Calendar.SATURDAY || calendar.get(java.util.Calendar.DAY_OF_WEEK) == java.util.Calendar.SUNDAY) {
// add another day
continue;
}
if (isHoliday(calendar)) {
// add another day
continue;
}
businessDaysAdded ++;
}
return new Date(calendar.getTimeInMillis());
}