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

В этой статье мы разберем все актуальные методы получения предыдущего месяца, включая нюансы работы с НачалоМесяца(), КонецМесяца(), запросами и программными конструкциями. Особое внимание уделим типичным ошибкам, которые приводят к сбоям в отчетах или некорректным расчетам. Материал будет полезен как начинающим разработчикам , так и опытным программистам, которые хотят оптимизировать свой код.

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

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

  • 📅 НачалоМесяца(Дата) — возвращает первую дату указанного месяца (например, 01.05.2026 для мая 2026)
  • 📅 КонецМесяца(Дата) — возвращает последнюю дату месяца (например, 31.05.2026)
  • 🔙 ДобавитьМесяц(Дата, Количество) — сдвигает дату на заданное количество месяцев (отрицательное значение вернет прошлый месяц)

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

ТекущаяДата = ТекущаяДата();

ПервыйДеньПрошлогоМесяца = НачалоМесяца(ДобавитьМесяц(ТекущаяДата, -1));

ПоследнийДеньПрошлогоМесяца = КонецМесяца(ДобавитьМесяц(ТекущаяДата, -1));

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

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

2. Использование запросов 1С для работы с датами

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

Пример запроса для выборки данных за предыдущий месяц:

ВЫБРАТЬ

ДатаДокумента КАК Дата,

СуммаДокумента КАК Сумма

ИЗ

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

ГДЕ

ДатаДокумента МЕЖДУ НачалоМесяца(ДобавитьМесяц(&ТекущаяДата, -1))

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

В этом примере &ТекущаяДата — параметр запроса, который можно передать из кода. Преимущество такого подхода в том, что фильтрация происходит на уровне СУБД, что ускоряет выполнение запроса на больших объемах данных.

💡

Если в запросе нужно получить не только предыдущий, но и текущий месяц, используйте конструкцию ДатаДокумента МЕЖДУ НачалоМесяца(ДобавитьМесяц(&ТекущаяДата, -1)) И КонецМесяца(&ТекущаяДата).

3. Программный код: гибкие решения для сложных задач

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

Пример 1. Получение рабочих дней предыдущего месяца

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

Процедура ПолучитьРабочиеДниПрошлогоМесяца()

ТекущаяДата = ТекущаяДата();

ПервыйДень = НачалоМесяца(ДобавитьМесяц(ТекущаяДата, -1));

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

РабочиеДни = Новый Массив;

ТекущаяДатаПроверки = ПервыйДень;

Пока ТекущаяДатаПроверки <= ПоследнийДень Цикл

Если НЕ ТекущаяДатаПроверки.ДеньНедели() = 6 // Суббота

И НЕ ТекущаяДатаПроверки.ДеньНедели() = 0 Тогда // Воскресенье

РабочиеДни.Добавить(ТекущаяДатаПроверки);

КонецЕсли;

ТекущаяДатаПроверки = ТекущаяДатаПроверки + 86400; // +1 день

КонецЦикла;

Возврат РабочиеДни;

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

Пример 2. Учет производственного календаря

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

Функция ЭтоРабочийДень(Дата)

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

Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ ДатаКакРабочаяДата

|ИЗ Справочник.ПроизводственныйКалендарь

|ГДЕ ДатаКакРабочаяДата = &Дата";

Запрос.УстановитьПараметр("Дата", Дата);

Результат = Запрос.Выполнить().Пустой();

Возврат НЕ Результат; // Если дата есть в календаре — это рабочий день

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

Как оптимизировать код для больших периодов?

Если вам нужно проверять рабочие дни за несколько лет, предварительно загрузите производственный календарь в регистр сведений с индексом по дате. Это ускорит проверку в 10-100 раз по сравнению с запросом к справочнику.

4. Особенности работы с началом и концом месяца

При работе с НачалоМесяца() и КонецМесяца() важно помнить о нескольких нюансах, которые могут привести к ошибкам:

  • Время в дате: Эти функции обнуляют время. Если ваша дата содержит время (например, 15.05.2026 14:30:00), результат все равно будет 01.05.2026 00:00:00 или 31.05.2026 23:59:59.
  • 📅 Февраль в високосный год: КонецМесяца() корректно обрабатывает 28/29 февраля, но если вы вручную добавляете дни, можете получить ошибку "Дата вне допустимого диапазона".
  • 🔄 Отрицательные месяцы: ДобавитьМесяц(Дата, -13) вернет дату на год и месяц назад, что иногда полезно для годовой аналитики.

