В процессе разработки на платформе 1С:Предприятие одной из самых частых задач является манипуляция с датами. Будь то расчет периода для отчета, определение срока оплаты счета или вычисление даты начала прошлого месяца, программисту необходимо точно знать, как система обрабатывает календарные циклы. В этой статье мы подробно разберем, как правильно отнять один месяц от текущей или произвольной даты, рассмотрим особенности работы встроенного языка и запросов, а также проанализируем поведение системы в сложных ситуациях, таких как високосные годы и разное количество дней в месяцах.
Для решения задачи во встроенном языке 1С предусмотрена стандартная функция ДобавитьМесяц. Несмотря на название, она универсальна: передавая в качестве второго параметра отрицательное число, мы выполняем операцию вычитания.
Рассмотрим простейший пример, когда нам нужно получить дату ровно на один месяц меньше текущей:
ТекущаяДата = ТекущаяДата(); // Например, 15.05.2023
ДатаМесяцНазад = ДобавитьМесяц(ТекущаяДата, -1);
// Результат будет: 15.04.2023
Разберем по шагам, что происходит в данном коде. Функция ДобавитьМесяц принимает два аргумента: исходную дату (тип Дата) и количество месяцев, которые нужно прибавить (тип Число). Если мы указываем -1, система пересчитывает календарный месяц назад. При этом время (часы, минуты, секунды) остается неизменным, что крайне важно для точности расчетов.
Выясним причину, по которой многие начинающие разработчики сталкиваются с трудностями при работе с датами вроде 31 марта. Логика 1С при вычитании месяца строится на сохранении числа месяца, но с учетом максимально возможного количества дней в целевом периоде.
Проанализируем ситуацию на примере:
ИсходнаяДата = '20230331'; // 31 марта
Результат = ДобавитьМесяц(ИсходнаяДата, -1);
// Результат: 28.02.2023
Посмотрим на этот механизм подробнее. Поскольку в феврале 2023 года всего 28 дней, система не может вернуть "31 февраля". Вместо возникновения ошибки или переноса на март, 1С "приземляет" результат на последний день месяца. Это стандартное и ожидаемое поведение функции ДобавитьМесяц. Однако здесь кроется важный нюанс, который мы назовем «проблемой необратимости».
Важный момент: если вы отнимете месяц от 31 марта и получите 28 февраля, а затем прибавите к результату один месяц обратно, вы получите 28 марта, а не 31-е. Система "забывает", что исходное число было 31-м, так как промежуточный результат (28.02) уже зафиксировал это число как конечное.
Зачастую задачу вычитания месяца нужно решить не в коде, а непосредственно при выборке данных из базы. В языке запросов 1С функция ДобавитьМесяц недоступна, вместо нее используется функция ДОБАВИТЬКДАТЕ.
Рассмотрим пример синтаксиса в запросе:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&НачальнаяДата, МЕСЯЦ, -1) КАК ДатаПрошлогоМесяца
В данном случае мы указываем три параметра:
Дата.МЕСЯЦ).-1 для вычитания).Логика работы функции ДОБАВИТЬКДАТЕ в запросах полностью идентична функции ДобавитьМесяц во встроенном языке в плане обработки границ месяцев и високосных годов.
На практике программистам часто требуется не просто "отнять 30 дней", а получить первый день предыдущего календарного месяца относительно текущего. Для этого лучше всего использовать комбинацию функций НачалоМесяца и ДобавитьМесяц.
Разберем этот надежный метод:
// Предположим, сегодня 31 мая
ПрошлыйМесяц = ДобавитьМесяц(ТекущаяДата(), -1); // Получаем конец апреля или дату в апреле
НачалоПрошлогоМесяца = НачалоМесяца(ПрошлыйМесяц); // Получаем 01.04.2023
Такой подход позволяет избежать любых ошибок, связанных с разной длиной месяцев. Независимо от того, 28, 29, 30 или 31 день в текущем месяце, вы всегда попадете в нужный период.
Иногда возникает соблазн просто отнять от даты количество секунд, эквивалентное 30 дням (60 * 60 * 24 * 30). Проанализируем, почему это является грубой ошибкой:
Функция ДобавитьМесяц — это высокоуровневый инструмент, который инкапсулирует в себе всю календарную логику, избавляя разработчика от ручных проверок.
Посмотрим на пример с 29 февраля. Если ваша задача — получить дату ровно год назад, вы можете использовать ДобавитьМесяц с параметром -12.
ДатаВисокосная = '20240229'; // 29 февраля 2024 (високосный)
ГодНазад = ДобавитьМесяц(ДатаВисокосная, -12);
// Результат: 28.02.2023
Система корректно понимает, что в 2023 году 29-го числа нет, и возвращает 28 февраля. Это еще раз подтверждает, что использование встроенных функций — единственный верный путь для работы с календарными данными в 1С.
В отчетах на базе СКД вы можете использовать те же функции в выражениях параметров или вычисляемых полей. Часто для удобства пользователей в СКД создаются параметры "НачалоПериода" и "КонецПериода".
Чтобы автоматически установить в параметре дату на месяц назад от текущей, в поле "Выражение" настроек СКД можно написать:
ДобавитьКДате(&ТекущаяДата, "Месяц", -1)
Это позволит отчету при открытии сразу предлагать пользователю данные за прошлый месяц, что значительно повышает удобство работы с программой.
Подводя итог, мы видим, что задача "отнять месяц" в 1С решается просто, но требует понимания внутреннего устройства платформы. Мы рассмотрели основные методы:
ДобавитьМесяц(Дата, -1).ДОБАВИТЬКДАТЕ(Дата, МЕСЯЦ, -1).НачалоМесяца() и ДобавитьМесяц().Использование этих инструментов гарантирует корректность ваших расчетов в любой день года и для любого календарного периода.