Найдите три предыдущих рабочих дня с данной даты

Я должен найти три предыдущих рабочих дня с данной даты, опуская выходные и праздничные дни. Это не трудная задача сам по себе, но кажется, что способ, которым я собирался сделать это, будет чрезмерно сложным, таким образом, я думал, что попрошу Ваше мнение сначала.

Для создания вещей более интересными давайте сделаем это конкурсом. Я предлагаю 300 как щедрость тому, кто бы ни предлагает самое короткое, самое чистое решение, которое придерживается этой спецификации:

  • Запишите функцию, которая возвращает три предыдущих рабочих дня из данной даты
  • Рабочий день определяется как любой день, который не является в субботу или в воскресенье и не является праздником
  • Функция знает праздники в течение года данной даты и может принять их во внимание
  • Функция принимает один параметр, дату, в Y-m-d формат
  • Функция возвращает массив с тремя датами в Y-m-d формат, отсортированный от самых старых до новейших.

Дополнительный:

  • Функция может найти также следующие три рабочих дня в дополнение к предыдущим трем

Пример праздничного массива:

$holidays = array(
    '2010-01-01',
    '2010-01-06',
    '2010-04-02',
    '2010-04-04',
    '2010-04-05',
    '2010-05-01',
    '2010-05-13',
    '2010-05-23',
    '2010-06-26',
    '2010-11-06',
    '2010-12-06',
    '2010-12-25',
    '2010-12-26'
);

Обратите внимание, что в реальном сценарии, праздники не являются hardcoded, но прибывают из get_holidays($year) функция. Можно включать / использование, что в ответе, если Вы желаете.

Поскольку я предлагаю щедрость, которая означает, что будет по крайней мере за три дня до того, как я смогу отметить ответ, как принято (2 дня для добавления щедрости, 1 день, пока я не могу принять).


Примечание:

При использовании фиксированного формата дня, такого как 86 400 секунд для спрыгивания со дня другому, Вы столкнетесь с проблемами с летним временем. Использовать strtotime('-1 day', $timestamp) вместо этого.

Пример этой проблемы:

http://codepad.org/uSYiIu5w


Конечное решение

Вот конечное решение, которое я закончил тем, что использовал, адаптированный от идеи Keith Minkler использовать strtotime last weekday. Обнаруживает направление от переданного количества, если отрицательный, поиски назад, и вперед на положительном:

function working_days($date, $count) {

    $working_days = array();
    $direction    = $count < 0 ? 'last' : 'next';
    $holidays     = get_holidays(date("Y", strtotime($date)));

    while(count($working_days) < abs($count)) {
        $date = date("Y-m-d", strtotime("$direction weekday", strtotime($date)));
        if(!in_array($date, $holidays)) {
            $working_days[] = $date;
        }
    }

    sort($working_days);
    return $working_days;
}

6
задан Tatu Ulmanen 26 June 2010 в 18:45
поделиться

12 ответов

В strtotime можно использовать такие выражения, как «последний будний день» или «следующий четверг», например:

function last_working_days($date, $backwards = true)
{
    $holidays = get_holidays(date("Y", strtotime($date)));

    $working_days = array();

    do
    {
        $direction = $backwards ? 'last' : 'next';
        $date = date("Y-m-d", strtotime("$direction weekday", strtotime($date)));
        if (!in_array($date, $holidays))
        {
            $working_days[] = $date;
        }
    }
    while (count($working_days) < 3);

    return $working_days;
}
8
ответ дан 8 December 2019 в 03:52
поделиться

Передайте true в качестве второго аргумента, чтобы двигаться вперед во времени, а не назад. Я также отредактировал функцию, чтобы у вас было более трех дней, если вы захотите в будущем.