Пример ошибки при ручном добавлении дней к февралю:

ДатаФевраль = '28.02.2026';

НоваяДата = ДатаФевраль + 86400 * 2; // Попытка добавить 2 дня

// Ошибка! В невисокосный год 28.02 + 2 дня = 02.03, но если год високосный,

// а дата была 29.02, то 29.02 + 1 день = 01.03 (корректно), но 29.02 + 2 дня

// в невисокосный год вызовет исключение.

Используйте НачалоМесяца()/КонецМесяца() вместо ручного добавления дней|Учитывайте високосные годы при работе с февралем|Проверяйте результат на граничных датах (31.12, 01.01)|Тестируйте код на разных конфигурациях (8.2 и 8.3)-->

5. Работа с предыдущим месяцем в отчетах (СКД)

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

Способ 1. Параметр с вычислением по умолчанию

В настройках параметра отчета укажите выражение для значения по умолчанию:

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

Способ 2. Вычисляемое поле в схеме компоновки

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

Формат(НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1)), "ММММ yyyy")

Это вернет строку вида "май 2026".

Способ 3. Динамический период в настройках СКД

В настройках периода отчета выберите тип "Произвольный период" и укажите:

  • 📌 Начало периода: =НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1))
  • 📌 Конец периода: =КонецМесяца(ДобавитьМесяц(ТекущаяДата(), -1))
💡

В СКД всегда проверяйте формат даты в параметрах отчета. Если пользователь вручную введет некорректную дату (например, "31.04.2026"), отчет может не сформироваться.

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

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

Ошибка Причина Решение
Некорректный расчет февраля в високосный год Ручное добавление дней к 29.02 в невисокосный год Используйте ДобавитьМесяц() вместо арифметики с днями
Отчет не показывает данные за предыдущий месяц В запросе не учтено время (например, фильтр по дате без времени) Добавьте НачалоДня() или КонецДня() в условия
Ошибка "Дата вне диапазона" при добавлении месяцев Переполнение даты (например, добавление -100 месяцев) Проверяйте границы дат перед операциями
Неверное название месяца в отчете Ошибка в формате даты (например, "MM" вместо "ММММ") Используйте Формат(Дата, "ММММ yyyy") для русскоязычного названия

Критическая ошибка: если вы используете предыдущий месяц для расчета налогов или зарплаты, всегда проверяйте результат на граничных датах (31 декабря, 1 января). Например, ДобавитьМесяц('31.12.2026', -1) вернет 30.11.2026, а не 31.11.2026 (которого не существует).

💡

Для отладки кода с датами используйте функцию Сообщить() с выводом промежуточных значений. Например: Сообщить(Формат(ДобавитьМесяц('31.01.2026', -1), "ДЛФ=DT"));

7. Оптимизация производительности при работе с датами

Если ваш код или запрос обрабатывает большие массивы данных (например, анализ продаж за несколько лет), неэффективная работа с датами может замедлить выполнение. Вот несколько советов по оптимизации:

  • Избегайте вложенных функций: Замените КонецМесяца(ДобавитьМесяц(Дата, -1)) на одно вычисление: ДобавитьМесяц(КонецМесяца(Дата), -1) — это сократит количество операций.
  • Используйте временные таблицы: Если в запросе много условий по датам, сохраните результат в временную таблицу и работайте с ней.
  • Кэшируйте результаты: Если предыдущий месяц вычисляется многократно (например, в цикле), сохраните его в переменную.

Пример оптимизированного кода для массовой обработки:

// Неоптимально (вычисление в каждом цикле)

Для Каждого Документ Из Документы Цикл

Если Документ.Дата МЕЖДУ НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1))

И КонецМесяца(ДобавитьМесяц(ТекущаяДата(), -1)) Тогда

// Обработка

КонецЕсли;

КонецЦикла;

// Оптимально (вычисление один раз)

НачалоПрошлогоМесяца = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1));

КонецПрошлогоМесяца = КонецМесяца(ДобавитьМесяц(ТекущаяДата(), -1));

Для Каждого Документ Из Документы Цикл

Если Документ.Дата МЕЖДУ НачалоПрошлогоМесяца И КонецПрошлогоМесяца Тогда

// Обработка

КонецЕсли;

КонецЦикла;

💡

При работе с большими базами данных (100 000+ документов) даже оптимизированный код может тормозить. В таких случаях используйте фоновые задания или разбивайте обработку на пакеты.

