Работа с производственным календарем на PHP



















Бывают ситуации, когда необходимо рассчитать дату с учетом именно рабочих, а не календарных дней. На какую-либо алгоритмизацию здесь полагаться достоверно не приходится, поскольку правительство РФ то и дело штампует исключения из правил. То новогодние выходные перенесут на май, то субботу сделают рабочей, а выходной перенесут на будний день. И так каждый год! И все эти ситуации необходимо учитывать.


В интернете на этот счет есть несколько решений, но все они либо некорректные, либо совершенно неудобные. Несколько таких решений: Один, Два и Три.



В связи с таким положением дел и был написан класс WorkCalendar, позволяющий удобно работать с производственным календарем. WorkCalendar расширяет возможности класса CarbonCarbon, поэтому работать с ним одно удовольствие. Итак, добавленные методы:




  • isWorkday(): bool - true, если день рабочий, иначе false. Один из наиболее удобных методов. Позволяет выяснить, является ли текущий день рабочим. Пример:
    $workday = WorkCalendar::create('2018', '02', '22');
    print_r($workday->isWorkday()); // true

    $workday = WorkCalendar::create('2018', '02', '23');
    print_r($date->isWorkday()); // true





  • diffInWorkdays(WorkCalendar $carbon): int - возвращает разницу в рабочих днях между двумя датами. Может возвращать отрицательное значение, если передаваемая дата меньше(раньше) текущей. Пример:
    $first = WorkCalendar::create('2018', '02', '23');
    $second = WorkCalendar::create('2018', '02', '08');
    print_r($first->diffInWorkdays($second)); // 8





  • addWorkday() - добавить рабочий день к текущей дате. Пример:
    $first = WorkCalendar::create('2018', '02', '26');
    $first->subWorkday();
    print_r($first->format('Y-m-d')); // 2018-02-22





  • subWorkday() - отнять рабочий день от текущей даты. Пример:
    $first = WorkCalendar::create('2018', '02', '26');
    $first->subWorkday();
    print_r($first->format('Y-m-d')); // 2018-02-22





  • addWorkdays(int $count) - добавить $count рабочих дней к текущей дате. Крайне полезен в ситуациях "через 10 рабочих дней", "в течение 5 рабочих дней", коими грешат наши государственные конторы. Пример:
    $first = WorkCalendar::create('2018', '03', '01');
    $first->addWorkdays(5);
    print_r($first->format('Y-m-d')); // 2018-03-12





  • subWorkdays(int $count) - отнять $count рабочих дней от текущей даты. Пример:
    $first = WorkCalendar::create('2018', '03', '12');
    $first->subWorkdays(5);
    print_r($first->format('Y-m-d')); // 2018-03-01



Устанавливается данный хелпер через composer:


$ composer require tochka-developers/work-calendar


Текущие ограничения

Для корректной работы класса, ему нужно откуда-то брать информацию о всех праздничных днях. Этим источником является xmlcalendar.ru. Так вот, там есть данные только начиная с 2013 года. Также данный хелпер учитывает только официальные праздники РФ, но не какие-либо региональные праздники.




Собственный производственный календарь

Производственный календарь использует массив из 365 элементов, который может принимать 0 в случае, если день является праздничным, и 1 иначе. Соответственно, чтобы использовать собственный производственный календарь основанный на национальных праздниках, например, Республики Казахстан, для этого нужно сделать несколько вещей:



  • Определить класс, который будет отвечать за формирование маски рабочих дней. Для этого необходимо отнаследоваться от класса TochkaCalendarAbstractYearMaskProvider и определить метод generateYearMask(int $year), который и занимается формированием массива с маской рабочих дней.

  • В новом классе переопределить константу RES_DIR, указывающую, в какую директорию необходимо сохранять маски рабочих дней, чтобы постоянно их не генерировать.

  • При использовании рабочего календаря с помощью метода TochkaCalendarWorkCalendar::setMaskProvider(AbstractYearMaskProvider $provider) указать вновь созданный класс.


Пример:


...
// Определяем новый класс, который генерирует
// необходимый нам производственный календарь
class KzYearMaskProvider extends AbstractYearMaskProvider
{
const RES_DIR = __DIR__ . '/../resources/kz/';

protected function generateYearMask(int $year)
{
$mask = array_fill(0, 365, 0);

// Тут должна быть логика выявления
// рабочих дней и их указания в маске $mask

return $mask;
}
}

WorkCalendar::setMaskProvider(new KzYearMaskProvider());

// Дальше можем использовать WorkCalendar
$date = WorkCalendar::now();
$date->addWorkdays(5); // Добавляем 5 рабочих дней, используя маску KzYearMaskProvider
...


Ссылки

Ссылка на github.


Ссылка на packagist.


3791   0  

Comments

    Ничего не найдено.