function last_workingdays($date, $forward = false, $numberofdays = 3) {
        $time = strtotime($date);
        $holidays = get_holidays();
        $found = array();
        while(count($found) < $numberofdays) {
                $time -= 86400 * ($forward?-1:1);
                $new = date('Y-m-d', $time);
                $weekday = date('w', $time);
                if($weekday == 0 || $weekday == 6 || in_array($new, $holidays)) {
                        continue;
                }
                $found[] = $new;
        }
        if(!$forward) {
                $found = array_reverse($found);
        }
        return $found;
}
3
ответ дан 8 December 2019 в 03:52
поделиться

Вы имеете в виду функцию РАБДЕНЬ () в Excel

Если вы посмотрите на функцию РАБДНИ в PHPExcel , вы найдете пример того, как кодировать такую ​​функцию

0
ответ дан 8 December 2019 в 03:52
поделиться

Редактировать:

Изменил 86400 на -1 день , хотя я не совсем понимаю, действительно ли это было проблемой.

Внесены некоторые изменения в исходные функции, но в целом они остались прежними.

// -----------------------
// Previous 3 working days # this is almost the same that someone already posted
function getWorkingDays($date){
    $workdays = array();
    $holidays = getHolidays();
    $date     = strtotime($date);

    while(count($workdays) < 3){
        $date = strtotime("-1 day", $date);

        if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
            $workdays[] = date('Y-m-d',$date);
    }

    krsort($workdays);
    return $workdays;
}
// --------------------------------
// Previous and Next 3 working days
function getWorkingDays2($date){
    $workdays['prev'] = $workdays['next'] = array();
    $holidays = getHolidays();
    $date     = strtotime($date);

    $start_date = $date;
    while(count($workdays['prev']) < 3){
        $date = strtotime("-1 day", $date);

        if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
            $workdays['prev'][] = date('Y-m-d',$date);
    }
    $date = $start_date;
    while(count($workdays['next']) < 3){
        $date = strtotime("+1 day", $date);

        if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
            $workdays['next'][] = date('Y-m-d',$date);
    }

    krsort($workdays['prev']);
    return $workdays;
}

function getHolidays(){
    $holidays = array(
        '2010-01-01', '2010-01-06',
        '2010-04-02', '2010-04-04', '2010-04-05',
        '2010-05-01', '2010-05-13', '2010-05-23',
        '2010-06-26',
        '2010-11-06',
        '2010-12-06', '2010-12-25', '2010-12-26'
    );
    return $holidays;
}

echo '<pre>';
print_r( getWorkingDays( '2010-04-04' ) );
print_r( getWorkingDays2( '2010-04-04' ) );
echo '</pre>';

Выходы:

Array
(
    [2] => 2010-03-30
    [1] => 2010-03-31
    [0] => 2010-04-01
)
Array
(
    [next] => Array
        (
            [0] => 2010-04-06
            [1] => 2010-04-07
            [2] => 2010-04-08
        )

    [prev] => Array
        (
            [2] => 2010-03-30
            [1] => 2010-03-31
            [0] => 2010-04-01
        )

)
1
ответ дан 8 December 2019 в 03:52
поделиться

Вот моя попытка:

function business_days($date) {
    $out = array();
    $day = 60*60*24;

    //three back
    $count = 0;
    $prev = strtotime($date);
    while ($count < 3) {
        $prev -= $day;
        $info = getdate($prev);
        $holidays = get_holidays($info['year']);
        if ($info['wday'] == 0 || $info['wday'] == 6 || in_array($date,$holidays))
                continue;
        else {
            $out[] = date('Y-m-d',$prev);
            $count++;
        }
    }

    $count = 0;
    $next = strtotime($date);
    while ($count < 3) {
        $next += $day;
        $info = getdate($next);
        $holidays = get_holidays($info['year']);
        if ($info['wday']==0 || $info['wday']==6 || in_array($date,$holidays))
                continue;
        else {
            $out[] = date('Y-m-d',$next);
            $count++;
        }
    }

    sort($out);

    return $out;
}
1
ответ дан 8 December 2019 в 03:52
поделиться

Попробуйте это (справедливое предупреждение - у меня нет доступа для проверки, поэтому исправьте все синтаксические ошибки).

