В практике разработки на платформе 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. Наша задача — перенести временную составляющую из второго объекта в первый.
Рассмотрим наиболее изящный способ, который основан на вычислении разницы в секундах. Мы берем переменную с датой и прибавляем к ней количество секунд, прошедших с начала дня в переменной с временем. Разберем по шагам логику этого вычисления:
Посмотрим на пример кода, который реализует эту логику:
// Допустим, у нас есть:
// РеквизитДата = 04.11.2019 00:00:00
// РеквизитВремя = 01.01.0001 10:00:00 (или любая другая дата с нужным временем)
Результат = РеквизитДата + (РеквизитВремя - НачалоДня(РеквизитВремя));
Почему это работает? Выражение в скобках (РеквизитВремя - НачалоДня(РеквизитВремя)) возвращает тип Число — общее количество секунд от 00:00:00 до указанного часа, минуты и секунды. Прибавляя это число к дате, мы смещаем её ровно на нужный интервал.
Проанализируем ситуацию, когда арифметический метод кажется слишком «магическим» и хочется более явного кода. Мы можем создать новый объект Дата, передав в конструктор год, месяц и день из первой переменной, а час, минуту и секунду — из второй.
Этот метод считается максимально надежным, так как он не зависит от того, какая дата (текущая или пустая) установлена в реквизите времени.
Результат = Дата(Год(РеквизитДата),
Месяц(РеквизитДата),
День(РеквизитДата),
Час(РеквизитВремя),
Минута(РеквизитВремя),
Секунда(РеквизитВремя));
Рассмотрим преимущества этого подхода: он легко читается даже начинающим программистом и исключает ошибки, связанные с переполнением или специфическим поведением платформы при сложении больших чисел. Однако стоит учитывать, что вызов шести функций извлечения составляющих даты работает чуть медленнее, чем простая арифметика.
Выясним причину, по которой предыдущие способы не сработают в языке запросов. В запросах 1С нельзя просто складывать даты и числа оператором +. Здесь нам на помощь приходит встроенная функция ДОБАВИТЬКДАТЕ. Мы также используем функцию РАЗНОСТЬДАТ, чтобы получить разницу в секундах.
Пример реализации логики в запросе:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(Таблица.РеквизитДата, СЕКУНДА,
РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Таблица.РеквизитВремя, ДЕНЬ), Таблица.РеквизитВремя, СЕКУНДА)) КАК ИтоговаяДатаВремя
ИЗ
Справочник.ВашСправочник КАК Таблица
В Системе Компоновки Данных (СКД) в вычисляемых полях можно использовать аналогичный синтаксис. Не забывайте использовать функцию ЕСТЬNULL, если ваши реквизиты могут быть не заполнены, иначе результат всего выражения станет NULL.
Иногда программисты прибегают к форматированию дат в строки, их конкатенации (склеиванию) и последующему преобразованию обратно в дату. Рассмотрим, как это выглядит:
СтрокаДаты = Формат(РеквизитДата, "ДФ=yyyyMMdd");
СтрокаВремени = Формат(РеквизитВремя, "ДФ=HHmmss");
Результат = Дата(СтрокаДаты + СтрокаВремени);
Важное замечание: Мы крайне не рекомендуем использовать этот метод в высоконагруженных участках кода. Преобразование даты в строку и обратно — это тяжелая операция для процессора. Используйте её только в крайних случаях, например, при парсинге данных из внешних текстовых файлов.
При работе с объединением даты и времени важно всегда выполнять предварительную проверку заполнения. Рассмотрим типичный сценарий: если РеквизитДата пустой, то результатом сложения по первому способу будет корректное время, но привязанное к 0001 году. В бизнес-логике это часто считается ошибкой.
Рекомендуем внедрить проверку перед объединением:
Если Не ЗначениеЗаполнено(РеквизитДата) Тогда
// Сообщить пользователю об ошибке или установить текущую дату
РеквизитДата = НачалоДня(ТекущаяДата());
КонецЕсли;
Результат = РеквизитДата + (РеквизитВремя - НачалоДня(РеквизитВремя));
Также стоит помнить о часовых поясах. Если ваш сервер 1С находится в одном часовом поясе, а пользователи работают в разных, убедитесь, что обе переменные приведены к единому стандарту времени (либо к локальному времени пользователя, либо к UTC), прежде чем производить арифметические действия. Использование функций МестноеВремя и УниверсальноеВремя поможет избежать смещения времени на несколько часов при записи в базу данных.