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

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

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

Способ 1: Математический расчет во встроенном языке (через секунды)

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

В одних сутках ровно 86 400 секунд (60 секунд * 60 минут * 24 часа). Поэтому базовая формула выглядит так:

КоличествоДней = (ДатаОкончания - ДатаНачала) / 86400;

Однако здесь кроется серьезная ошибка начинающих разработчиков. Если переменная ДатаНачала имеет время 23:59:59, а ДатаОкончания содержит время 00:00:00 следующего дня, то математическая разница составит всего 1 секунду! При делении на 86400 мы получим дробное число, близкое к нулю. Если система отбросит дробную часть, результат будет равен 0 дней, хотя календарно сутки сменились.

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


ВремяВСекундах = НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала);
КоличествоДней = ВремяВСекундах / 86400;

Либо можно записать это классической формулой в одну строку:


РазницаВДнях = (НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / (60 * 60 * 24);

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

Способ 2: Использование языка запросов (функция РАЗНОСТЬДАТ)

Если перед нами стоит задача посчитать разницу в днях для большого массива данных (например, в динамическом списке, при обработке тысяч строк из табличной части или в сложном отчете), использование запроса — лучший выбор. Давайте проанализируем ситуацию: если мы будем перебирать 100 000 строк в цикле и вычислять разницу в коде, это займет значительно больше времени, чем если бы мы поручили эту работу СУБД.

В языке запросов 1С для этого существует специальная функция РАЗНОСТЬДАТ. Разберем пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = "
    |ВЫБРАТЬ
    |    РАЗНОСТЬДАТ(&ДатаН, &ДатаК, ДЕНЬ) КАК КоличествоДней";
    
Запрос.УстановитьПараметр("ДатаН", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаК", КонецДня(ДатаОкончания));

Рез = Запрос.Выполнить();
Если Не Рез.Пустой() Тогда
    Выборка = Рез.Выбрать();
    Выборка.Следующий();
    РезультатДней = Выборка.КоличествоДней;
КонецЕсли;

Важный нюанс: функция РАЗНОСТЬДАТ в 1С транслируется напрямую в СУБД (например, в DATEDIFF для MS SQL Server). Она работает по принципу подсчета "переходов" через полночь. Если сравнить 31 декабря 23:59:59 и 1 января 00:00:01, запрос вернет ровно 1 день разницы. Несмотря на это, хорошим тоном остается предварительная нормализация дат передаваемых параметров (например, через НачалоДня), чтобы избежать неожиданного поведения при сложных группировках.

Способ 3: Использование готовых функций БСП

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

Рассмотрим модуль КалендарныеГрафики. В нем содержатся готовые функции, которые позволяют не только вычислять календарные дни, но и учитывать рабочие графики, праздники и переносы выходных дней. Использование стандартизированных процедур БСП избавляет вас от необходимости писать "велосипеды" и гарантирует корректную работу кода в любых условиях конфигурации.

Интервальный расчет vs Включительный расчет (Правило "+1")

Выясним причину частых споров между бухгалтерами/кадровиками и программистами при расчете дат. Проблема кроется в логике бизнес-процесса.

  1. Интервальный расчет (Длительность): Математическая разница. Вычисляется по формуле (Дата2 - Дата1). Если вы взяли в аренду оборудование 1-го числа в 12:00 и вернули 2-го числа в 12:00, прошли ровно сутки (1 день).
  2. Включительный расчет: Применяется, когда день начала и день окончания считаются полными рабочими или учетными днями. Если сотрудника приняли на работу 1-го числа, а уволили 2-го числа, он числился в штате 2 дня (и 1-е, и 2-е число).

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


// Прибавляем 1 день, чтобы учесть и день начала, и день окончания периода
КоличествоДнейВключительно = Цел((НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / (60 * 60 * 24)) + 1;

Проблема високосных лет и длины месяцев

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

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

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

← На главную