добавить / вычесть рабочие дни в Javascript - PullRequest
6 голосов
/ 28 июня 2011

Мне нужна функция Date.prototype.addBusDays это число будет целым числом, которое будет добавлено к дате.

Однако есть два соображения: 1. Выходные, 2. Выходные (я думаю, что это будет предустановленный массив для сравнения. Если начальная и конечная даты содержат 3 выходных, вы выталкиваете конечную дату на 3).

Я сталкивался с некоторыми сценариями в Интернете, и я могу подумать об одной дилемме: скажем, сначала вы обращаетесь ко всем выходным, затем проводите выходные, что если у вас +1 день (из-за выходных), и ваша дата окончания снова на выходных ... <

Есть идеи? Спасибо!

EDIT:

Это часть инструмента планирования, который я разрабатываю, что означает, что даты будут привязаны к задачам, которые связаны друг с другом. Добавление 1 дня к задаче приведет к пересчету всего, что с ней связано, возможно, всех дат в базе данных.

Ответы [ 6 ]

5 голосов
/ 04 июня 2014

Решение 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;
}
4 голосов
/ 23 апреля 2014

Посмотрите на следующую реализацию.Источник: 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);
1 голос
/ 15 февраля 2019

(обновлено) Я проверил этот алгоритм по его шагам, и он кажется стабильным, хотя он использует рекурсию для обработки выходных:

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;
}
1 голос
/ 28 июня 2011

Я бы сделал цикл. Продолжайте добавлять дни, пока не найдете подходящий день.

0 голосов
/ 04 апреля 2017

Простое решение для решения всей проблемы;Вы можете просто переходить по дням, чтобы пропустить будни и праздники:

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
0 голосов
/ 27 октября 2015

Я расширил ответ khellendros74 для моего проекта, в котором нужно было отключить воскресенья и почтовые праздники в средстве выбора даты и вернуть две даты одним нажатием кнопки: три рабочих дня (т.е. выходной и нерабочий день) после даты выбранный в указателе даты (поле с идентификатором «календарь») и через шесть рабочих дней после даты, выбранной в указателе даты, а затем поместите эти два результата в несколько отключенных полей ввода (handDelivered и mailed). Нажатие кнопки вызывает функцию 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;
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...