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

В этой статье мы разберем все возможные способы вычитания месяцев в 1С — от стандартных функций платформы до авторских алгоритмов на встроенном языке. Вы узнаете, какой метод выбрать для конкретной задачи, как избежать ошибок при работе с крайними датами (например, 31 января минус 1 месяц), и получите готовые коды для копирования. Материал будет полезен как начинающим пользователям, так и опытным программистам , которые хотят оптимизировать свои решения.

1. Стандартные функции 1С для работы с датами

Платформа 1С:Предприятие 8 предоставляет несколько встроенных инструментов для манипуляции датами. Самые очевидные — функции ДобавитьМесяц() и оператор - — на самом деле работают по-разному и дают разные результаты в пограничных случаях.

Функция ДобавитьМесяц(Дата, Количество) — самый безопасный способ, так как она автоматически корректирует день месяца, если он отсутствует в результирующем месяце. Например, ДобавитьМесяц('20260131', -1) вернет 31.12.2023, а не ошибочное 31.12.2023 (которое технически не существует). Это поведение заложено в логику платформы и соответствует бизнес-правилам: если дата выпадает на несуществующий день, берется последний день предыдущего месяца.

  • Плюсы: Простота, надежность, учет календарных особенностей
  • Минусы: Не всегда интуитивно понятный результат для пользователей (например, 30 февраля)
  • 🔧 Пример:
    Результат = ДобавитьМесяц(ТекущаяДата(), -3); // Вычитаем 3 месяца

Альтернативный подход — использование оператора - с интервалом. Например, ТекущаяДата() - 30 вычтет 30 дней, но это не то же самое, что вычитание месяца! Такой метод приведет к ошибкам в отчетах, где важна точность по календарным месяцам, а не дням. Например, '20260331' - 30 даст 01.03.2026, тогда как ДобавитьМесяц('20260331', -1) вернет 29.02.2026.

📊 Какой метод вычитания месяцев используете чаще?
Встроенную функцию ДобавитьМесяц()
Оператор минус с днями
Собственный алгоритм на 1С
Не работал с датами

2. Вычитание месяцев с учетом рабочих дней

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

Алгоритм может выглядеть так:

  1. Вычесть нужное количество месяцев функцией ДобавитьМесяц().
  2. Проверить, не попала ли результирующая дата на выходной (суббота/воскресенье).
  3. Если попала — сдвинуть на предыдущий рабочий день.
  4. Дополнительно учесть праздничные дни (требуется справочник праздников).
Функция ВычестьМесяцыСУчетомРабочихДней(НачальнаяДата, КоличествоМесяцев)

Результат = ДобавитьМесяц(НачальнаяДата, -КоличествоМесяцев);

Пока ДеньНедели(Результат) = 6 Или ДеньНедели(Результат) = 7 Или ЭтоПраздник(Результат) Цикл

Результат = Результат - 86400; // Вычитаем 1 день в секундах

КонецЦикла;

Возврат Результат;

КонецФункции

Для реализации проверки праздников потребуется:

  • 📅 Справочник с датами официальных праздников (можно загружать из внешних источников)
  • 🔄 Функция ЭтоПраздник(Дата), которая проверяет наличие даты в справочнике
  • ⚙️ Настройка региональных особенностей (например, переносы выходных)
💡

Для автоматизации загрузки праздничных дней используйте обработку "Календарь России" из каталога 1С-Софт (бесплатно для пользователей ИТС).

3. Программное вычитание месяцев для сложных сценариев

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

Рекурсивный метод подходит для вычитания большого количества месяцев (например, 12+). Он последовательно вычитает по одному месяцу, пока не достигнет нужного результата:

Функция ВычестьМесяцыРекурсивно(Дата, Месяцев)

Если Месяцев = 0 Тогда

Возврат Дата;

Иначе

Возврат ВычестьМесяцыРекурсивно(ДобавитьМесяц(Дата, -1), Месяцев - 1);

КонецЕсли;

КонецФункции

Итеративный метод более эффективен и не ограничен глубиной рекурсии:

Функция ВычестьМесяцыИтеративно(Дата, Месяцев)

Результат = Дата;

Для Сч = 1 По Месяцев Цикл

Результат = ДобавитьМесяц(Результат, -1);

КонецЦикла;

Возврат Результат;

КонецФункции

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

Функция НайтиФискальныйПериод(Дата, Месяцев)

ТекущийПериод = Справочники.ФискальныеПериоды.НайтиПоРеквизиту("ДатаНачала", Дата);

Если ТекущийПериод.Пустая() Тогда

Возврат Неопределено;

КонецЕсли;

Цель = ТекущийПериод.Индекс - Месяцев;

Возврат Справочники.ФискальныеПериоды.ПолучитьПоИндексу(Цель).ДатаНачала;

КонецФункции

Чем опасна рекурсия при работе с датами?

