К статье
Практический материал

Разработка и Конфигурация · 9 мин

Как посчитать разность дат в месяцах в 1С

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

Вычисление разности между двумя датами в месяцах — одна из самых частых задач при разработке и доработке конфигураций 1С. Она встречается при расчёте стажа, сроков действия договоров, отсрочек платежа, амортизации и периодов обслуживания.

Главная сложность в том, что встроенный язык 1С не содержит готовой функции вида «РазностьВМесяцах». Арифметика дат (вычитание одной даты из другой) возвращает результат в секундах, а месяц — нефиксированная величина: от 28 до 31 дня. Поэтому разработчику приходится выбирать подходящий алгоритм и явно определять, что считать полным месяцем.

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

Что считать разностью в месяцах: определяем правило

Прежде чем писать код, нужно зафиксировать бизнес-правило. Между 15 января и 10 марта можно насчитать один полный месяц, два календарных месяца или «почти два» — в зависимости от того, что требует задача.

На практике применяют три подхода:

  • Полные месяцы — месяц засчитывается, только если прошёл целиком (с 15 января по 15 февраля — один месяц, до 14 февраля — ноль полных месяцев);
  • Календарные месяцы — считается разность номеров месяцев без учёта дней (с 31 января по 1 февраля — один месяц);
  • Дробное значение — месяцы плюс остаток в днях, делённый на условную длину месяца (применяется в финансовых расчётах).

Способ 1. Функция РазностьДат из БСП

Если конфигурация построена на Библиотеке стандартных подсистем (БСП) — это «Управление торговлей», «Бухгалтерия предприятия», «Зарплата и управление персоналом» и большинство типовых решений — используйте готовую функцию РазностьДат.

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

bsl
РазностьВМесяцах = СтандартныеПодсистемыСервер.РазностьДат(ДатаНачала, ДатаОкончания, "месяц");

Функция возвращает целое число полных единиц указанного типа. Для режима «месяц» учитывается и день месяца: между 10.01 и 09.03 вернётся 1, между 10.01 и 10.03 — 2. Это соответствует логике «полных месяцев».

Преимущество способа — отсутствие собственного кода и предсказуемое поведение. Недостаток — функция доступна только в конфигурациях на БСП. Проверить наличие модуля можно через конфигуратор: раздел «Общие модули», поиск по имени СтандартныеПодсистемыСервер.

Способ 2. Ручной расчёт через год и месяц

Если БСП нет или нужна особая логика, разность вычисляется вручную через функции Год() и Месяц():

bsl
Функция РазностьВМесяцах(ДатаНачала, ДатаОкончания)
	Результат = (Год(ДатаОкончания) - Год(ДатаНачала)) * 12
		+ Месяц(ДатаОкончания) - Месяц(ДатаНачала);
	Если День(ДатаОкончания) < День(ДатаНачала) Тогда
		Результат = Результат - 1;
	КонецЕсли;
	Возврат Результат;
КонецФункции

Базовая формула (Год2 - Год1) * 12 + Месяц2 - Месяц1 даёт разность календарных месяцев. Поправка на день месяца переводит расчёт в режим «полных месяцев»: если день конечной даты меньше дня начальной, последний месяц не засчитывается.

Обратите внимание на граничный случай: 31 января и 28 февраля. День конечной даты меньше, поэтому формула вернёт 0 полных месяцев, хотя фактически прошёл почти месяц. Если для вашей задачи это неприемлемо (например, при начислении аренды), добавьте обработку: считать день конечной даты равным последнему дню месяца, когда начальная дата — последний день своего месяца. Функция КонецМесяца(Дата) поможет определить это условие.

Типичные ошибки
01
Делить разность дат в секундах на 2592000 (30 дней) — погрешность накапливается и на длинных периодах даёт ошибку в целый месяц
02
Забыть поправку на день месяца — между 25.01 и 05.02 получается 1 месяц вместо 0 полных
03
Не проверить порядок дат: если дата окончания раньше даты начала, результат станет отрицательным и сломает дальнейшую логику
04
Использовать пустую дату (01.01.0001) — расчёт вернёт огромное число месяцев без видимой ошибки

Способ 3. Разность дат в запросе

Когда расчёт нужен для выборки данных — например, отобрать договоры, до окончания которых осталось меньше трёх месяцев, — удобнее вычислять разность прямо в запросе с помощью функции РАЗНОСТЬДАТ:

bsl
ВЫБРАТЬ
	Договоры.Ссылка КАК Договор,
	РАЗНОСТЬДАТ(&ТекущаяДата, Договоры.ДатаОкончания, МЕСЯЦ) КАК МесяцевОсталось
