Как в 1С объединить дату и время из разных переменных или реквизитов?

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

В практике разработки на платформе 1С:Предприятие часто возникает ситуация, когда дата хранится в одном реквизите (например, ДатаДокумента), а время — в другом (например, ВремяСмены). Несмотря на то, что тип данных Дата в 1С всегда содержит и календарную часть, и временную, разработчику нужно «склеить» их так, чтобы получить корректный объект. Разберем детально, как это сделать правильно и эффективно.

Понимание типа данных Дата в 1С

Прежде чем переходить к коду, проанализируем, как 1С работает с датами. В системе 1С тип Дата — это не просто строка или объект, а число секунд, прошедших с начала нашей эры (01.01.0001 00:00:00). Это критически важный момент, так как знание этого факта позволяет нам использовать обычную арифметику для манипуляций с временными интервалами.

Когда мы видим в реквизите «только дату» (например, 04.11.2019), на самом деле там хранится 04.11.2019 00:00:00. Если в реквизите «только время» (например, 10:00:00), то платформа подставляет пустую дату начала времен, и фактически там записано 01.01.0001 10:00:00. Наша задача — перенести временную составляющую из второго объекта в первый.

Способ 1: Арифметическое сложение (самый быстрый метод)

Рассмотрим наиболее изящный способ, который основан на вычислении разницы в секундах. Мы берем переменную с датой и прибавляем к ней количество секунд, прошедших с начала дня в переменной с временем. Разберем по шагам логику этого вычисления:

  1. Выделяем время как число секунд: для этого из переменной со временем вычитаем её же «начало дня».
  2. Прибавляем полученное число к основной дате.

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


// Допустим, у нас есть:
// РеквизитДата = 04.11.2019 00:00:00
// РеквизитВремя = 01.01.0001 10:00:00 (или любая другая дата с нужным временем)

Результат = РеквизитДата + (РеквизитВремя - НачалоДня(РеквизитВремя));

Почему это работает? Выражение в скобках (РеквизитВремя - НачалоДня(РеквизитВремя)) возвращает тип Число — общее количество секунд от 00:00:00 до указанного часа, минуты и секунды. Прибавляя это число к дате, мы смещаем её ровно на нужный интервал.

Способ 2: Использование конструктора Дата

Проанализируем ситуацию, когда арифметический метод кажется слишком «магическим» и хочется более явного кода. Мы можем создать новый объект Дата, передав в конструктор год, месяц и день из первой переменной, а час, минуту и секунду — из второй.

Этот метод считается максимально надежным, так как он не зависит от того, какая дата (текущая или пустая) установлена в реквизите времени.


Результат = Дата(Год(РеквизитДата), 
                Месяц(РеквизитДата), 
                День(РеквизитДата), 
                Час(РеквизитВремя), 
                Минута(РеквизитВремя), 
                Секунда(РеквизитВремя));

Рассмотрим преимущества этого подхода: он легко читается даже начинающим программистом и исключает ошибки, связанные с переполнением или специфическим поведением платформы при сложении больших чисел. Однако стоит учитывать, что вызов шести функций извлечения составляющих даты работает чуть медленнее, чем простая арифметика.

Способ 3: Работа в запросах и СКД

Выясним причину, по которой предыдущие способы не сработают в языке запросов. В запросах 1С нельзя просто складывать даты и числа оператором +. Здесь нам на помощь приходит встроенная функция ДОБАВИТЬКДАТЕ. Мы также используем функцию РАЗНОСТЬДАТ, чтобы получить разницу в секундах.

Пример реализации логики в запросе:


ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(Таблица.РеквизитДата, СЕКУНДА, 
        РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Таблица.РеквизитВремя, ДЕНЬ), Таблица.РеквизитВремя, СЕКУНДА)) КАК ИтоговаяДатаВремя
ИЗ
    Справочник.ВашСправочник КАК Таблица

В Системе Компоновки Данных (СКД) в вычисляемых полях можно использовать аналогичный синтаксис. Не забывайте использовать функцию ЕСТЬNULL, если ваши реквизиты могут быть не заполнены, иначе результат всего выражения станет NULL.

Способ 4: Строковое преобразование (не рекомендуется, но возможно)

Иногда программисты прибегают к форматированию дат в строки, их конкатенации (склеиванию) и последующему преобразованию обратно в дату. Рассмотрим, как это выглядит:


СтрокаДаты = Формат(РеквизитДата, "ДФ=yyyyMMdd");
СтрокаВремени = Формат(РеквизитВремя, "ДФ=HHmmss");
Результат = Дата(СтрокаДаты + СтрокаВремени);

Важное замечание: Мы крайне не рекомендуем использовать этот метод в высоконагруженных участках кода. Преобразование даты в строку и обратно — это тяжелая операция для процессора. Используйте её только в крайних случаях, например, при парсинге данных из внешних текстовых файлов.

Полезные советы и проверка данных

При работе с объединением даты и времени важно всегда выполнять предварительную проверку заполнения. Рассмотрим типичный сценарий: если РеквизитДата пустой, то результатом сложения по первому способу будет корректное время, но привязанное к 0001 году. В бизнес-логике это часто считается ошибкой.

Рекомендуем внедрить проверку перед объединением:


Если Не ЗначениеЗаполнено(РеквизитДата) Тогда
    // Сообщить пользователю об ошибке или установить текущую дату
    РеквизитДата = НачалоДня(ТекущаяДата());
КонецЕсли;

Результат = РеквизитДата + (РеквизитВремя - НачалоДня(РеквизитВремя));

Также стоит помнить о часовых поясах. Если ваш сервер 1С находится в одном часовом поясе, а пользователи работают в разных, убедитесь, что обе переменные приведены к единому стандарту времени (либо к локальному времени пользователя, либо к UTC), прежде чем производить арифметические действия. Использование функций МестноеВремя и УниверсальноеВремя поможет избежать смещения времени на несколько часов при записи в базу данных.

← На главную