При вычитании большого количества месяцев (например, 100+) рекурсивная функция может превысить лимит стека вызовов в 1С (по умолчанию 1000), что приведет к ошибке выполнения. Итеративный метод лишен этого недостатка.

4. Обработка крайних случаев: 31-е числа и високосные годы

Самые распространенные ошибки при вычитании месяцев связаны с несуществующими датами. Например, что должно получиться при вычитании 1 месяца из 31 марта? Правильный ответ зависит от бизнес-логики:

Исходная дата Вычитаем 1 месяц Результат ДобавитьМесяц() Альтернативный результат Комментарий
31.01.2026 -1 31.12.2023 01.01.2026 Стандартная функция берет последний день предыдущего месяца
30.04.2026 -2 29.02.2026 30.02.2026 (ошибка) Високосный год: февраля 29 дней
31.03.2026 -1 29.02.2026 31.02.2026 (ошибка) Февраль не имеет 31-го числа
15.05.2026 -3 15.02.2026 15.02.2026 Без конфликтов — день существует в целевом месяце

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

Функция ВычестьМесяцыССохранениемДня(Дата, Месяцев)

Результат = Дата;

День = День(Дата);

Результат = НачалоМесяца(ДобавитьМесяц(Результат, -Месяцев)) + День - 1;

Возврат Результат;

КонецФункции

⚠️ Внимание: При таком подходе функция может вернуть несуществующую дату (например, 30.02.2026). Перед использованием результата обязательно проверяйте корректность даты функцией ДатаВремя.ДатаДействительна()!

5. Вычитание месяцев в запросах 1С

При работе с языком запросов 1С вычитание месяцев реализуется иначе, чем на встроенном языке. Здесь нельзя использовать ДобавитьМесяц() напрямую — нужно применять конструкции языка запросов или выносить логику в временные таблицы.

Базовый синтаксис для вычитания дней (не месяцев!):

ВЫБРАТЬ

ДатаДокмента КАК ИсходнаяДата,

ДатаДокмента - 30 КАК ДатаМинус30Дней

ИЗ

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

Для вычитания месяцев в запросе есть два варианта:

  1. Использовать виртуальную таблицу с предварительно рассчитанными датами:
    ВЫБРАТЬ
    

    Т.Дата КАК ДатаМинус1Месяц

    ИЗ

    (ВЫБРАТЬ

    ДобавитьМесяц(&ТекущаяДата, -1) КАК Дата) КАК Т

  2. Применить функцию НАЧАЛОПЕРИОДА() для сдвига на начало месяца с последующим вычитанием дней:
    ВЫБРАТЬ
    

    НАЧАЛОПЕРИОДА(ДатаДокмента, "МЕСЯЦ") - 1 КАК ПоследнийДеньПредыдущегоМесяца

Для сложных отчетов (например, сравнения данных за аналогичный период прошлого года) удобно использовать параметры запроса:

ЗАПРОС.УстановитьПараметр("ДатаНачала", ДобавитьМесяц(&ТекущаяДата, -12));

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

ЗАПРОС.Текст =

"ВЫБРАТЬ

Сумма(СуммаДокумента) КАК Итого

ИЗ

Документ.ПоступлениеТоваров

ГДЕ

Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";

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

Сформировать исходную дату на встроенном языке|Преобразовать дату в начало/конец периода при необходимости|Использовать параметры запроса для динамических дат|Проверить корректность результатов на пограничных датах-->

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

Даже опытные разработчики иногда допускают ошибки при работе с датами. Вот самые распространенные ловушки и способы их обхода:

  • 🗓️ "31-е числа": Как уже упоминалось, вычитание месяца из 31 января даст 31 декабря, что не всегда ожидаемо. Решение — явная проверка дня месяца и корректировка при необходимости.
  • 🔄 Циклические вычисления: При вычитании месяцев в цикле (например, для построения графика) легко получить бесконечный цикл, если не контролировать дату окончания. Всегда ограничивайте количество итераций.
  • Производительность: Вычитание месяцев по одному в большом цикле (например, для 1000 строк) работает медленно. Оптимизируйте код, используя массивы или временные таблицы.
  • 📅 Часовой пояс: При работе с датой-временем не забывайте про часовой пояс, особенно если данные синхронизируются с внешними системами. Используйте УстановитьЧасовойПояс().

Пример кода с защитой от бесконечного цикла:

Процедура ПостроитьГрафикПоМесяцам(НачальнаяДата, КоличествоМесяцев)

ТекущаяДата = НачальнаяДата;

МассивДат = Новый Массив();

Счетчик = 0;

Пока Счетчик < КоличествоМесяцев И ТекущаяДата >= Дата(1,1,1) Цикл

МассивДат.Добавить(ТекущаяДата);

ТекущаяДата = ДобавитьМесяц(ТекущаяДата, -1);

Счетчик = Счетчик + 1;

КонецЦикла;

Возврат МассивДат;

КонецПроцедуры