ИЗ
	Справочник.ДоговорыКонтрагентов КАК Договоры
ГДЕ
	РАЗНОСТЬДАТ(&ТекущаяДата, Договоры.ДатаОкончания, МЕСЯЦ) < 3

Функция принимает две даты и единицу измерения: МЕСЯЦ, ДЕНЬ, ГОД, КВАРТАЛ и другие. Результат — целое число полных единиц между датами.

Учтите, что поведение РАЗНОСТЬДАТ относительно дня месяца может отличаться от логики РазностьДат в БСП: в запросе обычно считается разность календарных месяцев. Поэтому не смешивайте оба способа в одном отчёте без сверки результатов на контрольных примерах.

Сравнение способов

РазностьДат (БСП)

Подходит для кода в типовых конфигурациях на БСП. Считает полные месяцы. Не работает без БСП.

Ручной расчёт

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

РАЗНОСТЬДАТ в запросе

Оптимален для отборов и отчётов по большим выборкам. Логика учёта дней отличается от БСП.

Как проверить результат

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

  • одинаковые даты — результат должен быть 0;
  • ровно год (10.03–10.03 следующего года) — ровно 12;
  • переход через границу года (15.12–15.01) — 1;
  • день конца меньше дня начала (20.01–10.03) — зависит от выбранного правила;
  • конец месяца (31.01–28.02) — проверяет обработку последнего дня месяца.
Контрольные точки

Как проверить результат

Расчёт на паре 10.01–10.03
Норма

Результат 2 полных месяца

Тревожный признак

Результат 1 или 3 — ошибка в базовой формуле

Расчёт на паре 20.01–10.02
Норма

0 полных месяцев (при правиле полных)

Тревожный признак

1 месяц — забыта поправка на день

Пустая дата окончания
Норма

Код явно обрабатывает пустую дату (пропуск или отдельное сообщение)

Тревожный признак

Возвращается огромное число месяцев

Дата окончания раньше даты начала
Норма

Есть проверка и понятная реакция (0, отрицательное значение или предупреждение)

Тревожный признак

Отрицательное число молча уходит дальше в расчёты

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

Безопасные границы самостоятельной доработки

Добавление собственной функции расчёта месяцев в общий модуль или внешнюю обработку — безопасная операция, если вы не меняете типовые модули. Изменение типового кода усложнит обновление конфигурации и может затереться при очередном релизе.

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

Частые вопросы

Почему вычитание дат в 1С не даёт месяцы?

Операция «Дата2 – Дата1» возвращает разность в секундах, потому что дата в 1С хранится с точностью до секунды. Чтобы получить месяцы, нужно либо делить с поправками (ненадёжно), либо использовать РазностьДат, РАЗНОСТЬДАТ в запросе или формулу через Год() и Месяц().

Как посчитать разность в месяцах с дробной частью?

Вычислите целые полные месяцы, затем остаток в днях разделите на количество дней в текущем месяце (День(КонецМесяца(Дата))). Например: 2 месяца и 15 дней при 30-дневном месяце дадут примерно 2,5. Точную формулу округления согласуйте с постановкой задачи.

Что вернёт расчёт, если дата окончания раньше даты начала?

РазностьДат и ручная формула вернут отрицательное число. Если по логике задачи это недопустимо, добавьте проверку: при ДатаОкончания < ДатаНачала возвращать 0, менять даты местами или выводить предупреждение пользователю.

Как учесть, что месяцы имеют разную длину?

При расчёте полных месяцев длина месяца не важна — важен день начала и окончания. Проблема возникает только на границе «31 число → 28/30 число». Стандартное решение: если начальная дата — последний день месяца, считать концом периода последний день соответствующего месяца через КонецМесяца().

Можно ли посчитать разность в месяцах в СКД без запроса?

Да, в вычисляемых полях СКД доступна функция РазностьДат языка выражений компоновки с аналогичным синтаксисом. Однако чаще проще вычислить поле в основном запросе через РАЗНОСТЬДАТ — так результат доступен и для отборов, и для группировок.

Чем отличается расчёт стажа от обычной разности в месяцах?

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

Итог

Что важно запомнить

Готовой функции разности в месяцах в языке 1С нет. В конфигурациях на БСП используйте РазностьДат, в запросах — РАЗНОСТЬДАТ, в остальных случаях — формулу (Год2-Год1)*12 + Месяц2-Месяц1 с поправкой на день. Сначала зафиксируйте правило (полные или календарные месяцы), затем проверьте расчёт на граничных датах.