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

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

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

Математический подход: вычисление через разницу секунд

В платформе 1С тип данных Дата хранит в себе как календарное число, так и время с точностью до секунды. Математически, при вычитании одной даты из другой, мы получаем результат в секундах. Чтобы перевести это значение в сутки, мы делим полученное число на 86 400 (количество секунд в 24 часах: 60 * 60 * 24).

Проанализируем классическую формулу, которую часто можно встретить в коде:

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

Рассмотрим подробнее, почему важно использовать НачалоДня и КонецДня. Если мы просто вычтем одну дату из другой без приведения к границам суток, результат будет зависеть от времени, указанного в переменных. Например, разница между "01.10.2023 23:00:00" и "02.10.2023 01:00:00" составит всего 2 часа (7200 секунд), хотя по календарю это разные дни. Применение НачалоДня нивелирует влияние времени и позволяет работать с «чистыми» сутками.

Проблема «лишней единицы» или ошибка столба и забора

Выясним причину вечных споров о том, нужно ли добавлять + 1 к результату. В программировании это известно как «fencepost error» (ошибка столба и забора). Представьте забор длиной 10 метров с секциями по 1 метру. Чтобы его построить, вам понадобится 11 столбов. Так же и с датами: всё зависит от того, что именно мы считаем — интервал (дистанцию) или период (события).

  1. Интервал: Сколько суток прошло между 1-м и 2-м числом? Ответ: 1 сутки. Формула: Дата2 - Дата1.
  2. Период (включительно): Сколько календарных дней занимает отпуск с 1-го по 2-е число? Ответ: 2 дня (1-е и 2-е). Формула: (Дата2 - Дата1) + 1.

Если ваша задача — рассчитать длительность отпуска, где дата начала 23.02 и дата окончания 23.02, то математическая разница будет равна 0, но фактически человек отдыхал 1 день. В этом случае добавление единицы обязательно. Рассмотрим правильный вариант расчета длительности периода:


// Вариант 1: Математическое прибавление единицы
КоличествоДней = (НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / 86400 + 1;

// Вариант 2: Использование границы следующего дня
КоличествоДней = (КонецДня(ДатаОкончания) + 1 - НачалоДня(ДатаНачала)) / 86400;

Использование стандартных функций БСП

Разберем, как сделать этот процесс более цивилизованным в 21 веке. Если ваша конфигурация использует Библиотеку стандартных подсистем (БСП), нет необходимости изобретать велосипед и писать формулы с делением на 86400. Посмотрим на функции общего модуля ОбщегоНазначения:

Метод ОбщегоНазначения.РазностьДатВКалендарныхДнях(Дата1, Дата2) берет на себя всю черновую работу. Он автоматически приводит даты к началу дня и возвращает целое количество дней. Однако помните, что он считает именно разницу. Если вам нужно количество дней включительно, результат всё равно придется инкрементировать.

Для кадровых задач, таких как расчет отпусков, проанализируем специализированные методы:


// Если нужно учесть производственный календарь и исключить праздники
КоличествоДней = КалендарныеГрафики.КоличествоДнейВПериоде(ДатаНачала, ДатаОкончания, Календарь);

Разница дат в запросах 1С

Ситуация в языке запросов отличается от встроенного языка. Посмотрим на функцию РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ). Важно понимать, что она работает по принципу подсчета границ. Это означает, что разница между 23:59:59 одного дня и 00:00:01 следующего дня будет равна 1, хотя прошло всего 2 секунды.

Пример использования в запросе:


ВЫБРАТЬ
    РАЗНОСТЬДАТ(&ДатаНачала, &ДатаОкончания, ДЕНЬ) КАК КолВоДней
ИЗ
    Справочник.Номенклатура ГДЕ Ссылка = &Ссылка

Важный нюанс: Если &ДатаОкончания меньше &ДатаНачала, функция вернет отрицательное число. Также помните, что РАЗНОСТЬДАТ в запросе не включает последний день (результат для одинаковых дат будет 0), поэтому для получения длительности периода «включительно» в запросах также часто добавляют + 1.

Почему нельзя использовать функцию ДеньГода()?

Проанализируем популярную ошибку начинающих разработчиков — попытку вычесть значения функции ДеньГода(). Этот подход кажется простым, но он таит в себе две критические опасности:

  1. Смена года: Если одна дата — 31 декабря, а вторая — 1 января, разница ДеньГода(Дата2) - ДеньГода(Дата1) даст отрицательное число (1 - 365 = -364), что абсолютно неверно.
  2. Високосные годы: В разных годах разное количество дней (365 и 366), что делает невозможным простую арифметику между датами разных лет через номер дня в году.

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

Учет специфики бизнес-логики

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

Подведем итоги

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

← На главную