К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как в запросе 1С вычесть дату и получить разницу

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

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

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

Ниже разобраны оба подхода с готовыми фрагментами кода, ограничениями платформы, способами проверки результата и типичными ошибками. Примеры применимы к любым типовым и самописным конфигурациям на управляемых и обычных формах, так как язык запросов одинаков.

Как получить разницу между датами через РАЗНОСТЬДАТ

Функция РАЗНОСТЬДАТ принимает три параметра: начальную дату, конечную дату и тип периода, в котором нужно получить результат. Синтаксис: РАЗНОСТЬДАТ(<Дата1>, <Дата2>, <Тип>). Если вторая дата больше первой, результат положительный; если меньше — отрицательный. Это удобно для проверки просрочки: отрицательное значение сразу показывает, что срок вышел.

Допустимые типы периода: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, КВАРТАЛ, ГОД. Важно понимать, что результат всегда целое число — функция возвращает количество полных периодов между датами. Разница в 1 день и 23 часа при типе ДЕНЬ даст 1, а не 1,96.

ВЫБРАТЬ

Заказ.Ссылка КАК Заказ,

РАЗНОСТЬДАТ(Заказ.Дата, Заказ.ДатаОтгрузки, ДЕНЬ) КАК ДнейДоОтгрузки

ИЗ

Документ.ЗаказПокупателя КАК Заказ

Такой запрос вернёт по каждому заказу количество дней от оформления до отгрузки. Если часть документов имеет незаполненную дату отгрузки, результат для них будет некорректным — обработка пустых дат разобрана ниже.

Как сдвинуть дату назад через ДОБАВИТЬКДАТЕ

Функция ДОБАВИТЬКДАТЕ прибавляет к дате заданное количество периодов: ДОБАВИТЬКДАТЕ(<Дата>, <Тип>, <Количество>). Чтобы «вычесть» время, передайте отрицательное количество. Например, ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -7) вернёт дату семь дней назад.

Типичный сценарий — отбор документов за последние N дней без вычисления даты на встроенном языке. Это удобно, когда запрос формируется динамически или используется в СКД, где параметр удобнее вычислить прямо в тексте запроса.

ВЫБРАТЬ

Реализация.Ссылка КАК Документ,

Реализация.Дата КАК Дата

ИЗ

Документ.РеализацияТоваровУслуг КАК Реализация

ГДЕ

Реализация.Дата >= ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -30)

Параметр &ТекущаяДата перед вызовом заполняется значением ТекущаяДатаСеанса(). Использовать дату сеанса предпочтительнее, чем локальное время компьютера: в серверном режиме часы клиента и сервера могут расходиться, и отбор «за последние 30 дней» съедет.

bsl
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДатаСеанса());

Выбор единиц измерения и точность расчёта

Единицы измерения влияют не только на представление, но и на корректность логики. Разница в месяцах считается по календарным месяцам, а не по 30 дням: между 31 января и 1 февраля разница в месяцах равна 0, потому что полный месяц не прошёл. Если бизнес-логика ждёт «месяц = 30 дней», считайте в днях и делите на 30 уже в коде.

Для расчётов стажа, возраста или срока хранения часто нужна разница в годах. Здесь та же особенность: РАЗНОСТЬДАТ с типом ГОД вернёт количество полных лет. Если требуется «возраст на дату», функция даст корректный результат, но для проверки «18 лет исполнилось» надёжнее сравнить дату рождения со сдвинутой датой через ДОБАВИТЬКДАТЕ(&ПроверяемаяДата, ГОД, -18).

ЗадачаФункцияРекомендуемый тип
Сколько дней между событиямиРАЗНОСТЬДАТДЕНЬ
Просрочка в часахРАЗНОСТЬДАТЧАС или МИНУТА
Документы за последнюю неделюДОБАВИТЬКДАТЕДЕНЬ, сдвиг -7
Проверка «старше N лет»ДОБАВИТЬКДАТЕГОД, сдвиг -N
Стаж в полных месяцахРАЗНОСТЬДАТМЕСЯЦ

Сравнение вариантов

РАЗНОСТЬДАТ

Возвращает число — разницу между двумя датами. Подходит для показателей, сроков, просрочек. Не подходит, если нужна новая дата.

