В процессе разработки или сопровождения конфигураций на базе 1С:ERP, ЗУП или КА программисты часто сталкиваются с использованием типовых механизмов Библиотеки стандартных подсистем (БСП). Одной из таких функций является ГрафикиРаботы.РазностьДатПоКалендарю. Однако при определенных граничных условиях, а именно при использовании в качестве даты начала последнего дня года (31 декабря), функция начинает вести себя непредсказуемо, возвращая странные отрицательные значения (например, -251), хотя ожидается положительное число дней. Давайте подробно разберем, почему это происходит и как это исправить.
Для того чтобы понять причину сбоя, проанализируем, как система вычисляет разницу дат по календарю. Вместо того чтобы перебирать каждый день в цикле или выполнять тяжелый запрос по всем записям за большой период, разработчики 1С применили оптимизацию. Система использует ресурс КоличествоДнейВГрафикеСНачалаГода в регистре сведений КалендарныеГрафики.
Рассмотрим алгоритм работы функции по шагам:
Проблема кроется в одном небольшом условии, которое должно определять необходимость добавления 31 декабря в массив дат. Посмотрим на типовой код, который вызывает ошибку:
ДатыГрафика = Новый Массив;
ДатыГрафика.Добавить(ДатаНачала);
Если Год(ДатаНачала) <> Год(ДатаОкончания) И КонецДня(ДатаНачала) <> КонецГода(ДатаНачала) Тогда
// Если даты разных годов, то добавляем "границы" годов.
Для НомерГода = Год(ДатаНачала) По Год(ДатаОкончания) - 1 Цикл
ДатыГрафика.Добавить(Дата(НомерГода, 12, 31));
КонецЦикла;
КонецЕсли;
ДатыГрафика.Добавить(ДатаОкончания);
Проанализируем ситуацию, описанную пользователем: ДатаНачала = 31.12.2024, а ДатаОкончания находится в 2025 году.
В этом случае условие КонецДня(ДатаНачала) <> КонецГода(ДатаНачала) возвращает Ложь, так как для 31 декабря конец дня и конец года совпадают. В результате блок добавления границ годов пропускается. Массив ДатыГрафика заполняется некорректно: в нем оказывается только 31.12.2024 и 01.01.2025 без четкого разделения логики перехода.
Когда запрос начинает обрабатывать эти данные, он получает накопленный итог за 2024 год (например, 251 рабочий день) и итог за начало 2025 года (например, 1 день). Из-за отсутствия «точки сброса» накопленного итога, математический алгоритм внутри функции выполняет вычитание 251 из 1 (или аналогичные некорректные действия в зависимости от версии БСП), что и приводит к результату вроде -250 или -251.
Самый простой и логичный способ — это исправить само условие, которое приводит к пропуску даты. Нам необходимо заставить систему всегда добавлять 31 декабря в массив, если года различаются, независимо от того, является ли сама дата начала последним днем года.
Рекомендуемое исправление: необходимо убрать проверку на конец года. Рассмотрим исправленный фрагмент кода:
// Измененное условие
Если Год(ДатаНачала) <> Год(ДатаОкончания) Тогда
Для НомерГода = Год(ДатаНачала) По Год(ДатаОкончания) - 1 Цикл
// Теперь 31.12 будет добавлено всегда при переходе через год
ДатыГрафика.Добавить(Дата(НомерГода, 12, 31));
КонецЦикла;
КонецЕсли;
При таком подходе, если ДатаНачала — 31.12.2024, в массиве окажется две записи с этой датой. Запрос обработает их корректно, и математическая разница между ними составит 0, а затем прибавится корректное количество дней за следующий год.
Если вы не хотите зависеть от логики БСП, которая может измениться при обновлении, или если вам нужно более прозрачное решение, можно написать собственную функцию. Рассмотрим вариант прямого подсчета рабочих дней в регистре сведений КалендарныеГрафики. Это решение работает стабильно и не подвержено ошибкам "граничных дат".
Рассмотрим пример запроса, который решает задачу:
ВЫБРАТЬ
КОЛИЧЕСТВО(Т1.ДатаГрафика) КАК КоличествоДней
ИЗ
РегистрСведений.КалендарныеГрафики КАК Т1
ГДЕ
Т1.Календарь = &Календарь
И Т1.ДатаГрафика МЕЖДУ &ДатаНачала И &ДатаОкончания
И Т1.ДеньВключенВГрафик = ИСТИНА
Этот подход имеет ряд преимуществ:
При анализе ошибок в календарных графиках иногда стоит учитывать фактор високосного года, хотя в данной проблеме он вторичен. Тем не менее, напомним правило определения високосного года, которое может пригодиться при написании своих алгоритмов проверки дат:
Функция Високосный(Год)
Если Год % 400 = 0 Тогда
Возврат Истина;
ИначеЕсли Год % 100 = 0 Тогда
Возврат Ложь;
ИначеЕсли Год % 4 = 0 Тогда
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
КонецФункции
В нашем случае (2024 год) год является високосным, что означает наличие 366 календарных дней и, как правило, на один рабочий день больше в графике. Это объясняет, почему числа в расчетах (например, 251 или 252) могут незначительно отличаться в разных годах, но не отменяет логическую ошибку в РазностьДатПоКалендарю.
Мы проанализировали ситуацию и выяснили, что ошибка в типовой функции РазностьДатПоКалендарю вызвана избыточным условием, которое некорректно обрабатывает дату 31 декабря. Для решения проблемы рекомендуется:
КалендарныеГрафики, убрав проверку КонецДня(ДатаНачала) <> КонецГода(ДатаНачала).КалендарныеГрафики, что будет более универсальным и надежным решением в долгосрочной перспективе.