Как реализовать функцию проверки выходных и праздничных дней в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке специализированных решений на платформе 1С, таких как системы мониторинга транспорта или логистические модули, разработчики часто сталкиваются с необходимостью программно определять, является ли конкретная дата рабочим днем или выходным. В типовых конфигурациях (ЗУП, ERP, Бухгалтерия) для этого существует встроенный производственный календарь, однако в самописных или узкоспециализированных базах его может не быть. Разберем подробно, как решить эту задачу различными способами: от использования внутренних механизмов до интеграции с внешними сервисами.

Вариант 1. Использование встроенных механизмов БСП

Если ваша конфигурация построена на основе Библиотеки стандартных подсистем (БСП), в ней уже заложен мощный функционал для работы с календарями. Прежде чем изобретать «велосипед», проанализируем состав общих модулей. За работу с датами обычно отвечает подсистема КалендарныеГрафики.

Рассмотрим, как вызвать готовую функцию, если подсистема внедрена. В программном интерфейсе БСП существует метод, позволяющий проверить статус дня. Пример использования может выглядеть следующим образом:


// Проверка через типовой механизм БСП
Результат = КалендарныеГрафики.ДатаЯвляетсяВыходным(НужнаяДата, Справочники.ПроизводственныеКалендари.НайтиПоНаименованию("Российская Федерация"));
Если Результат Тогда
    Сообщить("Это нерабочий день");
КонецЕсли;

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

Вариант 2. Реализация через собственный регистр сведений

В случаях, когда конфигурация полностью уникальна и не содержит БСП, нам необходимо создать собственную структуру данных. Рассмотрим по шагам, как это реализовать. Наилучшим объектом для хранения такой информации является Регистр сведений.

Шаг 1. Создание структуры регистра. Создадим непериодический регистр сведений, назовем его, например, РегламентированныйПроизводственныйКалендарь. В качестве измерения установим ДатаКалендаря (тип Дата, состав даты — Дата). В качестве ресурса добавим ВидДня (тип ПеречислениеСсылка.ВидыДнейПроизводственногоКалендаря).

Шаг 2. Разработка функции проверки. Проанализируем пример кода, который позволяет проверить статус дня на основе данных этого регистра. Важно предусмотреть обработку ситуации, когда данных в регистре нет.


Функция ЭтоРабочийДень(ПроверяемаяДата = Неопределено) Экспорт
    Если ПроверяемаяДата = Неопределено Тогда
        ПроверяемаяДата = ТекущаяДата();
    КонецЕсли;
    
    // Приводим к началу дня для корректного поиска в регистре
    ДатаПоиска = НачалоДня(ПроверяемаяДата);
    
    // Создаем запрос или используем менеджер регистра
    Запись = РегистрыСведений.РегламентированныйПроизводственныйКалендарь.СоздатьМенеджерЗаписи();
    Запись.ДатаКалендаря = ДатаПоиска;
    Запись.Прочитать();
    
    Если Запись.Выбран() Тогда
        ВидДня = Запись.ВидДня;
        // Проверяем, является ли день рабочим или предпраздничным (сокращенным)
        Если ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий 
           ИЛИ ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Предпраздничный Тогда
            Возврат Истина;
        Иначе
            Возврат Ложь;
        КонецЕсли;
    Иначе
        // Если данных в календаре нет, по умолчанию считаем будни рабочими
        ДеньНед = ДеньНедели(ДатаПоиска);
        Возврат НЕ (ДеньНед = 6 ИЛИ ДеньНед = 7);
    КонецЕсли;
КонецФункции

Важное замечание по производительности: Если вам требуется проверить большое количество дат (например, в цикле при расчете рейсов транспорта), использование СоздатьМенеджерЗаписи() или набора записей внутри цикла крайне неэффективно. В такой ситуации проанализируем возможность предварительной выгрузки данных календаря в таблицу значений или использование запроса к регистру по всему периоду сразу.

Вариант 3. Динамическая проверка через внешние API

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

Выясним, как реализовать HTTP-запрос к такому сервису. Большинство этих сервисов возвращают простой ответ (0 или 1), что очень удобно для парсинга в 1С. Рассмотрим пример функции, использующей HTTPСоединение:


Функция ПроверитьДеньЧерезAPI(ПроверяемаяДата) Экспорт
    ПараметрыДаты = Формат(ПроверяемаяДата, "ДФ=yyyyMMdd");
    Соединение = Новый HTTPСоединение("isdayoff.ru",,,, Неопределено, 5); // Таймаут 5 секунд
    Запрос = Новый HTTPЗапрос("/api/getdata?date=" + ПараметрыДаты);
    
    Попытка
        Ответ = Соединение.Получить(Запрос);
        Если Ответ.КодСостояния = 200 Тогда
            // 0 - рабочий день, 1 - выходной, 2 - сокращенный
            КодДня = Ответ.ПолучитьТелоКакСтроку();
            Возврат (КодДня = "0" ИЛИ КодДня = "2");
        КонецЕсли;
    Исключение
        // В случае ошибки связи используем расчетный метод
        ДеньНед = ДеньНедели(ПроверяемаяДата);
        Возврат НЕ (ДеньНед = 6 ИЛИ ДеньНед = 7);
    КонецПопытки;
КонецФункции

Оптимизация: При использовании внешних API обязательно внедряйте кэширование. Результат запроса для конкретной даты можно сохранять в глобальную переменную типа Соответствие или во временную таблицу. Это позволит избежать повторных медленных интернет-запросов при многократной проверке одной и той же даты в течение одного сеанса работы.

Вариант 4. Расчет рабочих дней за период

Часто задача звучит шире: не просто проверить один день, а вычислить дату окончания работ через N рабочих дней. Посмотрим на алгоритм, который использует рекурсивный подход или простой цикл для прибавления только рабочих смен. Рассмотрим пример реализации:


Функция ДобавитьРабочиеДни(НачальнаяДата, КолВоДней) Экспорт
    ТекущаяДата = НачалоДня(НачальнаяДата);
    ДобавленоДней = 0;
    
    Пока ДобавленоДней < КолВоДней Цикл
        ТекущаяДата = ТекущаяДата + 86400; // Прибавляем сутки
        Если ЭтоРабочийДень(ТекущаяДата) Тогда
            ДобавленоДней = ДобавленоДней + 1;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ТекущаяДата;
КонецФункции

Здесь функция ЭтоРабочийДень может быть реализована любым из описанных выше способов. Такой подход гарантирует точность расчетов даже при наличии длинных праздничных каникул (например, новогодних).

Региональные особенности и нюансы

Проанализируем ситуацию, когда мониторинг транспорта ведется в разных регионах. Важно помнить, что в некоторых республиках РФ (Татарстан, Башкортостан и др.) существуют локальные праздничные дни, которые являются выходными только в данном субъекте. Если ваша система должна учитывать такие тонкости, структура регистра сведений должна быть дополнена измерением ПроизводственныйКалендарь или Регион. Это позволит хранить несколько графиков одновременно и выбирать нужный в зависимости от контекста задачи (например, по месту регистрации транспортного средства).

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

← На главную