Часто в процессе разработки возникает базовая, казалось бы, задача — получить количество календарных дней между датой начала и датой окончания определенного периода. Это может понадобиться для расчета сроков аренды, вычисления трудового стажа, начисления пени по договорам и во многих других ситуациях. Давайте разберем по шагам, какие способы решения этой задачи существуют в платформе 1С:Предприятие, и проанализируем подводные камни каждого из подходов.
Рассмотрим самый простой и быстрый способ вычисления в коде 1С. Мы знаем, что базовая единица измерения типа Дата в системе 1С — это секунда. Соответственно, если мы вычтем из одной даты другую, результатом будет количество секунд, прошедших между ними.
В одних сутках ровно 86 400 секунд (60 секунд * 60 минут * 24 часа). Поэтому базовая формула выглядит так:
КоличествоДней = (ДатаОкончания - ДатаНачала) / 86400;
Однако здесь кроется серьезная ошибка начинающих разработчиков. Если переменная ДатаНачала имеет время 23:59:59, а ДатаОкончания содержит время 00:00:00 следующего дня, то математическая разница составит всего 1 секунду! При делении на 86400 мы получим дробное число, близкое к нулю. Если система отбросит дробную часть, результат будет равен 0 дней, хотя календарно сутки сменились.
Посмотрим на пример правильного подхода, который защитит нас от неточностей, связанных со временем. Мы всегда должны приводить обе даты к началу дня с помощью функции НачалоДня(). Рассмотрим надежный алгоритм расчета:
ВремяВСекундах = НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала);
КоличествоДней = ВремяВСекундах / 86400;
Либо можно записать это классической формулой в одну строку:
РазницаВДнях = (НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / (60 * 60 * 24);
Этот метод работает мгновенно и отлично подходит, если расчет производится для одной или нескольких переменных на клиенте или сервере.
Если перед нами стоит задача посчитать разницу в днях для большого массива данных (например, в динамическом списке, при обработке тысяч строк из табличной части или в сложном отчете), использование запроса — лучший выбор. Давайте проанализируем ситуацию: если мы будем перебирать 100 000 строк в цикле и вычислять разницу в коде, это займет значительно больше времени, чем если бы мы поручили эту работу СУБД.
В языке запросов 1С для этого существует специальная функция РАЗНОСТЬДАТ. Разберем пример реализации:
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
| РАЗНОСТЬДАТ(&ДатаН, &ДатаК, ДЕНЬ) КАК КоличествоДней";
Запрос.УстановитьПараметр("ДатаН", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаК", КонецДня(ДатаОкончания));
Рез = Запрос.Выполнить();
Если Не Рез.Пустой() Тогда
Выборка = Рез.Выбрать();
Выборка.Следующий();
РезультатДней = Выборка.КоличествоДней;
КонецЕсли;
Важный нюанс: функция РАЗНОСТЬДАТ в 1С транслируется напрямую в СУБД (например, в DATEDIFF для MS SQL Server). Она работает по принципу подсчета "переходов" через полночь. Если сравнить 31 декабря 23:59:59 и 1 января 00:00:01, запрос вернет ровно 1 день разницы. Несмотря на это, хорошим тоном остается предварительная нормализация дат передаваемых параметров (например, через НачалоДня), чтобы избежать неожиданного поведения при сложных группировках.
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП) — а это ERP, КА, Бухгалтерия предприятия 3.0, ЗУП и другие современные решения — мы настоятельно рекомендуем использовать готовые программные интерфейсы.
Рассмотрим модуль КалендарныеГрафики. В нем содержатся готовые функции, которые позволяют не только вычислять календарные дни, но и учитывать рабочие графики, праздники и переносы выходных дней. Использование стандартизированных процедур БСП избавляет вас от необходимости писать "велосипеды" и гарантирует корректную работу кода в любых условиях конфигурации.
Выясним причину частых споров между бухгалтерами/кадровиками и программистами при расчете дат. Проблема кроется в логике бизнес-процесса.
(Дата2 - Дата1). Если вы взяли в аренду оборудование 1-го числа в 12:00 и вернули 2-го числа в 12:00, прошли ровно сутки (1 день).Для включительного расчета, который часто используется при вычислении трудового стажа по правилам ПФР, необходимо к математической разности всегда прибавлять единицу. Посмотрим на пример кода для такого случая:
// Прибавляем 1 день, чтобы учесть и день начала, и день окончания периода
КоличествоДнейВключительно = Цел((НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / (60 * 60 * 24)) + 1;
Многие начинающие программисты задаются вопросом: а как алгоритмы 1С учитывают високосные годы (29 февраля)? Ответ прост: тип данных Дата в 1С основан на стандартном григорианском календаре. Платформа 1С изначально знает, сколько дней в каждом конкретном месяце и году.
При использовании как математического вычитания (деления на 86400), так и функции запроса РАЗНОСТЬДАТ, високосный год учитывается абсолютно автоматически. Вам не нужно писать дополнительные условия для проверки года на високосность.
Подводя итог, можно с уверенностью сказать: всегда выбирайте инструмент в зависимости от контекста. Для быстрых локальных расчетов используйте вычитание дат с применением НачалоДня(), для массовой обработки данных в таблицах используйте язык запросов, а при сложной бизнес-логике с учетом рабочих дней опирайтесь на функции Библиотеки стандартных подсистем.