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

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

Одной из классических задач в разработке на платформе 1С является вычисление точного периода между двумя датами. Казалось бы, задача тривиальна, однако на практике программисты сталкиваются с тем, что понятие «месяц» имеет переменную длительность (28, 29, 30 или 31 день). Простое деление разницы в секундах на фиксированное число 30 зачастую дает погрешность, неприемлемую для кадрового или финансового учета. В этой статье мы подробно разберем несколько проверенных способов реализации этой функции, проанализируем типовые алгоритмы и выясним, как правильно обрабатывать календарные особенности.

В чем сложность стандартного подхода?

Рассмотрим ситуацию: нам нужно узнать разницу между 31 января и 28 февраля. Если использовать стандартную математику и делить общее количество дней на 30, мы получим неполный месяц. Однако с точки зрения календаря — это ровно один месяц. Еще сложнее ситуация с високосными годами. Именно поэтому встроенная функция РАЗНОСТЬДАТ в языке запросов 1С работает по принципу пересечения границ календарных периодов, что не всегда подходит для бизнес-логики. Давайте разберем, какие инструменты у нас есть в арсенале.

Решение 1: Алгоритм «РазобратьРазностьДат» из УПП

Одним из самых надежных и проверенных временем способов является алгоритм, который ранее использовался в конфигурации «Управление производственным предприятием». Этот метод последовательно вычитает из конечной даты целые годы, затем месяцы, а остаток считает днями. Рассмотрим этот код подробнее и адаптируем его под современные требования.

Для реализации нам потребуется процедура, которая принимает две даты и возвращает результат через параметры. Мы также можем оформить это в виде функции, возвращающей Структура.


Процедура РазобратьРазностьДат(Дата1, Дата2, Лет = 0, Месяцев = 0, Дней = 0) Экспорт
    Лет = 0; Месяцев = 0; Дней = 0;
    
    Если Дата1 > Дата2 Тогда
        ВременнаяДата = Дата1;
        // Если день месяца у более поздней даты меньше, чем у ранней, 
        // значит, текущий месяц еще не завершился полностью.
        Если День(ВременнаяДата) < День(Дата2) Тогда
            Дней = ВременнаяДата - ДобавитьМесяц(ВременнаяДата, -1);
            ВременнаяДата = ДобавитьМесяц(ВременнаяДата, -1);
        КонецЕсли;
        
        // Считаем целые месяцы и годы
        Пока ДобавитьМесяц(Дата2, 12) <= ВременнаяДата Цикл
            Лет = Лет + 1;
            Дата2 = ДобавитьМесяц(Дата2, 12);
        КонецЦикла;
        
        Пока ДобавитьМесяц(Дата2, 1) <= ВременнаяДата Цикл
            Месяцев = Месяцев + 1;
            Дата2 = ДобавитьМесяц(Дата2, 1);
        КонецЦикла;
        
        // Остаток в днях
        Дней = Дней + (ВременнаяДата - Дата2) / 86400;
    КонецЕсли;
КонецПроцедуры

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

Решение 2: Использование функций из ЗУП 3.1

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


Функция ДнейВПериоде(Знач ДатаНачала, Знач ДатаОкончания) Экспорт
    // Возвращаем количество суток между датами, включая дату окончания
    Если НачалоДня(ДатаНачала) > НачалоДня(ДатаОкончания) Тогда
        Возврат 0;
    Иначе
        // Добавляем 1 к разнице, чтобы учесть последний день включительно
        Возврат (КонецДня(ДатаОкончания) + 1 - НачалоДня(ДатаНачала)) / (24 * 60 * 60);
    КонецЕсли;
КонецФункции

Если же ваша задача требует именно строкового представления разности (например, «2 месяца 5 дней»), рекомендуем заглянуть в модуль ОбщегоНазначения или СтроковыеФункцииКлиентСервер вашей конфигурации. В БСП часто встречаются функции, которые уже умеют склонять слова «день», «месяц», «год» в зависимости от числа.

Решение 3: Реализация через запрос

Иногда требуется получить разность дат сразу в выборке данных, не прибегая к перебору в цикле на встроенном языке. В этом случае мы можем использовать конструкцию ВЫБОР и функцию РАЗНОСТЬДАТ. Однако помните, что РАЗНОСТЬДАТ(..., МЕСЯЦ) возвращает количество переходов через границу месяца.

Рассмотрим пример логики, которую можно реализовать в запросе для более точного расчета:


ВЫБРАТЬ
    ВЫБОР
        КОГДА РАЗНОСТЬДАТ(&ДатаНачала, &ДатаКонца, МЕСЯЦ) >= 1
            ТОГДА РАЗНОСТЬДАТ(&ДатаНачала, КОНЕЦПЕРИОДА(&ДатаНачала, МЕСЯЦ), ДЕНЬ)
        ИНАЧЕ РАЗНОСТЬДАТ(&ДатаНачала, &ДатаКонца, ДЕНЬ)
    КОНЕЦ КАК ДниВПервомМесяце,
    РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&ДатаНачала, МЕСЯЦ), СЕКУНДА, 1), 
                НАЧАЛОПЕРИОДА(&ДатаКонца, МЕСЯЦ), МЕСЯЦ) КАК ПолныхМесяцев

Посмотрим на ситуацию критически: этот метод в запросе достаточно громоздкий. Выясним причину: в запросах нет прямого доступа к календарной логике «длины текущего месяца» без соединения с производственным календарем. Поэтому для сложных расчетов лучше вычислять разницу на стороне сервера 1С в коде модуля.

Особенности учета високосного года и 31-го числа

Проанализируем ситуацию с ДобавитьМесяц. Это ключевая функция платформы 1С. Она работает по следующему правилу: если мы добавляем месяц к 31 августа, мы получим 30 сентября (последний день месяца). Если добавим еще месяц — получим 31 октября. Платформа старается сохранять число месяца, но если в целевом месяце такого числа нет, она устанавливает последний день.

Рекомендации для разработчиков:

  1. Всегда уточняйте у заказчика, нужно ли включать дату окончания в расчет. Например, с 1-го по 2-е число — это 1 день или 2 дня?
  2. Если расчет идет для начисления пени или аренды, часто используется «коммерческий» календарь, где каждый месяц равен 30 дням. В этом случае алгоритм упрощается до формулы: (Месяцы * 30) + Дни.
  3. Для кадрового учета (стаж для больничного) используйте только типовые механизмы БСП, так как они соответствуют законодательству РФ.
  4. Используйте НачалоДня() перед любыми вычислениями, чтобы избежать ошибок, связанных с разницей в секундах, если время в датах заполнено произвольно.

Выяснив все нюансы, мы видим, что универсального «идеального» решения не существует, так как оно зависит от контекста задачи. Однако использование кода из УПП (Решение 1) покрывает 90% бизнес-задач, связанных с человекочитаемым представлением разности дат.

← На главную