Как в 1С изменить дату документа на текущую, сохранив исходное время?

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

При разработке регламентных заданий или специализированных обработок периодически возникает задача: обновить дату документа (перенести его на текущий день), но при этом оставить часы, минуты и секунды в точности такими, какими они были при создании. Если просто присвоить значение системной функции ТекущаяДата() или ТекущаяДатаСеанса(), документ получит текущее время сервера, что нарушит исходную хронологию внутри дня. В то же время отсечение времени стандартными средствами платформы обнуляет его до 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. Арифметическое сложение через разницу секунд (Решение с форума)

В платформе 1С значения типа Дата поддерживают операторы сложения и вычитания. Разность двух дат возвращает количество секунд между ними, а прибавление числа к дате увеличивает ее на указанное количество секунд. Это позволяет вычислить время документа как смещение в секундах от начала его дня и прибавить это смещение к началу текущего дня.

Рассмотрим, как выглядит этот лаконичный и быстрый алгоритм:


ТекущееНачалоДня = НачалоДня(ТекущаяДатаСеанса());
СмещениеВремени = ИсходнаяДата - НачалоДня(ИсходнаяДата);

НоваяДата = ТекущееНачалоДня + СмещениеВремени;

Посмотрим на конкретный пример применения этого метода при перезаписи документа:


ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
    ИсходнаяДата = ДокументОбъект.Дата;
    ДокументОбъект.Дата = НачалоДня(ТекущаяДатаСеанса()) + (ИсходнаяДата - НачалоДня(ИсходнаяДата));
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;

Разность ИсходнаяДата - НачалоДня(ИсходнаяДата) вычисляет, сколько секунд прошло с полуночи исходного документа. Прибавив полученное число к НачалоДня(ТекущаяДатаСеанса()), мы гарантированно получаем сегодняшнюю дату с исходным временем с точностью до секунды.

Способ 2. Конструктор даты по отдельным компонентам

Второй надежный подход заключается в использовании конструктора Дата(Год, Месяц, День, Час, Минута, Секунда). В качестве календарных частей мы передаем текущие значения, а в качестве временных — реквизиты исходного документа.

Разберем этот вариант в коде:


ТекущийМомент = ТекущаяДатаСеанса();
ИсходнаяДата  = ДокументОбъект.Дата;

ДокументОбъект.Дата = Дата(
    Год(ТекущийМомент),
    Месяц(ТекущийМомент),
    День(ТекущийМомент),
    Час(ИсходнаяДата),
    Минута(ИсходнаяДата),
    Секунда(ИсходнаяДата)
);

Данный вариант максимально нагляден при чтении кода другими разработчиками и исключает любые математические погрешности при работе со временем.

Способ 3. Сборка канонической строки формата ГГГГММДДччммсс

Еще один альтернативный метод — использование явного форматирования даты в строку строго фиксированного вида. Конструктор Дата() умеет корректно инициализировать значение из строки длиной ровно 14 символов (yyyyMMddHHmmss).

Посмотрим, как реализовать этот подход:


СтрокаДаты = Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd") + Формат(ДокументОбъект.Дата, "ДФ=HHmmss");
ДокументОбъект.Дата = Дата(СтрокаДаты);

Важные нюансы при пакетной перезаписи документов в регламентных заданиях

Если смена даты выполняется автоматически в фоновом режиме, необходимо учитывать ряд платформенных и прикладных ограничений:

  1. Часовой пояс сервера и сеанса: Всегда используйте ТекущаяДатаСеанса() вместо ТекущаяДата(). Функция ТекущаяДата() берет время операционной системы сервера, что может привести к нежелательным сдвигам даты, если сервер приложений и пользователи работают в разных часовых поясах.
  2. Движения регистров проведенных документов: Простая запись объекта с флагом РежимЗаписиДокумента.Запись обновит реквизит самого документа, но не изменит дату в записях регистров накопления и сведений (поле Период останется старым). Если документ проведен, требуется вызывать проведение (РежимЗаписиДокумента.Проведение), что может запустить механизмы контроля остатков.
  3. Последовательности документов: Перенос даты проведенного документа назад или вперед автоматически сдвигает границу последовательности документов на более раннюю из дат, требуя последующего перепроведения по цепочке.
  4. Даты запрета изменения: Если документ попадает в закрытый период, сервер заблокирует запись. В регламентном коде может потребоваться включение привилегированного режима с помощью вызова УстановитьПривилегированныйРежим(Истина).
  5. Блокировки объектов: При работе задания документ может быть занят другим пользователем. Всегда оборачивайте операцию записи в конструкцию Попытка ... Исключение для корректной обработки исключений блокировок.
← На главную