function LastThreeWorkdays($start_date) { 
    $startdateseed = strtotime($start_date); 
    $workdays = array(); 
    $holidays = get_holidays('2010'); 

    for ($counter = -1; $counter >= -10; $counter--) 
      if (date('N', $current_date = strtotime($counter.' day', $startdateseed)) < 6) $workdays[] = date('Y-m-d', $currentdate);

    return array_slice(array_reverse(array_diff($workdays, $holidays)), 0, 3);
}

Обычно создайте «кусок» дат, а затем используйте массив diff для удаления из него праздников. Верните только три верхних (последних) предмета. Очевидно, что для вычислений требуется немного больше места для хранения и времени, чем в предыдущих ответах, но код намного короче.

Размер «фрагмента» может быть изменен для дальнейшей оптимизации. В идеале это должно быть максимальное количество выходных дней подряд плюс 2 плюс 3, но это предполагает реалистичные сценарии праздников (целая неделя праздников невозможна и т. Д.).

Код также можно «развернуть», чтобы облегчить чтение некоторых уловок. В целом демонстрирует некоторые функции PHP немного лучше - хотя их можно комбинировать с другими идеями.

0
ответ дан 8 December 2019 в 03:52
поделиться

Я добавляю еще один ответ, поскольку он следует подходу, отличному от тех, которые я опубликовал ранее:

function getWorkDays($date){
    list($year,$month,$day) = explode('-',$date);
    $holidays = getHolidays();
    $dates    = array();

    while(count($dates) < 3){
        $newDate = date('Y-m-d',mktime(0,0,0,$month,--$day,$year));
        if(date('N',strtotime($newDate)) < 6 && !in_array($newDate,$holidays))
            $dates[] = $newDate;
    }

    return array_reverse($dates);
}

print_r(getWorkDays('2010-12-08'));

Вывод:

Array
(
    [0] => 2010-12-02
    [1] => 2010-12-03
    [2] => 2010-12-07
)
1
ответ дан 8 December 2019 в 03:52
поделиться

Это должно помочь:

    // Start Date must be in "Y-m-d" Format
    function LastThreeWorkdays($start_date) {
        $current_date = strtotime($start_date);
        $workdays = array();
        $holidays = get_holidays('2010');

        while (count($workdays) < 3) {
            $current_date = strtotime('-1 day', $current_date);

            if (in_array(date('Y-m-d', $current_date), $holidays)) {    
                // Public Holiday, Ignore.
                continue;
            }

            if (date('N', $current_date) < 6) {
                // Weekday. Add to Array.
                $workdays[] = date('Y-m-d', $current_date);
            }
        }

        return array_reverse($workdays);
    }

Я жестко вписал функцию get_holidays(), но я уверен, что вы поймете идею и подстроите ее под себя. Остальное - рабочий код.

10
ответ дан 8 December 2019 в 03:52
поделиться
/**
  * @param $currentdate like 'YYYY-MM-DD'
  * @param $n number of workdays to return
  * @param $direction 'previous' or 'next', default is 'next'
  **/
function adjacentWorkingDays($currentdate, $n, $direction='next') {
    $sign = ($direction == 'previous') ? '-' : '+';
    $workdays = array();
    $holidays = get_holidays();
    $i = 1;
    while (count($workdays) < $n) {
        $dateinteger = strtotime("{$currentdate} {$sign}{$i} days");
        $date = date('Y-m-d', $dateinteger);
        if (!in_array($date, $holidays) && date('N', $dateinteger) < 6) {
            $workdays[] = $date;
        }
        $i++;
    }
    return $workdays;
}

// you pass a year into get_holidays, make sure folks
// are accounting for the fact that adjacent holidays
// might cross a year boundary
function get_holidays() {
    $holidays = array(
        '2010-01-01',
        '2010-01-06',
        '2010-04-02',
        '2010-04-04',
        '2010-04-05',
        '2010-05-01',
        '2010-05-13',
        '2010-05-23',
        '2010-06-26',
        '2010-11-06',
        '2010-12-06',
        '2010-12-25',
        '2010-12-26'
    );
    return $holidays;
}

