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

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

Как от даты отнять день в 1С

Как от даты отнять день в 1С: функция ДобавитьКДате, арифметика секунд, работа в запросах и типовые ошибки. Примеры кода для встроенного языка 1С.

Задача отнять один день от даты возникает в 1С постоянно: при расчёте периодов отчётов, определении даты отгрузки, проверке сроков оплаты и формировании выборок «за вчера». Во встроенном языке 1С нет функции с названием «ОтнятьДень», поэтому новички часто ищут несуществующий метод. На практике задача решается двумя основными способами: универсальной функцией ДобавитьКДате и простой арифметикой, где дата хранится как число секунд.

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

Материал применим к платформе 1С:Предприятие 8.x — синтаксис работы с датами един для «Управления торговлей», «Бухгалтерии», «Зарплаты и управления персоналом» и собственных конфигураций.

Как устроен тип «Дата» в 1С и почему это важно

В платформе 1С дата внутренне представлена как количество секунд, прошедших от начала отсчёта. Именно поэтому к дате можно прибавлять и отнимать целые числа — каждая единица соответствует одной секунде. Один день — это 86 400 секунд (60 × 60 × 24).

Из этого следуют два практических вывода. Первый: выражение Дата - 86400 технически отнимает ровно сутки. Второй: если в дате есть время (например, 14:30:00), то вычитание суток сохранит это время — вы получите вчерашний день в 14:30:00, а не начало вчерашнего дня. Это частая причина ошибок при сравнении дат в условиях и отборах.

Для отсечения времени используйте функцию НачалоДня() или КонецДня(). Например, НачалоДня(Дата - 86400) вернёт 00:00:00 вчерашнего дня, что удобно для сравнения с датами документов без учёта времени.

Способ 1: функция ДобавитьКДате с отрицательным значением

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

ИсходнаяДата = ТекущаяДатаСеанса();

ВчерашняяДата = ДобавитьКДате(ИсходнаяДата, "ДЕНЬ", -1);

Сообщить(ВчерашняяДата);

Главное преимущество функции — корректная работа с календарной арифметикой. Если отнять день от 1 марта невисокосного года, результатом будет 28 февраля; от 1 января — 31 декабря предыдущего года. При вычитании месяцев функция также корректно обрабатывает разное количество дней в месяцах, чего простая арифметика секунд не умеет.

Обратите внимание на регистр и точное написание типа увеличения: строка должна быть одним из допустимых значений. Опечатка вроде «ДНИ» или «День» с лишними символами приведёт к ошибке выполнения.

bsl
ВчерашняяДата = ДобавитьКДате(ТекущаяДатаСеанса(), "ДЕНЬ", -1);
НачалоВчера = НачалоДня(ВчерашняяДата);
КонецВчера = КонецДня(ВчерашняяДата);

Способ 2: вычитание секунд напрямую

Второй вариант — использовать тот факт, что дата является числом секунд. Выражение Дата - 86400 отнимает ровно одни сутки. Этот приём короче и часто встречается в типовых конфигурациях.

ДатаДокумента = Документ.Дата;

ПредыдущиеСутки = ДатаДокумента - 86400; // минус один день

Для читаемости кода лучше записывать выражение как произведение: Дата - 24 60 60. Тогда человеку, читающему код, сразу понятно, что вычитаются именно сутки, а не «магическое число».

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

Сравнение способов

ДобавитьКДате(Дата, "ДЕНЬ", -1)

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

Дата - 86400

Коротко и быстро для суточных интервалов. Не подходит для месяцев и лет, менее очевидно при чтении кода.

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

Единственный вариант внутри текста запроса, когда нужно отобрать данные за смещённый период на стороне СУБД.

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

В языке запросов арифметика секунд недоступна — там работает только функция ДОБАВИТЬКДАТЕ с аналогичным синтаксисом. Типичная задача — отобрать документы за вчерашний день или сравнить дату документа со смещённой датой.

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

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

"ВЫБРАТЬ

| Реализация.Ссылка

|ИЗ

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

|ГДЕ

| Реализация.Дата >= &НачалоВчера

| И Реализация.Дата <= &КонецВчера";

Запрос.УстановитьПараметр("НачалоВчера", НачалоДня(ДобавитьКДате(ТекущаяДатаСеанса(), "ДЕНЬ", -1)));

Запрос.УстановитьПараметр("КонецВчера", КонецДня(ДобавитьКДате(ТекущаяДатаСеанса(), "ДЕНЬ", -1)));

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

Практическая рекомендация: если границы периода можно вычислить на встроенном языке, делайте это до запроса и передавайте готовые значения параметрами. Так запрос проще читать, а индексы по полю даты используются эффективнее.

Работа с пустой датой и граничными случаями

Пустая дата в 1С — это '00010101000000' (1 января года 1). Если отнять день от пустой даты, результат станет отрицательным значением даты, что почти наверняка сломает дальнейшую логику или вызовет ошибку при записи в базу. Поэтому перед вычитанием проверяйте заполненность.

Если ЗначениеЗаполнено(ДатаОтгрузки) Тогда

ДатаПодготовки = ДобавитьКДате(ДатаОтгрузки, "ДЕНЬ", -1);

Иначе

ДатаПодготовки = Неопределено;

КонецЕсли;

