【问题标题】:add/subtract business days in Javascript在 Javascript 中添加/减去工作日
【发布时间】:2011-06-27 23:03:03
【问题描述】:

我需要一个 Date.prototype.addBusDays 函数 这将采用整数作为要添加到日期的工作日数。

但是,有两个考虑因素:1. 周末,2. 假期(我想这将是一个预设数组进行比较。如果开始日期和结束日期包含 3 个假期,那么您将结束日期推迟 3 个)

我在网上遇到了一些脚本,我能想到的一个困境是,假设你先解决所有周末,然后你做假期,如果你 +1 天(由于假期)和你的结束日期又被推到周末了……

有什么想法吗? 谢谢!

编辑:

这是我正在开发的日程安排工具的一部分,这意味着日期将与链接在一起的任务相关联。为任务添加 1 天,将触发重新计算与其相关的所有内容,可能是数据库中的所有日期。

【问题讨论】:

  • 哪种方法最有效取决于可能添加的天数。如果数量很少,一个简单的循环就足够了。
  • @William Sham:如果您正在使用日程安排工具,最好以这样一种方式存储相对日期,以便很少(如果有的话)引起如此大规模的更新。
  • @Orbling:这不可能仅仅因为它是调度。当您将任务的持续时间延长 1 周并将所有内容推出时,您希望看到对项目最终日期的影响。
  • @William Sham:是的,我对调度软件非常熟悉。通常,关键路径中的大多数事件都存储为父节点的持续时间和边集,因此一个事件的更改通常不会导致表的其余部分发生更改,只会导致关键路径的不同呈现。
  • @Orbling:这是一种非常有趣的看待方式。有没有我可以阅读的文章?

标签: javascript date


【解决方案1】:

Datageek 的解决方案帮助了我,但我需要对其进行扩充。这仍然不包括假期,但确实可以选择包括周六和/或周日的工作日,并且支持添加负数:-

function AddWorkingDays(datStartDate, lngNumberOfWorkingDays, blnIncSat, blnIncSun) {
    var intWorkingDays = 5;
    var intNonWorkingDays = 2;
    var intStartDay = datStartDate.getDay(); // 0=Sunday ... 6=Saturday
    var intOffset;
    var intModifier = 0;

    if (blnIncSat) { intWorkingDays++; intNonWorkingDays--; }
    if (blnIncSun) { intWorkingDays++; intNonWorkingDays--; }
    var newDate = new Date(datStartDate)
    if (lngNumberOfWorkingDays >= 0) {
        // Moving Forward
        if (!blnIncSat && blnIncSun) {
            intOffset = intStartDay;
        } else {
            intOffset = intStartDay - 1;
        }
        // Special start Saturday rule for 5 day week
        if (intStartDay == 6 && !blnIncSat && !blnIncSun) {
            intOffset -= 6;
            intModifier = 1;
        }
    } else {
        // Moving Backward
        if (blnIncSat && !blnIncSun) {
            intOffset = intStartDay - 6;
        } else {
            intOffset = intStartDay - 5;
        }
        // Special start Sunday rule for 5 day week
        if (intStartDay == 0 && !blnIncSat && !blnIncSun) {
            intOffset++;
            intModifier = 1;
        }
    }
    // ~~ is used to achieve integer division for both positive and negative numbers
    newDate.setTime(datStartDate.getTime() + (new Number((~~((lngNumberOfWorkingDays + intOffset) / intWorkingDays) * intNonWorkingDays) + lngNumberOfWorkingDays + intModifier)*86400000));
    return newDate;
}

