В разработке на платформе 1С часто возникает задача прибавить к определенной дате не просто календарные дни, а именно рабочие или банковские. Это критично для расчета сроков оплаты счетов, даты поставки или исполнения обязательств по договору. Простая арифметика Дата + (КоличествоДней * 86400) здесь не поможет, так как она не учитывает выходные и праздничные дни. Рассмотрим подробнее, как решить эту задачу различными способами — от встроенных функций БСП до эффективных запросов.
Если ваша конфигурация построена на базе БСП (Библиотека стандартных подсистем), то самым правильным и «чистым» решением будет использование типовых механизмов. Для этого в БСП предусмотрен программный интерфейс в общем модуле КалендарныеГрафики.
Разберем пример кода, который позволяет получить дату через определенное количество рабочих дней. Для этого нам понадобится ссылка на производственный календарь (например, «РФ»).
&НаСервере
Функция ДобавитьКДатеРабочиеДни(ПередаваемаяДата, КоличествоРабочихДней) Экспорт
// Получаем ссылку на основной производственный календарь
ГрафикРаботы = Справочники.ПроизводственныеКалендари.НайтиПоКоду("РФ");
// Если календарь не найден, расчет может быть некорректным
Если ГрафикРаботы.Пустая() Тогда
Возврат Дата(1, 1, 1);
КонецЕсли;
// Используем метод БСП для расчета даты по календарю
СледующийДень = КалендарныеГрафики.ДатаПоКалендарю(ГрафикРаботы, ПередаваемаяДата, КоличествоРабочихДней);
Возврат СледующийДень;
КонецФункции
Этот метод хорош тем, что он учитывает все государственные праздники и переносы, которые уже занесены в систему. Важный нюанс: функция ДатаПоКалендарю умеет работать и в обратную сторону — если передать отрицательное число в параметр КоличествоРабочихДней, она вычислит дату, которая была несколько рабочих дней назад.
Когда нам нужно рассчитать даты для большого массива данных (например, в динамическом списке или сложном отчете), вызов функции в цикле может существенно замедлить работу. В этом случае проанализируем ситуацию и поймем: данные о днях хранятся в регистре сведений. В типовых конфигурациях это обычно ДанныеПроизводственныхКалендарей.
Рассмотрим логику запроса для получения даты через N рабочих дней:
ДанныеПроизводственныхКалендарей.Дата > &НашаДата.ВидДня = Рабочий.ВЫБРАТЬ ПЕРВЫЕ N.Посмотрим на пример такого запроса:
ВЫБРАТЬ ПЕРВЫЕ 1
ВложенныйЗапрос.Дата КАК ИскомаяДата
ИЗ (
ВЫБРАТЬ ПЕРВЫЕ 5
Т.Дата КАК Дата
ИЗ
РегистрСведений.ДанныеПроизводственныхКалендарей КАК Т
ГДЕ
Т.ПроизводственныйКалендарь = &Календарь
И Т.Дата > &ДатаНачала
И Т.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий)
УПОРЯДОЧИТЬ ПО
Дата ВОЗРАСТАНИЮ
) КАК ВложенныйЗапрос
УПОРЯДОЧИТЬ ПО
ВложенныйЗапрос.Дата УБЫВАНИЮ
Такой подход работает молниеносно даже на больших объемах данных, так как платформа оптимизирует работу с индексами регистра.
Если вы работаете в старой конфигурации или вам нужно реализовать специфический алгоритм «на лету», можно использовать перебор дней в цикле. Выясним причину, почему это может быть полезно: иногда «банковские дни» не совпадают с «рабочими» (например, банк работает по субботам). В таком случае мы можем проверять каждый день функцией ЭтоРабочийДень().
Функция ДобавитьКДатеРабочиеДниЦиклом(Знач ИсходнаяДата, КоличествоДней) Экспорт
ТекущаяДата = ИсходнаяДата;
ДобавленоДней = 0;
Пока ДобавленоДней < КоличествоДней Цикл
ТекущаяДата = ТекущаяДата + 86400; // Прибавляем одни сутки
Если ЭтоРабочийДень(ТекущаяДата) Тогда
ДобавленоДней = ДобавленоДней + 1;
КонецЕсли;
КонецЦикла;
Возврат ТекущаяДата;
КонецФункции
Функция ЭтоРабочийДень(ПроверяемаяДата)
// Здесь может быть запрос к регистру или простая проверка дня недели
ДеньНедели = ДеньНедели(ПроверяемаяДата);
Возврат ?(ДеньНедели < 6, Истина, Ложь); // Упрощенно: Пн-Пт — рабочие
КонецФункции
Внимание: Будьте осторожны с рекурсией. В некоторых обсуждениях предлагается использовать рекурсивный вызов функции добавления дней. При больших значениях (например, прибавить 100 рабочих дней) это может привести к переполнению стека вызовов.
Проанализируем критическую ошибку, о которой часто забывают разработчики. Производственные календари заполняются вручную или через классификаторы раз в год. Если ваша логика пытается прибавить рабочие дни в конце декабря так, что результат попадает на январь следующего года, а календарь еще не заполнен, система может выдать ошибку (BSOD в 1С или просто аварийное завершение).
Чтобы избежать этой ситуации, придерживайтесь следующих правил:
Важно помнить, что Рабочие дни (по законодательству РФ) и Банковский день — это не всегда одно и то же. Для расчетов внутри РФ они обычно совпадают. Однако, если вы работаете с валютными переводами, могут использоваться календари других стран (например, выходные в США при расчетах в USD). БСП позволяет создавать несколько производственных календарей. В коде всегда явно указывайте, какой именно календарь вы используете в Справочники.ПроизводственныеКалендари, чтобы избежать путаницы между графиком офиса и графиком банковских операций.
Рассмотренные нами методы позволяют гибко настроить расчет дат в любой конфигурации 1С, обеспечивая точность и высокую скорость работы системы.