Второй граничный случай — дата документа с границей последовательности или с временем 23:59:59. Если бизнес-логика опирается на «предыдущий календарный день», убедитесь, что вы отсекаете время через НачалоДня(), иначе сравнение дат в условиях даст неожиданный результат.

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

Типичные ошибки
01
Вычитание дня без отсечения времени: сравнение Дата - 86400 с датой документа даёт сдвиг на часы и минуты — отборы «за вчера» не срабатывают.
02
Использование арифметики секунд для месяцев: Дата - 30*86400 не равно «минус месяц», на границах месяцев получаются неверные даты.
03
Вычитание из пустой даты без проверки: результат уходит в область недопустимых значений и ломает запись данных.
04
Кавычки вокруг ДЕНЬ в тексте запроса: в языке запросов это ключевое слово, строковый литерал вызовет ошибку.
05
ТекущаяДата() в серверном коде: при разных часовых поясах пользователей «вчерашний день» вычисляется по-разному.

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

Простейший способ убедиться, что вычитание сработало, — вывести обе даты через Сообщить() или посмотреть значения в отладчике. Для наглядности форматируйте дату функцией Формат() с указанием даты и времени.

Сообщить("Было: " + Формат(ИсходнаяДата, "ДЛФ=DT"));

Сообщить("Стало: " + Формат(ВчерашняяДата, "ДЛФ=DT"));

Особое внимание уделите тестированию на границах: 1 января, 1 марта високосного и невисокосного года, 1 число любого месяца. Именно здесь проявляются ошибки календарной арифметики. Если логика критична (например, расчёт сроков оплаты или блокировок), покройте эти случаи явными проверками в коде или тестовыми данными.

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

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

Дата после вычитания дня от 1-го числа месяца
Норма

Получилось последнее число предыдущего месяца

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

Дата осталась в том же месяце или стала пустой

Время в результирующей дате
Норма

Время сохранилось или обрезано через НачалоДня/КонецДня — как задумано

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

Время «прыгает» непредсказуемо, сравнения не срабатывают

Поведение на пустой дате
Норма

Есть проверка ЗначениеЗаполнено, пустая дата обрабатывается отдельно

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

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

Источник текущей даты в серверном коде
Норма

Используется ТекущаяДатаСеанса()

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

Используется ТекущаяДата() с клиентского компьютера

Практические сценарии применения

Подбор документов за вчерашний день

Классическая задача для регламентных заданий и отчётов: обработать всё, что было проведено вчера. Вычислите границы через НачалоДня и КонецДня от смещённой даты и передайте их параметрами запроса — пример приведён в разделе про запросы выше.

Расчёт срока со сдвигом назад

Если по бизнес-правилу подготовка заказа должна начаться за день до отгрузки, дата подготовки вычисляется как ДобавитьКДате(ДатаОтгрузки, "ДЕНЬ", -1). Для сроков в рабочих днях простого вычитания недостаточно — потребуется производственный календарь из регистра сведений, где учитываются выходные и праздники.

Установка периода отчёта по умолчанию

При открытии отчёта удобно подставлять период «вчера» или «последние 7 дней». Для этого в обработчике при создании формы установите дату окончания как конец вчерашнего дня, а дату начала — через ДобавитьКДате с нужным отрицательным сдвигом.

Порядок действий
  1. 1
    Определите источник даты: реквизит объекта, ТекущаяДатаСеанса() или параметр.
  2. 2
    Проверьте заполненность даты через ЗначениеЗаполнено().
  3. 3
    Вычтите день: ДобавитьКДате(Дата, "ДЕНЬ", -1) или Дата - 86400.
  4. 4
    При необходимости отсеките время через НачалоДня() или КонецДня().
  5. 5
    Проверьте результат на граничных датах: 1 января, 1 марта, 1-е число месяца.

Частые вопросы по вычитанию дней из даты в 1С

Как отнять не один, а несколько дней от даты?

Измените третий параметр функции: ДобавитьКДате(Дата, "ДЕНЬ", -7) отнимет неделю. При арифметике секунд умножьте 86400 на нужное количество дней: Дата - 7 * 86400.

Чем отличается ТекущаяДата() от ТекущаяДатаСеанса()?

ТекущаяДата() возвращает время компьютера, где выполняется код — на клиенте это часы пользователя. ТекущаяДатаСеанса() возвращает серверное время с учётом часового пояса сеанса. В серверных процедурах и регламентных заданиях корректно использовать ТекущаяДатаСеанса().

Как отнять день в запросе, если дата хранится в таблице?

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

Что произойдёт, если отнять день от 1 января?

Функция ДобавитьКДате корректно вернёт 31 декабря предыдущего года. Арифметика секунд (Дата - 86400) также даст корректный результат, так как сутки — фиксированный интервал. Проблемы возникают только при вычитании месяцев и лет через секунды.

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

Комбинируйте функции: НачалоДня(ДобавитьКДате(ТекущаяДатаСеанса(), "ДЕНЬ", -1)). Результат — 00:00:00 вчерашней даты. Для конца дня аналогично используйте КонецДня() — получится 23:59:59.

Можно ли отнять рабочий день, а не календарный?

Встроенной функции для этого нет. Нужно использовать производственный календарь из регистра сведений (в типовых конфигурациях — «РегламентированныйПроизводственныйКалендарь» или аналог) и в цикле сдвигать дату назад, пропуская выходные и праздничные дни.

Итог

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

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