ДОБАВИТЬКДАТЕ с отрицательным сдвигом

Возвращает дату, сдвинутую назад. Подходит для фильтров по периоду и проверок наступления срока. Не даёт числовой разницы.

Вычисление на встроенном языке

Дату можно вычесть в коде (даты вычитаются с результатом в секундах) и передать готовый параметр в запрос. Проще для сложных расчётов, но требует вычисления до выполнения запроса.

Работа с пустыми датами и NULL

Пустая дата в 1С — это ДАТАВРЕМЯ(1,1,1), и она не равна NULL. Если в регистре или документе дата не заполнена, РАЗНОСТЬДАТ посчитает разницу от начала летоисчисления — получится огромное число, которое «утонет» в отчёте и исказит итоги. Поэтому незаполненные даты нужно обрабатывать явно.

Для полей, которые могут быть NULL (например, при левом соединении), используйте ЕСТЬNULL с подстановкой осмысленного значения. Для пустых дат — конструкцию ВЫБОР с проверкой на ДАТАВРЕМЯ(1,1,1).

ВЫБРАТЬ

Заказ.Ссылка КАК Заказ,

ВЫБОР

КОГДА Заказ.ДатаОтгрузки = ДАТАВРЕМЯ(1, 1, 1)

ТОГДА NULL

ИНАЧЕ РАЗНОСТЬДАТ(Заказ.Дата, Заказ.ДатаОтгрузки, ДЕНЬ)

КОНЕЦ КАК ДнейДоОтгрузки

ИЗ

Документ.ЗаказПокупателя КАК Заказ

Возврат NULL вместо числа позволяет потом отфильтровать такие строки условием ГДЕ ДнейДоОтгрузки ЕСТЬ НЕ NULL или корректно посчитать среднее — агрегатные функции игнорируют NULL, но учли бы фиктивные огромные значения.

Проверка результата и отладка

Перед тем как встраивать вычисление в рабочий отчёт, проверьте его на консоли запросов на нескольких заведомо известных примерах. Возьмите документ, где вы вручную можете посчитать разницу по календарю, и сверьте с результатом запроса. Проверяйте граничные случаи: одинаковые даты (должно быть 0), даты в обратном порядке (отрицательное число), разные годы.

Отдельно проверьте поведение с временем. Дата в 1С хранится с точностью до секунды, и РАЗНОСТЬДАТ(..., ДЕНЬ) для 10:00 понедельника и 09:00 вторника вернёт 0, хотя календарно это «следующий день». Если нужна разница по календарным датам без учёта времени, сначала отсечь время функцией НАЧАЛОПЕРИОДА(Дата, ДЕНЬ).

РАЗНОСТЬДАТ(

НАЧАЛОПЕРИОДА(Заказ.Дата, ДЕНЬ),

НАЧАЛОПЕРИОДА(Заказ.ДатаОтгрузки, ДЕНЬ),

ДЕНЬ) КАК КалендарныхДней

Контрольные точки

Как проверить результат

Одинаковые даты на входе
Норма

РАЗНОСТЬДАТ возвращает 0

Тревожный признак

Любое другое число или ошибка

Конечная дата раньше начальной
Норма

Отрицательное число периодов

Тревожный признак

Ошибка выполнения запроса

Разница в 23 часа при типе ДЕНЬ
Норма

Результат 0 (неполный день)

Тревожный признак

Результат 1 — значит, где-то время обрезано

Документ с пустой датой
Норма

NULL или строка исключена из расчёта

Тревожный признак

Огромное число дней в результате

Типичные ошибки при вычитании дат

Первая ошибка — попытка написать Дата2 - Дата1 в тексте запроса. Конструктор запросов такое выражение не примет, а при ручном наборе запрос упадёт с ошибкой при выполнении. Вторая — перепутанный порядок аргументов РАЗНОСТЬДАТ: первым идёт более ранняя дата, вторым — более поздняя. При обратном порядке знак результата инвертируется, и логика просрочек переворачивается.

