Работа с производственным календарем на 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
...Ссылки
Comments