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

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

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

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

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

Важно понимать ограничение: функция работает только с типом «Дата» и возвращает значение того же типа. Если вычесть день из даты-времени, время суток сохранится. Например, из 15.03 14:30 при вычитании одного дня получится 14.03 14:30, а не начало дня. Для получения начала или конца дня дополнительно применяют НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА.

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

Синтаксис функции ДОБАВИТЬКДАТЕ

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

Количество — целое число. Чтобы вычесть период, передаётся отрицательное число. Таким образом, «минус один день» записывается как ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1), «минус неделя» — ДОБАВИТЬКДАТЕ(&Дата, НЕДЕЛЯ, -1), «минус три месяца» — ДОБАВИТЬКДАТЕ(&Дата, МЕСЯЦ, -3).

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

Базовый пример: вычесть один день

Самый частый сценарий — отобрать данные за день, предшествующий заданной дате. Запрос передаёт параметр с датой и вычисляет из неё вчерашнее число:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1) КАК ВчерашняяДата";

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

Результат = Запрос.Выполнить().Выгрузить();

Если параметр содержит дату со временем (например, текущую дату сеанса), результат сохранит то же время. Когда нужна именно календарная дата без времени, оборачивайте выражение в НАЧАЛОПЕРИОДА:

НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1), ДЕНЬ)

Такая конструкция вернёт 00:00:00 вчерашнего дня. Аналогично КОНЕЦПЕРИОДА(..., ДЕНЬ) даст 23:59:59 — это удобно для условий «меньше или равно» при отборе по периоду.

Порядок действий
  1. 1
    Определите, какая дата является исходной: параметр запроса, поле документа или функция вроде &ТекущаяДата
  2. 2
    Напишите выражение ДОБАВИТЬКДАТЕ(ИсходнаяДата, ДЕНЬ, -1) в списке выбираемых полей или в условии ГДЕ
  3. 3
    При необходимости отсечь время оберните результат в НАЧАЛОПЕРИОДА или КОНЕЦПЕРИОДА
  4. 4
    Выполните запрос в консоли запросов и сверьте полученную дату с ожидаемой

Вычитание дня в условии отбора

На практике вычитание дня чаще нужно не в результате, а в условии ГДЕ. Пример: выбрать все документы реализации за вчерашний день относительно текущей даты:

ВЫБРАТЬ

РеализацияТоваровУслуг.Ссылка,

РеализацияТоваровУслуг.Дата

ИЗ

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

ГДЕ

РеализацияТоваровУслуг.Дата МЕЖДУ НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -1), ДЕНЬ)

И КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -1), ДЕНЬ)

И РеализацияТоваровУслуг.Проведен

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

Ещё один рабочий приём — сравнение через отсечение периода у поля: НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1), ДЕНЬ). Учтите, однако, что применение функции к полю таблицы мешает использованию индекса по дате, и на больших таблицах запрос может выполняться заметно медленнее.

Вычитание месяцев и лет: особенности границ

Вычитание дня работает предсказуемо всегда, а вот вычитание месяца или года имеет нюанс на границах месяцев. Если из 31 марта вычесть один месяц, результатом будет последний день февраля — 28 или 29 число в зависимости от високосности года. Функция не выдаёт ошибку и не «проваливается» в март, а подгоняет дату под последний существующий день целевого месяца.

Аналогично вычитание года из 29 февраля високосного года даст 28 февраля невисокосного. Это нужно учитывать в расчётах сроков: если бизнес-логика требует иного поведения (например, строго то же число либо ошибка), придётся реализовывать проверку отдельно в коде.

Для перехода через границу месяца при вычитании дней никаких специальных действий не требуется: из 1 марта при вычитании одного дня корректно получится последний день февраля, из 1 января — 31 декабря предыдущего года. Календарная арифметика выполняется платформой автоматически.

Способы получить «вчера»

ДОБАВИТЬКДАТЕ в запросе

Подходит, когда расчёт нужен прямо в тексте запроса или в СКД. Ограничение — громоздкий текст при повторном использовании результата.

Расчёт в коде и передача параметром

Подходит для программных запросов: чище текст, лучше производительность. Требует вычисления до выполнения запроса.

Стандартные периоды в СКД

Подходит для отчётов на СКД: пользователь сам выбирает период «Вчера». Не применимо в произвольных запросах вне СКД.

Контрольная проверка: РАЗНОСТЬДАТ

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

Пример проверочного запроса:

ВЫБРАТЬ

&Дата КАК ИсходнаяДата,

ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1) КАК МинусДень,

РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1), &Дата, ДЕНЬ) КАК РазницаДней

