Работа с датами в 1С:Предприятие — одна из самых востребованных задач, с которой сталкиваются и бухгалтеры при формировании отчетов, и разработчики при создании обработок. Особенно часто требуется вычесть из даты определенное количество месяцев — например, для расчета просроченной задолженности, определения периода действия договора или анализа динамики продаж. На первый взгляд задача кажется простой, но на практике сталкиваешься с нюансами: разное количество дней в месяцах, високосные годы, переходы через годовые границы.
В этой статье мы разберем все возможные способы вычитания месяцев в 1С — от стандартных функций платформы до авторских алгоритмов на встроенном языке. Вы узнаете, какой метод выбрать для конкретной задачи, как избежать ошибок при работе с крайними датами (например, 31 января минус 1 месяц), и получите готовые коды для копирования. Материал будет полезен как начинающим пользователям, так и опытным программистам 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.
2. Вычитание месяцев с учетом рабочих дней
В бизнес-задачах часто требуется не просто вычесть календарные месяцы, а получить рабочую дату — например, последний рабочий день месяца или дату с учетом выходных. Для этого стандартных функций недостаточно: придется комбинировать их с проверкой на выходные и праздники.
Алгоритм может выглядеть так:
- Вычесть нужное количество месяцев функцией
ДобавитьМесяц(). - Проверить, не попала ли результирующая дата на выходной (суббота/воскресенье).
- Если попала — сдвинуть на предыдущий рабочий день.
- Дополнительно учесть праздничные дни (требуется справочник праздников).
Функция ВычестьМесяцыСУчетомРабочихДней(НачальнаяДата, КоличествоМесяцев)
Результат = ДобавитьМесяц(НачальнаяДата, -КоличествоМесяцев);
Пока ДеньНедели(Результат) = 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 КАК ПоследнийДеньПредыдущегоМесяца
Для сложных отчетов (например, сравнения данных за аналогичный период прошлого года) удобно использовать параметры запроса:
ЗАПРОС.УстановитьПараметр("ДатаНачала", ДобавитьМесяц(&ТекущаяДата, -12));
ЗАПРОС.УстановитьПараметр("ДатаОкончания", &ТекущаяДата);
ЗАПРОС.Текст =
"ВЫБРАТЬ
Сумма(СуммаДокумента) КАК Итого
ИЗ
Документ.ПоступлениеТоваров
ГДЕ
Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
⚠️ Внимание: В запросах 1С нет встроенной функции для вычитания месяцев — только дней. Все манипуляции с месяцами нужно выполнять на встроенном языке до формирования запроса или использовать обходные пути, как показано выше.
Сформировать исходную дату на встроенном языке|Преобразовать дату в начало/конец периода при необходимости|Использовать параметры запроса для динамических дат|Проверить корректность результатов на пограничных датах-->
6. Типичные ошибки и как их избежать
Даже опытные разработчики 1С иногда допускают ошибки при работе с датами. Вот самые распространенные ловушки и способы их обхода:
- 🗓️ "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С:Предприятие доступны те же функции работы с датами, что и в десктопной версии. Однако учитывайте:
- Производительность на мобильных устройствах ниже — избегайте сложных циклов.
- Интерфейс для ввода дат адаптирован под сенсорные экраны (используйте элемент управления
ПолеВводаДаты). - В офлайн-режиме проверяйте синхронизацию дат с сервером, чтобы избежать расхождений.
Как вычесть месяцы с учетом производственного календаря?
Для этого потребуется:
- Загрузить производственный календарь в справочник (можно экспортировать из Excel).
- Создать функцию, которая проверяет, является ли дата рабочей:
- В цикле вычитать дни, пока не будет найдена рабочая дата:
Функция ДатаРабочая(Дата)
Возврат НЕ Справочники.ПроизводственныйКалендарь.НайтиПоРеквизиту("Дата", Дата).ЭтоВыходной;
КонецФункции
Результат = ДобавитьМесяц(ИсходнаяДата, -1);
Пока НЕ ДатаРабочая(Результат) Цикл
Результат = Результат - 1;
КонецЦикла;
Где хранится справочник праздничных дней в типовых конфигурациях?
В большинстве типовых конфигураций (например, 1С:ЗУП или 1С:ERP) праздничные дни хранятся в справочнике Календари или ПроизводственныеКалендари. Путь к ним:
Справочники → Организации → [Выбрать организацию] → Производственные календари
Если справочник пуст, его можно заполнить автоматически через обработку "Загрузка производственного календаря" (доступна в ИТС).