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

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

Как в 1С вычесть из даты: рабочие способы и примеры кода

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

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

Главная тонкость в том, что дата в 1С хранится с точностью до секунды, а месяцы и годы имеют переменную длину. Вычесть «30 дней» и вычесть «1 месяц» — это разные операции, которые могут дать разные результаты. Ниже разобраны все рабочие способы с примерами кода, особенностями запросов и типичными ошибками.

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

Значение типа «Дата» во встроенном языке 1С хранится как момент времени с точностью до секунды. При арифметических операциях с датами платформа оперирует именно секундами: выражение Дата1 - Дата2 возвращает число — разницу в секундах, а Дата - Число вычитает из даты указанное количество секунд.

Из этого следует важное правило: нельзя написать Дата - 7, ожидая получить дату неделей ранее. Выражение сработает, но вычтет всего 7 секунд. Для вычитания дней число нужно умножить на количество секунд в сутках — 86400.

Месяцы и годы арифметикой вычитать некорректно: в месяцах разное количество дней (28–31), а в годах — 365 или 366. Для них существует отдельная функция, которая корректно обрабатывает календарные границы.

Вычитание дней, часов и минут арифметикой

Самый простой способ вычесть из даты фиксированный интервал — умножить нужное количество единиц времени на секунды. Формулы для основных интервалов:

// Вычесть 1 день

НоваяДата = ИсходнаяДата - 86400;

// Вычесть 7 дней (неделю)

НоваяДата = ИсходнаяДата - 7 * 86400;

// Вычесть 3 часа

НоваяДата = ИсходнаяДата - 3 * 3600;

// Вычесть 45 минут

НоваяДата = ИсходнаяДата - 45 * 60;

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

Обратите внимание на время внутри даты. Если исходная дата содержит время 15:30:00, после вычитания суток время сохранится. Чтобы получить начало дня результата, примените функцию НачалоДня(НоваяДата).

bsl
// Вычесть 10 календарных дней и получить начало дня
ИсходнаяДата = ТекущаяДатаСеанса();
НоваяДата = НачалоДня(ИсходнаяДата - 10 * 86400);

Вычитание месяцев и лет через ДобавитьМесяц

Для календарных месяцев и лет используется встроенная функция ДобавитьМесяц(Дата, КоличествоМесяцев). Чтобы вычесть, передайте отрицательное число:

// Вычесть 1 месяц

НоваяДата = ДобавитьМесяц(ИсходнаяДата, -1);

// Вычесть 6 месяцев

НоваяДата = ДобавитьМесяц(ИсходнаяДата, -6);

// Вычесть 1 год (12 месяцев)

НоваяДата = ДобавитьМесяц(ИсходнаяДата, -12);

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

Проверить результат просто: выведите обе даты через Сообщить() или в отладчике и убедитесь, что число, месяц и год соответствуют ожиданиям, особенно на граничных датах — 31 января, 29 февраля, 31 декабря.

Вычитание дат в запросах

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

ВЫБРАТЬ

ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -7) КАК ДатаНеделюНазад,

ДОБАВИТЬКДАТЕ(&Дата, МЕСЯЦ, -3) КАК ТриМесяцаНазад

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

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

Способы вычитания из даты

Арифметика с секундами

Подходит для дней, часов, минут. Точный сдвиг на фиксированный интервал. Не подходит для месяцев и лет из-за их переменной длины.

ДобавитьМесяц с минусом

Подходит для месяцев, кварталов, лет. Корректно обрабатывает границы, но конец месяца может «съехать» на последний день короткого месяца.

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

Единственный способ сдвинуть дату прямо в запросе. Работает со всеми единицами, но параметр количества должен быть известен на момент выполнения.

Разница между датами: как получить количество дней

Обратная задача — узнать, сколько дней между двумя датами. В модуле это делается делением разности на секунды в сутках:

КоличествоДней = (ДатаОкончания - ДатаНачала) / 86400;

Если даты содержат время, результат будет дробным. Для целого числа дней применяйте Цел() или предварительно обрежьте время через НачалоДня() у обеих дат. Какой вариант выбрать, зависит от прикладной логики: при расчёте просрочки обычно берут целую часть, а при тарификации по часам дробное значение может быть осмысленным.

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

Типичные ошибки и как их избежать

Первая и самая массовая ошибка — забыть умножить дни на 86400. Код Дата - 30 отрабатывает без ошибки, но сдвигает дату на полминуты, и заметить это можно только при внимательной проверке результата. Всегда выносите множитель в именованную конструкцию или комментарий.

Вторая ошибка — попытка вычесть месяц как 30 дней. Для января это даст сдвиг на 30 дней вместо календарного месяца, и в части сценариев результат окажется неверным на 1–3 дня. Месяцы вычитайте только через ДобавитьМесяц.

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

Типичные ошибки
01
Написать Дата - 7 вместо Дата - 7 * 86400 — дата сдвинется на 7 секунд, а не на 7 дней
02
Вычитать месяц как 30 дней — результат расходится с календарным месяцем на 1–3 дня
03
Не проверить дату на заполненность — вычитание из пустой даты 01.01.0001 портит расчёты и отчёты
04
Игнорировать время внутри даты — результат содержит неожиданное время, сравнения дат в запросах срабатывают неверно
05
Ожидать от ДобавитьМесяц сохранение числа 31 — для коротких месяцев платформа вернёт последний день месяца

Проверка результата и безопасная отладка

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

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

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

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

Сдвиг на дни
Норма

Дата изменилась ровно на заданное число суток, время сохранилось или обрезано осознанно

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

Дата сдвинулась на секунды — забыто умножение на 86400

Сдвиг на месяцы
Норма

Месяц и год уменьшились корректно, конец месяца обработан

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

Результат отличается на 1–3 дня от ожидаемого — месяц вычитали как 30 дней

Пустые значения
Норма

Перед расчётом есть проверка ЗначениеЗаполнено

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

В отчётах появляются даты 01.01.0001 или огромные интервалы

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

Как вычесть из текущей даты 30 дней?

Получите текущую дату через ТекущаяДатаСеанса() и вычтите секунды: ТекущаяДатаСеанса() - 30 * 86400. Если нужна дата без времени, оберните результат в НачалоДня().

Как вычесть один год из даты?

Используйте ДобавитьМесяц(ИсходнаяДата, -12). Так корректно обработается 29 февраля: для високосного исходного года результатом станет 28 февраля предыдущего года.

Можно ли вычесть дату из даты и получить дни?

Да. Выражение Дата2 - Дата1 возвращает разницу в секундах. Разделите её на 86400, чтобы получить количество дней; при необходимости округлите через Цел().

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

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

Почему ДобавитьМесяц(31.03, -1) даёт конец февраля?

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

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

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

Итог

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

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