【讨论】:

    【解决方案2】:

    看看下面的实现。来自about.com

    addWeekdays = function(date, dd) {
      var wks = Math.floor(dd/5);
      var dys = dd.mod(5);
      var dy = this.getDay();
      if (dy === 6 && dys > -1) {
         if (dys === 0) {dys-=2; dy+=2;}
         dys++; dy -= 6;
      }
      if (dy === 0 && dys < 1) {
        if (dys === 0) {dys+=2; dy-=2;}
        dys--; dy += 6;
      }
      if (dy + dys > 5) dys += 2;
      if (dy + dys < 1) dys -= 2;
      date.setDate(date.getDate()+wks*7+dys);
    }
    
    var date = new Date();
    addWeekdays(date, 9);
    

    【讨论】:

    • 开箱即用并不适合我。需要进行一些修改才能接受负数。
    【解决方案3】:

    (已更新)我已经对这个算法进行了测试,它看起来很稳定,尽管它确实使用递归来处理假期:

    holidays = [new Date("2/13/2019"), new Date("2/19/2019")];
    
    function addWorkdays(workdays, startDate) {
      //Make adjustments if the start date is on a weekend
      let dayOfWeek = startDate.getDay();
      let adjustedWorkdays = Math.abs(workdays);
      if (0 == dayOfWeek || 6 == dayOfWeek) {
        adjustedWorkdays += (Math.abs((dayOfWeek % 5) + Math.sign(workdays)) % 2) + 1;
        dayOfWeek = (dayOfWeek - 6) * -1;
      }
      let endDate = new Date(startDate);
      endDate.setDate(endDate.getDate() + (((Math.floor(((workdays >= 0 ? dayOfWeek - 1 : 6 - dayOfWeek) + adjustedWorkdays) / 5) * 2) + adjustedWorkdays) * (workdays < 0 ? -1 : 1)));
      //If we cross holidays, recompute our end date accordingly
      let numHolidays = holidays.reduce(function(total, holiday) { return (holiday >= Math.min(startDate, endDate) && holiday <= Math.max(startDate, endDate)) ? total + 1 : total; }, 0);
      if (numHolidays > 0) {
        endDate.setDate(endDate.getDate() + Math.sign(workdays));
        return addWorkdays((numHolidays - 1) * Math.sign(workdays), endDate);
      } else return endDate;
    }

    【讨论】:

    • 如何考虑假期(问题中提出的两个基本要求之一)?
    【解决方案4】:

    我扩展了 khellendros74 对我的一个项目的回答,该项目需要在日期选择器中禁用星期日和邮寄假期,并在按下按钮时返回两个日期:日期之后的三个工作日(即非节假日和非星期日)在日期选择器(ID 为“日历”的字段)中选择,并在日期选择器中选择的日期后六个工作日内选择,然后将这两个结果放入几个禁用的输入字段(手动交付和邮寄)。按下按钮调用函数calculateDates。这是代码:

    var disabledDates = ['11/11/2015', '11/26/2015', '12/25/2015', '01/01/2016','01/18/2016', '02/15/2016','05/30/2016', '07/04/2016','09/05/2016','10/10/2016','11/11/2016','11/24/2016', '12/26/2016','01/02/2017','01/16/2017', '02/20/2017','05/29/2017', '07/04/2017','09/04/2017','10/09/2017','11/10/2017','11/23/2017', '12/25/2017','01/01/2018','01/15/2018', '02/19/2018','05/28/2018', '07/04/2018','09/03/2018','10/08/2018','11/12/2018','11/22/2018', '12/25/2018','01/01/2019','01/21/2019', '02/18/2019','05/27/2019', '07/04/2019','09/02/2019','10/14/2019','11/11/2019','11/28/2019', '12/25/2019','01/01/2020','01/20/2020', '02/17/2020','05/25/2020', '07/03/2020','09/07/2020','10/11/2020','11/26/2020','11/26/2020', '12/25/2020'];
    
    $(function(){
    
        $('#calendar').datepicker({
            dateFormat: 'mm/dd/yy',
            beforeShowDay: editDays
        });
    
        function editDays(date) {
            for (var i = 0; i < disabledDates.length; i++) {
                if (new Date(disabledDates[i]).toString() == date.toString() || date.getDay() == 0) {             
                     return [false];
                }
            }
            return [true];
         }   
    
    });
    
    function calculateDates()
    {
        if( !$('#calendar').val()){
            alert("Please enter a date.");
            document.getElementById('calendar').focus();
            return false;
        }
    
        var dayThreeAdd = 0;
        var daySixAdd = 0;
    
        for (var i = 0; i < disabledDates.length; i++) {
            var oneDays = AddWorkingDays($('#calendar').val(),1,true,false);
            var twoDays = AddWorkingDays($('#calendar').val(),2,true,false);
            var threeDays = AddWorkingDays($('#calendar').val(),3,true,false);
            var fourDays = AddWorkingDays($('#calendar').val(),4,true,false);
            var fiveDays = AddWorkingDays($('#calendar').val(),5,true,false);
            var sixDays = AddWorkingDays($('#calendar').val(),6,true,false);
    
            if (new Date(disabledDates[i]).toString() == oneDays.toString()) {
                 dayThreeAdd++;
                 daySixAdd++;
            }
            if (new Date(disabledDates[i]).toString() == twoDays.toString()) {             
                 dayThreeAdd++;
                 daySixAdd++;
            }
            if (new Date(disabledDates[i]).toString() == threeDays.toString()) {             
                 dayThreeAdd++;
                 daySixAdd++;
            }
            if (new Date(disabledDates[i]).toString() == fourDays.toString()) {
                daySixAdd++;
            }
            if (new Date(disabledDates[i]).toString() == fiveDays.toString()) {
                daySixAdd++;
            }
            if (new Date(disabledDates[i]).toString() == sixDays.toString()) {
                daySixAdd++;
            }
    
        }
    
        var threeDays = AddWorkingDays($('#calendar').val(),(3 + dayThreeAdd),true,false);
        var sixDays = AddWorkingDays($('#calendar').val(),(6 + daySixAdd),true,false);
    
        $('#handDelivered').val((threeDays.getMonth()+1) + '/' + threeDays.getDate() + '/' + (threeDays.getYear()+1900));
        $('#mailed').val((sixDays.getMonth()+1) + '/' + sixDays.getDate() + '/' + (sixDays.getYear()+1900));
    
    
    
    }
    
    function AddWorkingDays(datStartDate, lngNumberOfWorkingDays, blnIncSat, blnIncSun) {
        datStartDate = new Date(datStartDate);
        var intWorkingDays = 5;
        var intNonWorkingDays = 2;
        var intStartDay = datStartDate.getDay(); // 0=Sunday ... 6=Saturday
        var intOffset;
        var intModifier = 0;
    
        if (blnIncSat) { intWorkingDays++; intNonWorkingDays--; }
        if (blnIncSun) { intWorkingDays++; intNonWorkingDays--; }
        var newDate = new Date(datStartDate)
        if (lngNumberOfWorkingDays >= 0) {
            // Moving Forward
            if (!blnIncSat && blnIncSun) {
                intOffset = intStartDay;
            } else {
                intOffset = intStartDay - 1;
            }
            // Special start Saturday rule for 5 day week
            if (intStartDay == 6 && !blnIncSat && !blnIncSun) {
                intOffset -= 6;
                intModifier = 1;
            }
        } else {
            // Moving Backward
            if (blnIncSat && !blnIncSun) {
                intOffset = intStartDay - 6;
            } else {
                intOffset = intStartDay - 5;
            }
            // Special start Sunday rule for 5 day week
            if (intStartDay == 0 && !blnIncSat && !blnIncSun) {
                intOffset++;
                intModifier = 1;
            }
        }
        // ~~ is used to achieve integer division for both positive and negative numbers
        newDate.setTime(datStartDate.getTime() + (new Number((~~((lngNumberOfWorkingDays + intOffset) / intWorkingDays) * intNonWorkingDays) + lngNumberOfWorkingDays + intModifier)*86400000));
        return newDate;
    }
    

    【讨论】:

      【解决方案5】:

      解决整个问题的简单解决方案;您可以循环跳过工作日和节假日:

      Date.prototype.holidays = {
        // fill in common holidays
        all: [
          '0101', // Jan 01
          '1225' // Dec 25
        ],
        2016: [
          // add year specific holidays
          '0104' // Jan 04 2016
        ],
        2017: [
          // And so on for other years.
        ]
      };
      
      Date.prototype.addWorkingDays = function(days) {
        while (days > 0) {
          this.setDate(this.getDate() + 1);
          if (!this.isHoliday()) days--;
        }
      
        return this;
      };
      
      Date.prototype.substractWorkingDays = function(days) {
        while (days > 0) {
          this.setDate(this.getDate() - 1);
          if (!this.isHoliday()) days--;
        }
      
        return this;
      };
      
      Date.prototype.isHoliday = function() {
        function zeroPad(n) {
          n |= 0;
          return (n < 10 ? '0' : '') + n;
        }
      
        // if weekend return true from here it self;
        if (this.getDay() == 0 || this.getDay() == 6) {
          return true;
        }
      
        var day = zeroPad(this.getMonth() + 1) + zeroPad(this.getDate());
      
        // if date is present in the holiday list return true;
        return !!~this.holidays.all.indexOf(day) ||      
          (this.holidays[this.getFullYear()] ?
      !!~this.holidays[this.getFullYear()].indexOf(day) : false);
      };
      
      // Uasage
      var date = new Date('2015-12-31');
      
      date.addWorkingDays(10);
      alert(date.toDateString()); // Mon Jan 18 2016
      
      date.substractWorkingDays(10);
      alert(date.toDateString()) // Thu Dec 31 2015

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-30
        • 2022-01-23
        • 1970-01-01
        • 2018-10-24
        • 1970-01-01
        • 1970-01-01
        • 2021-06-11
        • 1970-01-01
        相关资源
        最近更新 更多