Разработка и Конфигурация · 11 мин
Как от даты отнять год в 1С
Как отнять год от даты в 1С: функция ДобавитьМесяц, работа с високосными годами, запросы и типовые ошибки. Примеры кода для 1С 8.3.
Во встроенном языке 1С нет отдельной функции «отнять год», и простое вычитание числа из даты работает иначе, чем ожидают новички: при вычитании целого числа из значения типа Дата вычитаются секунды, а не дни или годы. Поэтому попытка написать Дата - 365 сдвинет дату всего на шесть минут, а не на год.
Правильный инструмент — функция ДобавитьМесяц(), которая умеет сдвигать дату на произвольное количество месяцев вперёд и назад. Чтобы отнять год, достаточно добавить минус двенадцать месяцев: НоваяДата = ДобавитьМесяц(ИсходнаяДата, -12);. Функция корректно обрабатывает переход через границу года, конец месяца и високосные даты.
Ниже разобраны все рабочие способы: встроенная функция, ручной пересчёт через составляющие даты, вычитание в запросах, обработка 29 февраля и типовые ошибки, которые приводят к неверным результатам в расчётах стажа, сроков годности и периодов отчётности.
Как устроены даты в 1С и почему нельзя вычитать напрямую
Значение типа Дата в 1С хранится с точностью до секунды и содержит одновременно календарную дату и время. Арифметические операции с датой определены только для сложения и вычитания чисел, и единицей такого числа является одна секунда. Выражение Дата + 86400 сдвинет дату на сутки, потому что в сутках 86400 секунд.
Год — нефиксированный интервал: в нём может быть 365 или 366 дней, а в разных месяцах разное число дней. Поэтому платформа не позволяет складывать или вычитать «годы» напрямую, и любой перенос такой логики из других языков программирования приводит к ошибкам. Например, вычитание 365 * 86400 секунд из 1 марта високосного года даст не 1 марта прошлого года, а 29 февраля или 2 марта в зависимости от соседних лет.
Для календарных сдвигов на месяцы и годы в платформе предусмотрена специальная функция, а для запросов — отдельная конструкция языка запросов. Именно их и следует использовать в прикладном коде.
Основной способ: функция ДобавитьМесяц
Функция ДобавитьМесяц(ИсходнаяДата, ЧислоМесяцев) возвращает дату, смещённую на указанное количество месяцев. Отрицательное значение сдвигает дату в прошлое. Для вычитания одного года передаётся значение -12, для двух лет — -24 и так далее.
ИсходнаяДата = Дата(2026, 3, 15);
ГодНазад = ДобавитьМесяц(ИсходнаяДата, -12);
// Результат: 15.03.2023
ТриГодаНазад = ДобавитьМесяц(ИсходнаяДата, -36);
// Результат: 15.03.2021
Функция учитывает реальный календарь: если исходная дата — последний день месяца, а в целевом месяце меньше дней, результатом станет последний день целевого месяца. Например, при вычитании года из 29 февраля високосного года получится 28 февраля предыдущего года, что обычно и требуется в бизнес-логике.
Время в составе даты сохраняется без изменений: если исходное значение было 15.03.2026 14:30:00, то после вычитания года получится 15.03.2023 14:30:00. Это важно при сравнении дат-времени в документах и регистрах.
- 1Определите переменную с исходной датой (тип Дата)
- 2Вызовите ДобавитьМесяц(ИсходнаяДата, -12)
- 3Сохраните результат в новую переменную — исходное значение не меняется
- 4Проверьте результат на граничных датах: 29 февраля, 31 января, начало года
Как отнять год в запросе 1С
В языке запросов функция ДобавитьМесяц недоступна — вместо неё используется функция ДОБАВИТЬКДАТЕ с указанием единицы измерения. Чтобы отнять год, передаётся отрицательное количество и тип периода ГОД:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&ДатаОтчета, ГОД, -1) КАК ГодНазад
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
Допустимые типы периода: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, а также варианты начала периода (НАЧАЛОПЕРИОДА). В отличие от встроенного языка, здесь можно вычитать год напрямую, не пересчитывая в месяцы.
Типичный сценарий — отбор документов за последний год от произвольной даты. В этом случае вычисленную дату удобно поместить во временную таблицу или использовать прямо в условии:
ВЫБРАТЬ
Продажи.Ссылка,
Продажи.Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
ГДЕ
Продажи.Дата >= ДОБАВИТЬКДАТЕ(&ТекущаяДата, ГОД, -1)
Учтите, что сравнение дат в запросе идёт с точностью до секунды. Если документ проведён ровно в полночь граничной даты, условие >= включит его, а > — нет. Для отчётов за период надёжнее использовать конструкцию МЕЖДУ ... И ... с явно заданными границами.
Альтернативные способы и когда они нужны
Если по какой-то причине ДобавитьМесяц не подходит — например, нужен иной алгоритм обработки 29 февраля — дату можно разобрать на составляющие и собрать заново. Функции Год(), Месяц() и День() извлекают части даты, а конструктор Дата(Год, Месяц, День) собирает новое значение:
Функция ОтнятьГод(ИсходнаяДата)
НовыйГод = Год(ИсходнаяДата) - 1;
НовыйМесяц = Месяц(ИсходнаяДата);
НовыйДень = День(ИсходнаяДата);
// Защита от 29 февраля в невисокосном году
Если НовыйМесяц = 2 И НовыйДень = 29 Тогда
НовыйДень = 28;
КонецЕсли;
Возврат Дата(НовыйГод, НовыйМесяц, НовыйДень);
КонецФункции
Такой ручной способ теряет время из исходной даты — конструктор без параметров времени вернёт начало дня. Если время значимо, его нужно передавать дополнительными параметрами: Дата(Год, Месяц, День, Час, Минута, Секунда).
В управляемых формах для полей ввода даты есть ещё один вариант — свойство или кнопки выбора периода, но для программных расчётов это неприменимо. Третий вариант, вычитание секунд (Дата - 365*86400), использовать для отнимания года не следует: он игнорирует високосные годы и даёт сдвиг на день-два в длинных интервалах.
Сравнение вариантов
Стандартный способ в коде. Корректно обрабатывает конец месяца и високосность. Подходит почти всегда.
Единственный способ сдвинуть дату прямо в запросе. Позволяет вычитать ГОД напрямую.
Нужен при нестандартной логике для 29 февраля. Требует отдельной обработки времени и граничных дат.
Не рекомендуется: не учитывает високосные годы, результат смещается на дни.
Обработка високосных годов и конца месяца
Главный граничный случай — 29 февраля. При вычитании года из 29.02.2026 функция ДобавитьМесяц вернёт 28.02.2023, поскольку 2023 год невисокосный. Это соответствует общему правилу платформы: если целевой день не существует, берётся последний день целевого месяца.
Аналогично ведёт себя и 31-е число: вычитание года из 31.03.2026 даст 31.03.2023 без сюрпризов, но вычитание месяца из 31 марта даст 28 или 29 февраля. Если ваша логика требует, чтобы из 29 февраля получалось 1 марта, а не 28 февраля, ДобавитьМесяц не подойдёт — придётся реализовать собственную функцию с явным правилом.
Ещё одна тонкость — пустая дата Дата(1,1,1). Вычитание года из неё даст отрицательный год и ошибку выполнения. Перед сдвигом даты, которая может быть не заполнена (например, реквизит «Дата окончания действия»), добавляйте проверку:
Если ЗначениеЗаполнено(ДатаОкончания) Тогда
ДатаНачала = ДобавитьМесяц(ДатаОкончания, -12);
КонецЕсли;
Как проверить результат
Результат 15.06.2023, время сохранено
Сдвиг на день-два или потеря времени
28 февраля предыдущего года
Ошибка выполнения или 1 марта без явного требования
Код пропускает расчёт через ЗначениеЗаполнено
Ошибка «некорректное значение даты»
Типичные ошибки при вычитании года
Самая частая ошибка — вычитание числа из даты в расчёте на дни или годы. Начинающие разработчики пишут Дата - 365, ожидая сдвига на год, а получают сдвиг на 365 секунд. В отладчике это почти незаметно, но расчёты сроков, стажа и периодов хранения «плывут».
Вторая ошибка — использование ДобавитьМесяц в запросе. В языке запросов этой функции нет, и консоль запросов выдаст ошибку. В запросах работает только ДОБАВИТЬКДАТЕ, а логику со встроенным языком нужно выносить в параметры запроса, вычисляя дату до его выполнения.
Третья группа ошибок связана со временем. Если исходная дата содержит время 23:59:59, а дальше результат сравнивается с границей периода, возможны «выпадающие» документы. Для границ периодов применяйте НачалоДня() и КонецДня() после сдвига даты.
Практические сценарии применения
Отбор данных за последний год
Классическая задача — показать пользователю документы или обороты за последние 12 месяцев от текущей даты. Вычислите границу в коде и передайте её параметром: Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ДобавитьМесяц(ТекущаяДатаСеанса(), -12)));. Использование ТекущаяДатаСеанса() вместо ТекущаяДата() важно на сервере — оно возвращает дату сеанса с учётом часового пояса.
Расчёт сроков и напоминаний
При расчёте даты окончания договора, гарантии или поверки оборудования логика обратная: к дате начала прибавляется срок. Но для проверки «истекает ли договор в ближайший месяц» удобно от текущей даты отнять год и сравнить с датой начала: если договор годовой и его начало раньше этой границы — срок подходит к концу.
Сравнение периодов в отчётах
В отчётах «продажи к прошлому году» каждую дату факта сдвигают на -12 месяцев, чтобы сопоставить с аналогичным периодом. В СКД это делается вычисляемым полем с функцией ДобавитьМесяц — она доступна в выражениях компоновки данных наряду со встроенным языком.
Если вычисленная дата-год-назад используется в нескольких запросах и отчётах, выносите расчёт в одну общую функцию общего модуля. Так правило обработки 29 февраля и границ периода будет единым во всей конфигурации, и его не придётся править в десяти местах.
FAQ
Что вернёт ДобавитьМесяц(29.02.2026, -12)?
Функция вернёт 28.02.2023, потому что 2023 год невисокосный и 29 февраля в нём не существует. Платформа в таких случаях берёт последний день целевого месяца. Если нужно другое правило, например 1 марта, реализуйте собственную функцию с явной обработкой этой ситуации.
Можно ли отнять год простым вычитанием, как в Excel?
Нет. В 1С вычитание числа из даты вычитает секунды. Выражение Дата - 365 сдвинет значение всего на 365 секунд. Даже пересчёт в секунды (365 * 86400) неточен из-за високосных лет. Используйте ДобавитьМесяц(Дата, -12).
Как отнять год от даты в запросе?
В языке запросов используется функция ДОБАВИТЬКДАТЕ с типом периода ГОД: ДОБАВИТЬКДАТЕ(&Дата, ГОД, -1). Функция ДобавитьМесяц в запросах недоступна — она работает только во встроенном языке и выражениях СКД.
Сохраняется ли время при вычитании года через ДобавитьМесяц?
Да, время сохраняется без изменений: из 15.03.2026 14:30:00 получится 15.03.2023 14:30:00. Если результат используется как граница периода, дополнительно примените НачалоДня() или КонецДня(), чтобы не потерять документы на границах.
Что будет, если отнять год от пустой даты?
Пустая дата в 1С — это 01.01.0001. Вычитание из неё года приведёт к отрицательному значению года и ошибке выполнения. Перед сдвигом всегда проверяйте реквизиты функцией ЗначениеЗаполнено().
Как отнять несколько лет, например пять?
Умножьте количество лет на 12 и передайте в ДобавитьМесяц со знаком минус: ДобавитьМесяц(Дата, -5 * 12). В запросе аналогично: ДОБАВИТЬКДАТЕ(&Дата, ГОД, -5). Дополнительных ограничений на величину сдвига нет.
Что важно запомнить
Год от даты в 1С отнимают функцией ДобавитьМесяц(Дата, -12), а в запросах — ДОБАВИТЬКДАТЕ(Дата, ГОД, -1). Прямое вычитание чисел не работает, так как единица арифметики дат — секунда. Проверяйте пустые даты и поведение на 29 февраля, а для границ периодов дополнительно применяйте НачалоДня и КонецДня.