8. Практическое применение: примеры из реальных задач

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

Кейс 1. Автоматическое формирование регламентных отчетов

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

Процедура СформироватьОтчетПоПродажам()

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

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

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

Запрос.Текст = "ВЫБРАТЬ

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

ИЗ

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

ГДЕ

Дата МЕЖДУ &Начало И &Конец";

Запрос.УстановитьПараметр("Начало", НачалоПериода);

Запрос.УстановитьПараметр("Конец", КонецПериода);

Результат = Запрос.Выполнить();

Отчет = Новый ТабличныйДокумент;

// Формирование отчета...

Почта = Новый Почта;

Почта.Отправить("director@company.ru", "Отчет по продажам", Отчет);

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

Кейс 2. Расчет премий сотрудников

Задача: начислить премию за предыдущий месяц, если план продаж выполнен.

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

ПервыйДень = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1));

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

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

Запрос.Текст = "ВЫБРАТЬ

Сотрудники.Ссылка КАК Сотрудник,

СУММА(Продажи.Сумма) КАК ФактическиеПродажи

ИЗ

Документ.Продажи КАК Продажи

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сотрудники КАК Сотрудники

ПО Продажи.Сотрудник = Сотрудники.Ссылка

ГДЕ

Продажи.Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО

Сотрудники.Ссылка";

Запрос.УстановитьПараметр("Начало", ПервыйДень);

Запрос.УстановитьПараметр("Конец", ПоследнийДень);

Результат = Запрос.Выполнить();

Пока Результат.Следующий() Цикл

Если Результат.ФактическиеПродажи >= ПланПродаж(Результат.Сотрудник) Тогда

НачислитьПремию(Результат.Сотрудник, Результат.ФактическиеПродажи * 0.1);

КонецЕсли;

КонецЦикла;

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

Как учитывать праздничные дни в расчетах?

Если премия зависит от количества рабочих дней, используйте регистр сведений с производственным календарем. Пример структуры регистра:

- Измерение: Дата (тип Дата)

- Ресурс: ЯвляетсяРабочим (тип Булево)

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

⚠️ Внимание: При расчете зарплаты или премий всегда сверяйте логику с действующими нормативными актами. Например, если предыдущий месяц включал праздничные дни, их оплата может регулироваться ст. 112 ТК РФ (в России). Уточняйте актуальные правила в бухгалтерии или кадровой службе.

Часто задаваемые вопросы

Как получить название предыдущего месяца на русском языке?

Используйте функцию Формат() с параметром "ММММ":

Сообщить(Формат(ДобавитьМесяц(ТекущаяДата(), -1), "ММММ"));

Это вернет "май", "апрель" и т. д. Для полного названия с годом: Формат(Дата, "ММММ yyyy") → "май 2026".

Почему ДобавитьМесяц('31.01.2026', -1) возвращает 31.12.2023, а не 30.12.2023?

Функция ДобавитьМесяц() сохраняет день месяца, если он существует в целевом месяце. Поскольку в декабре 31 день, автоматически подставляет 31.12. Если бы вы добавили месяц к 31.03, результат был бы 30.04 (в апреле 30 дней).

Как получить предыдущий месяц в управляемых формах?

В управляемых формах используйте тот же подход, но с учетом контекста. Например, для поля ввода даты:

Процедура УстановитьПрошлыйМесяц(Команда)

ЭлементыФормы.ДатаНачала.Значение = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1));

ЭлементыФормы.ДатаОкончания.Значение = КонецМесяца(ДобавитьМесяц(ТекущаяДата(), -1));

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

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

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

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

  • 📱 Производительность на мобильных устройствах ниже — избегайте сложных вычислений в циклах.
  • 📱 Для отображения дат используйте формат, адаптированный под маленькие экраны (например, "дд.MM" вместо "дд ММММ yyyy").
Как проверить, что дата принадлежит предыдущему месяцу?

Сравните год и месяц даты с предыдущим месяцем:

Функция ДатаВПрошломМесяце(ПроверяемаяДата)

ТекущийМесяц = Месяц(ТекущаяДата());

ТекущийГод = Год(ТекущаяДата());

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

ПрошлыйГод = Год(ДобавитьМесяц(ТекущаяДата(), -1));

Возврат (Год(ПроверяемаяДата) = ПрошлыйГод)

И (Месяц(ПроверяемаяДата) = ПрошлыйМесяц);

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