Как правильно рассчитать количество месяцев между двумя датами в 1С?

Программист
← На главную

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

Проанализируем ситуацию. Автор столкнулся с проблемой, когда при вычислении разницы между 15.01.2025 и 04.09.2024 его код выдавал "-8".


МояПеременная = Месяц(Выборка.ДатаПоказания) - Месяц(Выборка.ДатаПредыдущегоПоказания);
// Результат: 1 - 9 = -8

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

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

Решение 1: Математический расчет на встроенном языке

Это самый универсальный способ, который работает непосредственно во встроенном языке 1С без использования запросов. Идея заключается в том, чтобы перевести обе даты в "абсолютное" количество месяцев с начала летосчисления и затем найти разницу.

Формула выглядит так: общее количество месяцев для одной даты вычисляется как Год * 12 + Месяц. Вычтя одно значение из другого, мы получим искомую разницу.

Посмотрим на пример из темы:

Реализуем это на встроенном языке 1С:


ДатаНачала = Дата('20240904');
ДатаКонца = Дата('20250115');

РазницаМесяцев = (Год(ДатаКонца) - Год(ДатаНачала)) * 12 + Месяц(ДатаКонца) - Месяц(ДатаНачала);

// Рассчитаем: (2025 - 2024) * 12 + 1 - 9 = 1 * 12 - 8 = 4
// Результат: 4

Этот код вернет 4. Результат означает, что период между датами затрагивает 4 календарных месяца: сентябрь, октябрь, ноябрь, декабрь и январь. Но будьте внимательны: этот способ не учитывает дни и не скажет, сколько полных месяцев прошло.

Как посчитать количество полных месяцев?

Часто в задачах требуется узнать, сколько именно полных месяцев прошло между датами. Например, между 30 сентября и 15 января прошло три полных месяца (октябрь, ноябрь, декабрь). Чтобы учесть это, нужно доработать предыдущий алгоритм. Добавим простое условие: если день конечной даты меньше дня начальной, значит, последний месяц не завершился, и его нужно вычесть из результата.

Оформим это в виде удобной функции:


Функция ПолныхМесяцевМеждуДатами(ДатаНачала, ДатаКонца)
    
    // Проверяем, чтобы даты не были пустыми
    Если ДатаНачала = '00010101' ИЛИ ДатаКонца = '00010101' Тогда
        Возврат 0;
    КонецЕсли;

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

// Пример использования
Разница = ПолныхМесяцевМеждуДатами(Дата('20240904'), Дата('20250115')); // Вернет 4
Разница2 = ПолныхМесяцевМеждуДатами(Дата('20240904'), Дата('20250103')); // Вернет 3

В первом примере результат 4, так как с 4 сентября по 4 января прошло 4 полных месяца, а у нас 15 января. Во втором примере результат 3, так как 3 января меньше 4 сентября, и неполный январь отбрасывается.

Решение 2: Использование функции `РазностьДат()` в запросах

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

Синтаксис функции:

РазностьДат(ДатаНачала, ДатаОкончания, ТипРазности)

Где ТипРазности может быть "ГОД", "КВАРТАЛ", "МЕСЯЦ", "ДЕНЬ" и т.д.

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


ВЫБРАТЬ
    ПоказанияСчетчиков.Ссылка,
    ПоказанияСчетчиков.ДатаПоказания,
    ПоказанияСчетчиков.ДатаПредыдущегоПоказания,
    РАЗНОСТЬДАТ(
        ПоказанияСчетчиков.ДатаПредыдущегоПоказания,
        ПоказанияСчетчиков.ДатаПоказания,
        МЕСЯЦ
    ) КАК КоличествоМесяцев
ИЗ
    РегистрСведений.ПоказанияСчетчиков КАК ПоказанияСчетчиков

Преимущества этого подхода:

  1. Точность и надежность: Расчет выполняется средствами платформы, что гарантирует корректность для любых дат.
  2. Производительность: Вычисления происходят на уровне СУБД, что обычно быстрее, чем обработка данных в цикле на стороне 1С.
  3. Читаемость кода: Запрос становится более понятным, а его логика — прозрачной.
  4. Обработка пустых дат: Можно легко обработать ситуации, когда одна из дат не заполнена, с помощью конструкции ЕСТЬNULL.

// Пример с обработкой пустой начальной даты
РАЗНОСТЬДАТ(
    ЕСТЬNULL(ПоказанияСчетчиков.ДатаПредыдущегоПоказания, НАЧАЛОПЕРИОДА(ПоказанияСчетчиков.ДатаПоказания, МЕСЯЦ)),
    ПоказанияСчетчиков.ДатаПоказания,
    МЕСЯЦ
)

Решение 3: Приблизительный расчет через секунды (не рекомендуется)

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


// Разница в секундах
РазницаСекунд = ДатаКонца - ДатаНачала; 
// Переводим в месяцы, принимая в месяце 30 дней
РазницаМесяцев = РазницаСекунд / 86400 / 30; 

Этот метод категорически не рекомендуется для точных вычислений. Его главная проблема — погрешность. Месяцы имеют разную продолжительность (от 28 до 31 дня), и использование среднего значения "30" приведет к неверным результатам, особенно на больших интервалах. Результат такого вычисления будет дробным числом (в примере автора 4,43), что может потребовать дополнительного округления и усложнит логику.

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

Итоги

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

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

← На главную