Учтите особенность РАЗНОСТЬДАТ: функция считает полные периоды. Между 15.03 23:00 и 16.03 01:00 разница в днях будет ноль, хотя календарные даты различаются. Если нужна разница в календарных днях, сначала приведите обе даты к началу дня через НАЧАЛОПЕРИОДА.

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

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

Значение вычисленной даты
Норма

Дата ровно на один календарный день меньше исходной, время совпадает с ожиданием

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

Смещение на два дня или сброс времени, которого вы не планировали

Переход через начало месяца
Норма

Из 1-го числа получается последний день предыдущего месяца

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

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

Отбор документов за вчера
Норма

В выборку попали документы только вчерашнего дня

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

Попали документы сегодняшнего дня — проверьте границы периода

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

Первая и самая частая ошибка — попытка написать &Дата - 1. Язык запросов 1С не поддерживает арифметику дат оператором «минус», и запрос завершится с ошибкой синтаксиса или типов. Используйте только ДОБАВИТЬКДАТЕ с отрицательным параметром.

Вторая ошибка — указание типа периода в кавычках: ДОБАВИТЬКДАТЕ(&Дата, "ДЕНЬ", -1). Тип периода — это ключевое слово языка запросов, а не строка. Кавычки приведут к ошибке «Неверные параметры функции».

Третья ошибка — забыть про состав даты. Если поле или параметр имеет составной тип (например, дата или строка), функция может вернуть NULL для строковых значений. Проверяйте типы данных в консоли запросов, а при необходимости приводите значение через ВЫРАЗИТЬ(... КАК ДАТА).

Четвёртая ошибка касается пустых дат. Вычитание дня из пустой даты (01.01.0001) даст значение до начала летоисчисления, и сравнения с таким результатом будут вести себя неочевидно. Если поле может быть незаполненным, добавьте условие проверки: ВЫБОР КОГДА Поле = ДАТАВРЕМЯ(1,1,1) ТОГДА NULL ИНАЧЕ ДОБАВИТЬКДАТЕ(...) КОНЕЦ.

Типичные ошибки
01
Написать &Дата - 1 вместо ДОБАВИТЬКДАТЕ — запрос не выполнится, оператор минус для дат не поддерживается
02
Взять тип периода в кавычки ("ДЕНЬ") — ошибка параметров функции, тип периода пишется без кавычек
03
Игнорировать время в дате — отбор «за вчера» захватывает или теряет часть документов из-за границ 00:00:00 и 23:59:59
04
Вычитать день из пустой даты без проверки — получается некорректное значение, ломающее сравнения

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

Во встроенном языке 1С дату можно вычитать напрямую: Вчера = ТекущаяДатаСеанса() - 86400; — здесь единица равна одной секунде, поэтому день — это 86400 секунд. Этот способ проще, но работает только в коде, не в тексте запроса и не в выражениях СКД.

Практический вывод простой: если запрос формируется программно, вычислите нужные даты в коде и передайте их параметрами — так текст запроса останется читаемым, а СУБД сможет использовать индексы. Функцию ДОБАВИТЬКДАТЕ оставьте для случаев, когда расчёт должен выполняться внутри запроса: в СКД, в динамических списках, в произвольных выражениях отчётов.

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

Итог

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

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

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

Можно ли вычесть день оператором «минус» в запросе 1С?

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

Как вычесть день и получить начало вчерашнего дня?

Объедините две функции: НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -1), ДЕНЬ). Результат — вчерашняя дата с временем 00:00:00. Для конца дня используйте КОНЕЦПЕРИОДА с теми же аргументами — получите 23:59:59.

Что произойдёт при вычитании дня из первого числа месяца?

Функция корректно перейдёт на последний день предыдущего месяца: из 1 марта получится 28 или 29 февраля в зависимости от високосности года, из 1 января — 31 декабря предыдущего года. Никаких специальных проверок для границ месяцев и лет не требуется.

Как вычесть переменное количество дней, хранящееся в реквизите?

Передайте поле третьим параметром со знаком минус: ДОБАВИТЬКДАТЕ(Документ.Дата, ДЕНЬ, -Контрагент.СрокДней). Если реквизит может быть незаполнен, оберните его в ЕСТЬNULL(Контрагент.СрокДней, 0), иначе результат для таких строк будет NULL.

Почему запрос с ДОБАВИТЬКДАТЕ в условии работает медленно?

Если функция применяется к полю таблицы (например, к дате документа), СУБД не может использовать индекс по этому полю и вынуждена сканировать таблицу целиком. Решение — вычислить граничные даты заранее и сравнивать поле с параметрами: Документ.Дата МЕЖДУ &Начало И &Конец.

Как проверить, что дата вычислена правильно?

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