Третья группа ошибок связана с производительностью. Функции над полем в условии ГДЕ (например, ГДЕ РАЗНОСТЬДАТ(Таб.Дата, &Дата, ДЕНЬ) > 30) мешают СУБД использовать индекс по полю даты, и запрос сканирует всю таблицу. На больших регистрах это критично. Правильнее инвертировать условие: вычислить граничную дату через ДОБАВИТЬКДАТЕ от параметра и сравнить с ней само поле — тогда индекс работает.

Типичные ошибки
01
Оператор «минус» между датами в запросе — синтаксическая ошибка, используйте РАЗНОСТЬДАТ
02
Перепутанный порядок аргументов РАЗНОСТЬДАТ — знак результата инвертируется, просрочки считаются наоборот
03
Игнорирование пустых дат ДАТАВРЕМЯ(1,1,1) — в отчёт попадают фиктивные миллионы дней
04
Функция над полем в условии ГДЕ — отключает индекс, запрос тормозит на больших таблицах
05
Ожидание дробного результата — РАЗНОСТЬДАТ возвращает только целые периоды, остаток отбрасывается

Альтернатива: вычесть дату на встроенном языке

Если вычисление нужно один раз до выполнения запроса, проще сделать его на встроенном языке и передать готовое значение параметром. Даты в коде вычитаются напрямую, результат — разница в секундах. Например, РазницаВДнях = (Дата2 - Дата1) / 86400;. Для сдвига даты назад используется ДобавитьМесяц() для месяцев и лет или арифметика с секундами для дней и часов.

Правило выбора простое: если значение одно на весь запрос (граница периода, дата отсечки) — считайте в коде и передавайте параметром, это и быстрее, и читабельнее. Если вычитание нужно построчно для каждой записи выборки — используйте функции языка запросов.

Маршрут решения

Как выбрать действие

Нужна разница между датами в каждой строке выборкиИспользуйте РАЗНОСТЬДАТ с подходящим типом периода
Нужно отобрать записи за последние N дней/месяцевИспользуйте ДОБАВИТЬКДАТЕ с отрицательным сдвигом в условии ГДЕ
Нужна одна граничная дата для всего запросаВычислите её на встроенном языке и передайте параметром
Нужна дробная точность (например, 1,5 дня)Считайте разницу в секундах на встроенном языке и делите на нужный коэффициент

Комбинировать подходы тоже допустимо: например, границу периода вычислить параметром, а внутри выборки показать РАЗНОСТЬДАТ от этой границы для каждого документа. Такой запрос останется быстрым и даст полную аналитику.

Итог

Что важно запомнить

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

Частые вопросы

Можно ли в запросе 1С написать Дата1 - Дата2?

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

Как вычесть из даты 7 дней в запросе?

Используйте ДОБАВИТЬКДАТЕ с отрицательным количеством: ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -7). Функция вернёт дату, сдвинутую на неделю назад. Аналогично работают типы ЧАС, МЕСЯЦ, КВАРТАЛ и ГОД.

Почему РАЗНОСТЬДАТ возвращает 0, хотя даты разные?

Функция считает только полные периоды. Если между датами прошло 20 часов, а тип указан ДЕНЬ, результат будет 0. Выберите более мелкую единицу (ЧАС, МИНУТА) или отсеките время через НАЧАЛОПЕРИОДА, если нужна разница по календарным датам.

Что делать, если одна из дат может быть не заполнена?

Оберните вычисление в ВЫБОР с проверкой на ДАТАВРЕМЯ(1,1,1) и возвращайте NULL для пустых дат. Для полей из соединений, где возможен NULL, применяйте ЕСТЬNULL. Иначе в отчёте появятся фиктивные огромные значения разницы.

Как посчитать возраст или стаж в полных годах?

Два варианта: РАЗНОСТЬДАТ(ДатаРождения, &ДатаОтсечки, ГОД) вернёт полные годы, либо сравните дату рождения с ДОБАВИТЬКДАТЕ(&ДатаОтсечки, ГОД, -N) для проверки «исполнилось N лет». Второй способ надёжнее для условий отбора.

Почему запрос с РАЗНОСТЬДАТ в условии ГДЕ работает медленно?

Функция над полем таблицы в условии не позволяет СУБД использовать индекс по этому полю, и выполняется полное сканирование. Перепишите условие так, чтобы функция применялась к параметру, а поле сравнивалось напрямую: например, Таб.Дата < ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -30).