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

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

При разработке учетных механизмов и отчетов часто возникает задача сопоставить только время суток, проигнорировав календарную дату. Классический пример — проверка времени оформления заказа клиента на соответствие графику приема заказов поставщиком, где время ограничения хранится как дополнительный реквизит со значением вида 01.01.0001 15:00:00, а заказ оформлен текущей датой.

Напрямую сравнить такие значения оператором < или > в запросе нельзя, так как платформа 1С сравнивает дату целиком, включая год, месяц и день. Разберем особенности платформы, выясним причины типичных ошибок и подробно рассмотрим рабочие способы решения этой задачи.

Ограничения языка запросов 1С при работе с датами

Прежде чем перейти к написанию запроса, проанализируем ограничения платформы 1С:

  1. Отсутствие функций извлечения компонентов времени: В отличие от встроенного языка 1С (где доступны функции Час(), Минута(), Секунда()), в языке запросов таких функций нет.
  2. Отсутствие прямой арифметики дат: В запросах нельзя вычитать или складывать даты напрямую операторами + и - (выражение вида Дата1 - Дата2 вызовет синтаксическую ошибку «Неверные параметры»). Для вычислений предназначены только функции РАЗНОСТЬДАТ и ДОБАВИТЬКДАТЕ.
  3. Хранение пустого дня: Если для реквизита выбран состав даты «Время», на уровне базы данных дата сохраняется как пустая дата — 01.01.0001.

Почему возникает ошибка «Неверные параметры» в РАЗНОСТЬДАТ?

При попытке рассчитать интервал с помощью функции РАЗНОСТЬДАТ разработчики часто сталкиваются с ошибкой разбора запроса. Выясним две главные причины ее возникновения:

  1. Составной тип дополнительных реквизитов: Поле Значение табличной части дополнительных реквизитов имеет составной тип. Функция РАЗНОСТЬДАТ строго требует тип ДАТА. Чтобы запрос выполнился корректно, значение необходимо явно типизировать конструкцией ВЫРАЗИТЬ(Поле КАК ДАТА).
  2. Наличие значений NULL: При левом соединении с таблицей реквизитов или в случаях, когда реквизит не заполнен, в поле возвращается NULL. Всегда оборачивайте значение в функцию ЕСТЬNULL с передачей пустой даты ДАТАВРЕМЯ(1, 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 вернет значение Истина, что полностью соответствует логике сравнения времени суток.

Способ 2. Приведение времени дедлайна к календарной дате заказа

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

Посмотрим на реализацию данного подхода в коде запроса:


ВЫБОР
    КОГДА ЗаказКлиента.Дата < ДОБАВИТЬКДАТЕ(
            НАЧАЛОПЕРИОДА(ЗаказКлиента.Дата, ДЕНЬ),
            СЕКУНДА,
            РАЗНОСТЬДАТ(
                ДАТАВРЕМЯ(1, 1, 1), 
                ВЫРАЗИТЬ(ЕСТЬNULL(ДопРеквизиты.Значение, ДАТАВРЕМЯ(1, 1, 1)) КАК ДАТА), 
                СЕКУНДА
            )
        )
    ТОГДА ИСТИНА
    ИНАЧЕ ЛОЖЬ
КОНЕЦ

Способ 3. Сравнение времени во встроенном языке (после выборки)

Если логика проверки вынесена из запроса во встроенный язык 1С, вычисление секунд от начала дня реализуется с помощью функции НачалоДня() и базовой операции вычитания дат (в BSL вычитание дат возвращает разницу в секундах).

Разберем пример обработки строк таблицы значений:


Для Каждого СтрокаТЗ Из РезультатТаблица Цикл
    СекундыЗаказа = СтрокаТЗ.ДатаЗаказа - НачалоДня(СтрокаТЗ.ДатаЗаказа);
    СекундыОграничения = СтрокаТЗ.ВремяПоставщика - НачалоДня(СтрокаТЗ.ВремяПоставщика);
    
    Если СекундыЗаказа < СекундыОграничения Тогда
        // Заказ оформлен вовремя
    Иначе
        // Превышено допустимое время приема заказа
    КонецЕсли;
КонецЦикла;

Совет: Если есть возможность изменить архитектуру хранения настроек, рекомендуется хранить время либо в виде числа (количество минут/секунд от полуночи), либо в виде строки фиксированной длины формата ЧЧ:ММ:СС. Строки такого формата корректно сравниваются стандартными операторами < и > без необходимости сложных вычислений в запросах.

← На главную