При разработке регламентных заданий или специализированных обработок периодически возникает задача: обновить дату документа (перенести его на текущий день), но при этом оставить часы, минуты и секунды в точности такими, какими они были при создании. Если просто присвоить значение системной функции ТекущаяДата() или ТекущаяДатаСеанса(), документ получит текущее время сервера, что нарушит исходную хронологию внутри дня. В то же время отсечение времени стандартными средствами платформы обнуляет его до 00:00:00.
Разберем природу типа данных Дата в платформе 1С:Предприятие, выясним причину распространенных ошибок при форматировании и подробно рассмотрим корректные программные способы решения этой задачи.
В платформе 1С отсутствует отдельный тип данных «Время» — существует только составной тип Дата, содержащий календарную дату и время с точностью до секунды. Если у даты не задана временная составляющая, платформа автоматически считает время равным 00:00:00. Если же дата не заполнена вовсе, формируется пустая дата — 01.01.0001 00:00:00.
Посмотрим на типичную ошибку, которую часто допускают разработчики при попытке «очистить» дату:
// Ошибочный подход:
ДокументОбъект.Дата = Формат(ТекущаяДатаСеанса(), "ДЛФ=Д");
Проанализируем, почему этот код не работает. Функция Формат() всегда возвращает строковое значение (например, "25.10.2023"). При попытке неявного приведения строки к типу Дата реквизита документа платформа не может распознать произвольную локализованную строку и без генерации исключения подставляет пустую дату 01.01.0001 00:00:00. Конструктор Дата() от строки принимает исключительно канонический формат вида ГГГГММДДччммсс или ГГГГММДД, поэтому передача строки из Формат(..., "ДЛФ=Д") завершается ошибкой времени выполнения.
Также не решает задачу функция НачалоДня() в чистом виде: вызов НачалоДня(ТекущаяДатаСеанса()) вернет дату с обнуленным временем 00:00:00, полностью стерев исходные часы и минуты документа.
В платформе 1С значения типа Дата поддерживают операторы сложения и вычитания. Разность двух дат возвращает количество секунд между ними, а прибавление числа к дате увеличивает ее на указанное количество секунд. Это позволяет вычислить время документа как смещение в секундах от начала его дня и прибавить это смещение к началу текущего дня.
Рассмотрим, как выглядит этот лаконичный и быстрый алгоритм:
ТекущееНачалоДня = НачалоДня(ТекущаяДатаСеанса());
СмещениеВремени = ИсходнаяДата - НачалоДня(ИсходнаяДата);
НоваяДата = ТекущееНачалоДня + СмещениеВремени;
Посмотрим на конкретный пример применения этого метода при перезаписи документа:
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
ИсходнаяДата = ДокументОбъект.Дата;
ДокументОбъект.Дата = НачалоДня(ТекущаяДатаСеанса()) + (ИсходнаяДата - НачалоДня(ИсходнаяДата));
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Разность ИсходнаяДата - НачалоДня(ИсходнаяДата) вычисляет, сколько секунд прошло с полуночи исходного документа. Прибавив полученное число к НачалоДня(ТекущаяДатаСеанса()), мы гарантированно получаем сегодняшнюю дату с исходным временем с точностью до секунды.
Второй надежный подход заключается в использовании конструктора Дата(Год, Месяц, День, Час, Минута, Секунда). В качестве календарных частей мы передаем текущие значения, а в качестве временных — реквизиты исходного документа.
Разберем этот вариант в коде:
ТекущийМомент = ТекущаяДатаСеанса();
ИсходнаяДата = ДокументОбъект.Дата;
ДокументОбъект.Дата = Дата(
Год(ТекущийМомент),
Месяц(ТекущийМомент),
День(ТекущийМомент),
Час(ИсходнаяДата),
Минута(ИсходнаяДата),
Секунда(ИсходнаяДата)
);
Данный вариант максимально нагляден при чтении кода другими разработчиками и исключает любые математические погрешности при работе со временем.
Еще один альтернативный метод — использование явного форматирования даты в строку строго фиксированного вида. Конструктор Дата() умеет корректно инициализировать значение из строки длиной ровно 14 символов (yyyyMMddHHmmss).
Посмотрим, как реализовать этот подход:
СтрокаДаты = Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd") + Формат(ДокументОбъект.Дата, "ДФ=HHmmss");
ДокументОбъект.Дата = Дата(СтрокаДаты);
Если смена даты выполняется автоматически в фоновом режиме, необходимо учитывать ряд платформенных и прикладных ограничений:
ТекущаяДатаСеанса() вместо ТекущаяДата(). Функция ТекущаяДата() берет время операционной системы сервера, что может привести к нежелательным сдвигам даты, если сервер приложений и пользователи работают в разных часовых поясах.РежимЗаписиДокумента.Запись обновит реквизит самого документа, но не изменит дату в записях регистров накопления и сведений (поле Период останется старым). Если документ проведен, требуется вызывать проведение (РежимЗаписиДокумента.Проведение), что может запустить механизмы контроля остатков.УстановитьПривилегированныйРежим(Истина).Попытка ... Исключение для корректной обработки исключений блокировок.