При разработке учетных механизмов и отчетов часто возникает задача сопоставить только время суток, проигнорировав календарную дату. Классический пример — проверка времени оформления заказа клиента на соответствие графику приема заказов поставщиком, где время ограничения хранится как дополнительный реквизит со значением вида 01.01.0001 15:00:00, а заказ оформлен текущей датой.
Напрямую сравнить такие значения оператором < или > в запросе нельзя, так как платформа 1С сравнивает дату целиком, включая год, месяц и день. Разберем особенности платформы, выясним причины типичных ошибок и подробно рассмотрим рабочие способы решения этой задачи.
Прежде чем перейти к написанию запроса, проанализируем ограничения платформы 1С:
Час(), Минута(), Секунда()), в языке запросов таких функций нет.+ и - (выражение вида Дата1 - Дата2 вызовет синтаксическую ошибку «Неверные параметры»). Для вычислений предназначены только функции РАЗНОСТЬДАТ и ДОБАВИТЬКДАТЕ.01.01.0001.При попытке рассчитать интервал с помощью функции РАЗНОСТЬДАТ разработчики часто сталкиваются с ошибкой разбора запроса. Выясним две главные причины ее возникновения:
Значение табличной части дополнительных реквизитов имеет составной тип. Функция РАЗНОСТЬДАТ строго требует тип ДАТА. Чтобы запрос выполнился корректно, значение необходимо явно типизировать конструкцией ВЫРАЗИТЬ(Поле КАК ДАТА).NULL. Всегда оборачивайте значение в функцию ЕСТЬNULL с передачей пустой даты ДАТАВРЕМЯ(1, 1, 1).Рассмотрим основной и самый надежный способ решения задачи в запросе. Приведем оба времени к количеству секунд, прошедших от полуночи (начала суток), и сравним полученные числовые значения.
Для даты заказа началом дня является НАЧАЛОПЕРИОДА(ЗаказКлиента.Дата, ДЕНЬ). Для дополнительного реквизита с датой 01.01.0001 началом дня служит пустая дата ДАТАВРЕМЯ(1, 1, 1).
Посмотрим на пример условия в секции ГДЕ или в выражении ВЫБОР:
ВЫБОР
КОГДА РАЗНОСТЬДАТ(
НАЧАЛОПЕРИОДА(ЗаказКлиента.Дата, ДЕНЬ),
ЗаказКлиента.Дата,
СЕКУНДА
) < РАЗНОСТЬДАТ(
ДАТАВРЕМЯ(1, 1, 1),
ВЫРАЗИТЬ(ЕСТЬNULL(ДопРеквизиты.Значение, ДАТАВРЕМЯ(1, 1, 1)) КАК ДАТА),
СЕКУНДА
)
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ
Разберем, как работает эта конструкция: если заказ создан в 11:31:01, разность от 00:00:00 составит 41 461 секунду. Если время поставщика задано как 15:00:00, разность от 01.01.0001 00:00:00 составит 54 000 секунд. Числовое сравнение 41461 < 54000 вернет значение Истина, что полностью соответствует логике сравнения времени суток.
Рассмотрим альтернативный способ: вместо сравнения секунд мы можем динамически сформировать полную дату-время ограничения на день заказа, прибавив секунды дедлайна к началу дня документа, и сравнить даты напрямую.
Посмотрим на реализацию данного подхода в коде запроса:
ВЫБОР
КОГДА ЗаказКлиента.Дата < ДОБАВИТЬКДАТЕ(
НАЧАЛОПЕРИОДА(ЗаказКлиента.Дата, ДЕНЬ),
СЕКУНДА,
РАЗНОСТЬДАТ(
ДАТАВРЕМЯ(1, 1, 1),
ВЫРАЗИТЬ(ЕСТЬNULL(ДопРеквизиты.Значение, ДАТАВРЕМЯ(1, 1, 1)) КАК ДАТА),
СЕКУНДА
)
)
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ
Если логика проверки вынесена из запроса во встроенный язык 1С, вычисление секунд от начала дня реализуется с помощью функции НачалоДня() и базовой операции вычитания дат (в BSL вычитание дат возвращает разницу в секундах).
Разберем пример обработки строк таблицы значений:
Для Каждого СтрокаТЗ Из РезультатТаблица Цикл
СекундыЗаказа = СтрокаТЗ.ДатаЗаказа - НачалоДня(СтрокаТЗ.ДатаЗаказа);
СекундыОграничения = СтрокаТЗ.ВремяПоставщика - НачалоДня(СтрокаТЗ.ВремяПоставщика);
Если СекундыЗаказа < СекундыОграничения Тогда
// Заказ оформлен вовремя
Иначе
// Превышено допустимое время приема заказа
КонецЕсли;
КонецЦикла;
Совет: Если есть возможность изменить архитектуру хранения настроек, рекомендуется хранить время либо в виде числа (количество минут/секунд от полуночи), либо в виде строки фиксированной длины формата ЧЧ:ММ:СС. Строки такого формата корректно сравниваются стандартными операторами < и > без необходимости сложных вычислений в запросах.