Еще одна типичная ошибка — неучет летних/зимних переходов времени при работе с датой-временем. Например, вычитание 24 часов из даты в день перехода на зимнее время даст некорректный результат. Решение — работать только с датами (без времени) или использовать UTC.

💡

Всегда тестируйте код на пограничных датах: 28-31 числа, 1 января, 29 февраля. Это позволит выявить 90% ошибок еще на этапе разработки.

7. Готовые обработки для вычитания месяцев

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

Структура обработки:

  • 📅 Поле ввода исходной даты (тип Дата)
  • 🔢 Поле для количества месяцев (тип Число)
  • ➕/➖ Переключатель "Прибавить/Вычесть"
  • ⚙️ Флажок "Учитывать рабочие дни"
  • 📊 Кнопка "Рассчитать" и поле вывода результата

Код обработки:

&НаКлиенте

Процедура Рассчитать(Команда)

ИсходнаяДата = ЭлементыФормы.Дата.Значение;

Количество = ЭлементыФормы.Количество.Значение;

Операция = ЭлементыФормы.Операция.Значение; // "Вычесть" или "Прибавить"

УчитыватьРабочиеДни = ЭлементыФормы.УчитыватьРабочиеДни.Значение;

Если Операция = "Вычесть" Тогда

Количество = -Количество;

КонецЕсли;

Результат = ДобавитьМесяц(ИсходнаяДата, Количество);

Если УчитыватьРабочиеДни Тогда

Результат = ПолучитьРабочуюДата(Результат);

КонецЕсли;

ЭлементыФормы.Результат.Значение = Результат;

КонецПроцедуры

&НаСервере

Функция ПолучитьРабочуюДата(Дата)

// Логика проверки на выходные и праздники

// ...

Возврат Дата;

КонецФункции

Готовые обработки для работы с датами можно найти:

  • 🔹 В каталоге 1С-Софт (раздел "Работа с датами")
  • 🔹 На портале Infostart (поиск по тегу "даты")
  • 🔹 В GitHub-репозиториях с открытым кодом для 1С
⚠️ Внимание: Перед использованием сторонних обработок проверяйте их на вирусы и совместимость с вашей версией платформы. Некоторые решения могут содержать уязвимости или конфликтовать с типовыми конфигурациями.

FAQ: Частые вопросы по вычитанию месяцев в 1С

Как вычесть месяцы из даты в отчете СКД?

В схеме компоновки данных используйте выражение вида ДобавитьМесяц(&ТекущаяДата, -3) в настройках параметра. Либо создайте вычисляемое поле с формулой:

ВЫБРАТЬ

ДобавитьМесяц(ДатаДокмента, -&КоличествоМесяцев) КАК ДатаСмещенная

Не забывайте объявить параметр &КоличествоМесяцев в параметрах СКД.

Почему при вычитании месяца из 31.03.2026 получаю 28.02.2026, а не 31.02.2026?

Это корректное поведение функции ДобавитьМесяц(). Февраль 2026 года имеет только 29 дней (високосный год), поэтому 31-е число автоматически корректируется до последнего существующего дня — 29.02.2026. Если вам нужно сохранить 31-е число, используйте альтернативный алгоритм из раздела 4.

Можно ли вычесть месяцы в мобильном приложении 1С?

Да, в мобильной платформе 1С:Предприятие доступны те же функции работы с датами, что и в десктопной версии. Однако учитывайте:

  • Производительность на мобильных устройствах ниже — избегайте сложных циклов.
  • Интерфейс для ввода дат адаптирован под сенсорные экраны (используйте элемент управления ПолеВводаДаты).
  • В офлайн-режиме проверяйте синхронизацию дат с сервером, чтобы избежать расхождений.
Как вычесть месяцы с учетом производственного календаря?

Для этого потребуется:

  1. Загрузить производственный календарь в справочник (можно экспортировать из Excel).
  2. Создать функцию, которая проверяет, является ли дата рабочей:
  3. Функция ДатаРабочая(Дата)
    

    Возврат НЕ Справочники.ПроизводственныйКалендарь.НайтиПоРеквизиту("Дата", Дата).ЭтоВыходной;

    КонецФункции

  4. В цикле вычитать дни, пока не будет найдена рабочая дата:
  5. Результат = ДобавитьМесяц(ИсходнаяДата, -1);
    

    Пока НЕ ДатаРабочая(Результат) Цикл

    Результат = Результат - 1;

    КонецЦикла;

Где хранится справочник праздничных дней в типовых конфигурациях?

В большинстве типовых конфигураций (например, 1С:ЗУП или 1С:ERP) праздничные дни хранятся в справочнике Календари или ПроизводственныеКалендари. Путь к ним:

Справочники → Организации → [Выбрать организацию] → Производственные календари

Если справочник пуст, его можно заполнить автоматически через обработку "Загрузка производственного календаря" (доступна в ИТС).