При разработке алгоритмов обработки периодов разработчики нередко сталкиваются с ошибкой «Преобразование значения к типу Дата не может быть выполнено», а также с неожиданным поведением при попытке прибавить дни к дате. Разберем подробно, почему возникает данная ошибка, как работает встроенная арифметика дат в платформе 1С:Предприятие и как правильно организовать работу с временными интервалами.
Проанализируем исходную ситуацию. При выполнении кода:
ДатаНач = НачалоДня(ДатаНач) + 1;
ДатаКон = КонецДня(ДатаКон);
Платформа выбрасывает исключение, если переменная ДатаНач или ДатаКон еще не содержит корректную дату. Выделим основные причины возникновения этой проблемы:
Неопределено. Встроенные функции НачалоДня() и КонецДня() ожидают на вход строго тип Дата (или строку установленного формата для неявного приведения). Попытка передать в них Неопределено или NULL вызывает критическую ошибку.Дата, Строка, Число) или поле ввода не заполнено пользователем, значением реквизита будет Неопределено, а не пустая дата вида '00010101'.NULL, которое также не может быть автоматически обработано функциями работы с датами.Разберем вторую частую ошибку, связанную со сдвигом дат. Выражение + 1 в коде НачалоДня(ДатаНач) + 1 не сдвигает дату на следующий день.
Важное правило платформы 1С: любое сложение или вычитание числа с объектом типа Дата всегда выполняется в секундах.
Посмотрим на практические примеры смещения времени:
НачалоДня(ТекущаяДатаСеанса()) + 1 возвращает время 00:00:01 тех же самых суток.24 * 60 * 60 = 86400 секунд. Для перехода на следующий день необходимо прибавлять 86400.КонецДня(ДатаНач) + 1 (к 23:59:59 прибавляется 1 секунда, что дает 00:00:00 следующего дня).Рассмотрим, как правильно организовать проверку значений перед вычислением границ периода. Для защиты от значений Неопределено и NULL используем функцию ЗначениеЗаполнено(), а для получения актуального времени пользователя — функцию ТекущаяДатаСеанса():
// Проверяем заполнение начальной даты
Если Не ЗначениеЗаполнено(ДатаНач) Тогда
ДатаНач = ТекущаяДатаСеанса();
КонецЕсли;
// Устанавливаем начало дня со сдвигом на 1 сутки вперед
ДатаНач = НачалоДня(ДатаНач) + 86400;
// Проверяем заполнение конечной даты
Если Не ЗначениеЗаполнено(ДатаКон) Тогда
ДатаКон = ТекущаяДатаСеанса();
КонецЕсли;
ДатаКон = КонецДня(ДатаКон);
Если требуется выполнить сдвиг на более крупные интервалы (месяцы или годы), используем специализированную встроенную функцию ДобавитьМесяц():
// Сдвиг на 1 месяц вперед
ДатаСледующегоМесяца = ДобавитьМесяц(ДатаНач, 1);
// Сдвиг на 1 год назад
ДатаПрошлогоГода = ДобавитьМесяц(ДатаНач, -12);
Если расчет интервалов требуется перенести в текст запроса или схему компоновки данных (СКД), правила синтаксиса меняются. В языке запросов 1С предусмотрены отдельные конструкции:
НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ) и КОНЕЦПЕРИОДА(&Дата, ДЕНЬ).ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, 1), где явно указывается тип интервала (СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, ГОД).Пример использования в запросе:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаНач, ДЕНЬ, 1), ДЕНЬ) КАК ДатаНачалаСледДня,
КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ) КАК ДатаОкончанияПериода
В параметрах СКД всегда явно задавайте тип Дата, чтобы избежать передачи пустого значения Неопределено из настроек компоновщика.