Как правильно получить ближайшую рабочую дату в прошлом в 1С?

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

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

Проблема: инклюзивность поиска в БСП

Рассмотрим ситуацию, которую описывает автор темы. При использовании функции КалендарныеГрафики.БлижайшиеРабочиеДаты для даты 16.03.2026 (понедельник), система возвращает эту же дату — 16.03.2026, хотя ожидалось получение предыдущей пятницы. Проанализируем причину такого поведения.

Согласно технической документации 1С, алгоритм поиска "ближайшей" даты работает по принципу нестрогого неравенства. Если в параметрах указано ПолучатьПредшествующие = Истина, система ищет дату, которая Меньше или Равна исходной. Поскольку сама исходная дата (понедельник) является рабочей, расстояние до ближайшего рабочего дня равно нулю, и функция возвращает исходное значение.

Посмотрим на пример кода, который вызывает это поведение:


&НаСервере
Процедура Команда1НаСервере()
    НекаяДата = '20260316'; // Понедельник
    ПроизводственныйКалендарь = Справочники.ПроизводственныеКалендари.НайтиПоКоду("РФ");
    
    Параметры = КалендарныеГрафики.ПараметрыПолученияБлижайшихРабочихДат(ПроизводственныйКалендарь);
    Параметры.ПолучатьПредшествующие = Истина; 
    Параметры.ПолучатьДатыЕслиКалендарьНеЗаполнен = Истина;
    
    СоответвиеРабочиеДни = КалендарныеГрафики.БлижайшиеРабочиеДаты(
        ПроизводственныйКалендарь,
        ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(НекаяДата),
        Параметры);
    
    // Результат будет '20260316', так как это рабочий день
    ПредыдущийРабочийДень = СоответвиеРабочиеДни.Получить(НекаяДата);
КонецПроцедуры

Решение 1: Использование смещения (метод «минус один день»)

Самый простой и очевидный способ заставить систему искать именно в прошлом, исключая текущий день, — это передать в функцию дату, уменьшенную на одну секунду или на один день. Выясним, как это реализовать на практике. Если мы вычтем из текущей даты хотя бы одну секунду, то условие «меньше или равно» для исходного дня уже не выполнится, и алгоритм перейдет к поиску реально предшествующего рабочего дня.

Обновим наш алгоритм:


// Вместо исходной даты передаем вчерашний день
ДатаДляПоиска = НекаяДата - 86400; // Вычитаем сутки (24 * 60 * 60)

СоответвиеРабочиеДни = КалендарныеГрафики.БлижайшиеРабочиеДаты(
    ПроизводственныйКалендарь,
    ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ДатаДляПоиска),
    Параметры);

// Теперь в результате будет пятница 13.03.2026
ПредыдущийРабочийДень = СоответвиеРабочиеДни.Получить(ДатаДляПоиска);

Важный нюанс: при таком подходе в качестве ключа в Соответствие нужно использовать именно ту дату (измененную), которую вы передали в массив. Этот метод надежен, но многие разработчики считают его "костылем".

Решение 2: Использование функции ДобавитьКДатеРабочиеДни

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

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


ПроизводственныйКалендарь = Справочники.ПроизводственныеКалендари.НайтиПоКоду("РФ");
ПредыдущийРабочийДень = КалендарныеГрафики.ДобавитьКДатеРабочиеДни(НекаяДата, -1, ПроизводственныйКалендарь);

Этот метод предпочтительнее по нескольким причинам:

  1. Код становится более читаемым и понятным (прослеживается явное намерение "получить прошлый рабочий день").
  2. Исключаются ошибки, связанные с ручным вычитанием секунд.
  3. Функция оптимизирована для работы с производственными календарями внутри типовых конфигураций.

Решение 3: Получение данных через запрос (для отчетов)

Если задача стоит в рамках разработки сложного отчета, где нужно получить предыдущие рабочие даты сразу для большого количества строк, вызов функций в цикле может существенно замедлить работу. В этом случае рассмотрим вариант использования запроса к регистру сведений ДанныеПроизводственныхКалендарей.

В запросе мы можем явно указать строгое условие «меньше». Проанализируем пример логики запроса:


ВЫБРАТЬ МАКСИМУМ(Календарь.Дата) КАК ПредыдущийРабочийДень
ИЗ РегистрСведений.ДанныеПроизводственныхКалендарей КАК Календарь
ГДЕ Календарь.ПроизводственныйКалендарь = &Календарь
  И Календарь.Дата < &ИсходнаяДата
  И Календарь.ВидДня В (ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий), 
                      ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Предпраздничный))

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

Особенности заполнения календаря

Стоит обратить внимание на параметр ПолучатьДатыЕслиКалендарьНеЗаполнен. Если этот флаг установлен в Истина, система при отсутствии данных в справочнике ПроизводственныеКалендари будет использовать логику обычной пятидневки (суббота и воскресенье — выходные).

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

Резюме

Подведем итоги нашего анализа. Если вам нужно получить строго предшествующий рабочий день:

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

← На главную