В этих функциях мы используем parallelWorkingDays () функция:

// next $n working days, in ascending order
function nextWorkingDays($date, $n) {
    return adjacentWorkingDays($date, $n, 'next');
}

// previous $n workind days, in ascending order
function previousWorkingDays($date, $n) {
    return array_reverse(adjacentWorkingDays($date, $n, 'previous'));
}

Вот проверка:

print "<pre>";
print_r(nextWorkingDays('2010-06-24', 3));
print_r(previousWorkingDays('2010-06-24', 3));
print "<pre>";

Результаты:

Array
(
    [0] => 2010-06-25
    [1] => 2010-06-28
    [2] => 2010-06-29
)
Array
(
    [0] => 2010-06-21
    [1] => 2010-06-22
    [2] => 2010-06-23
)
0
ответ дан 8 December 2019 в 03:52
поделиться

Вот мой вариант, использующий класс DateTime в PHP. Что касается праздников, то он учитывает, что вы можете начать в одном году, а закончить в другом.

function get_workdays($date, $num = 3, $next = false)
{
    $date = DateTime::createFromFormat('Y-m-d', $date);
    $interval = new DateInterval('P1D');
    $holidays = array();

    $res = array();
    while (count($res) < $num) {
        $date->{$next ? 'add' : 'sub'}($interval);

        $year = (int) $date->format('Y');
        $formatted = $date->format('Y-m-d');

        if (!isset($holidays[$year]))
            $holidays[$year] = get_holidays($year);

        if ($date->format('N') <= 5 && !in_array($formatted, $holidays[$year]))
            $res[] = $formatted;
    }
    return $next ? $res : array_reverse($res);
}
3
ответ дан 8 December 2019 в 03:52
поделиться

вот мое сообщение;)

/**
 * Helper function to handle year overflow
 */
function isHoliday($date) {
  static $holidays = array(); // static cache
  $year = date('Y', $date);

  if(!isset($holidays["$year"])) {
    $holidays["$year"] = get_holidays($year);
  }

  return in_array(date('Y-m-d', $date), $holidays["$year"]);
}

/**
 * Returns adjacent working days (by default: the previous three)
 */
function adjacentWorkingDays($start_date, $limit = 3, $direction = 'previous') {
  $current_date = strtotime($start_date);
  $direction = ($direction === 'next') ? 'next' : 'previous'; // sanity
  $workdays = array();

  // no need to verify the count before checking the first day.
  do {
    // using weekday here skips weekends.
    $current_date = strtotime("$direction weekday", $current_date);
    if (!isHoliday()) {
      // not a public holiday.
      $workdays[] = date('Y-m-d', $current_date);
    }
  } while (count($workdays) < $limit)

  return array_reverse($workdays);
}
0
ответ дан 8 December 2019 в 03:52
поделиться

Вот мое мнение. Эта функция (в отличие от большинства других опубликованных) не даст сбой, если вы введете дату в начале года. Если бы вы вызывали функцию get_holidays только для одного года, результирующий массив мог бы включать даты, которые являются выходными днями предыдущего года. Мое решение снова вызовет get_holidays , если мы вернемся в предыдущий год.

function get_working_days($date)
{
    $date_timestamp = strtotime($date);
    $year = date('Y', $date_timestamp);
    $holidays = get_holidays($year);
    $days = array();

    while (count($days) < 3)
    {
        $date_timestamp = strtotime('-1 day', $date_timestamp);
        $date = date('Y-m-d', $date_timestamp);         

        if (!in_array($date, $holidays) && date('N', $date_timestamp) < 6)
            $days[] = $date;


        $year2 = date('Y', $date_timestamp);
        if ($year2 != $year)
        {
            $holidays = array_merge($holidays, get_holidays($year2));
            $year = $year2;
        }
    }

    return $days;
}
0
ответ дан 8 December 2019 в 03:52
поделиться