Иногда при разработке на платформе 1С возникает специфическая задача: нужно получить разность между двумя датами в часах, но при этом считать полные сутки не как 24 часа, а как заданное количество рабочих часов (например, 20 часов). Рассмотрим подробнее, как мы можем реализовать такой алгоритм во встроенном языке и в языке запросов.
Прежде чем писать код, разберем важный нюанс. Во встроенном языке 1С мы можем напрямую вычитать одну дату из другой — результатом всегда будет разница в секундах. Однако в языке запросов прямое математическое вычитание дат недоступно. Для этого мы должны использовать специализированную функцию РАЗНОСТЬДАТ(). Посмотрим на оба варианта.
Проанализируем ситуацию: нам нужно посчитать полные сутки по 20 часов, а остатки ("хвостики") в начале и конце периода прибавить как есть. Разберем по шагам алгоритм, который отлично подходит для встроенного языка:
Посмотрим на пример реализации данного алгоритма:
Дата1 = Выборка.ДатаВыезда;
Дата2 = Выборка.ДатаВозвращения;
Если НачалоДня(Дата1) = НачалоДня(Дата2) Тогда
// Если события произошли в один день
Результат = (Дата2 - Дата1) / 3600;
Если Результат > 20 Тогда
Результат = 20;
КонецЕсли;
Иначе
ВторойДень = НачалоДня(Дата1) + 86400; // Добавляем 24 часа в секундах
КоличествоСуток = (НачалоДня(Дата2) - ВторойДень) / 86400;
ХвостикВНачале = (ВторойДень - Дата1) / 3600;
ХвостикВКонце = (Дата2 - НачалоДня(Дата2)) / 3600;
Результат = КоличествоСуток * 20 + ХвостикВНачале + ХвостикВКонце;
КонецЕсли;
Теперь выясним причину частых ошибок в запросах. Неочевидная особенность функции РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ) заключается в том, что она отбрасывает часы, минуты и секунды, приводя обе даты к началу дня. Поэтому просто вычесть дни и умножить их на разницу нельзя — мы потеряем точность.
В запросе мы берем разность дат в днях, умножаем на 20 часов, а затем аккуратно добавляем остаток. Если остаток больше 20 часов (что возможно из-за специфики задачи), мы ограничиваем его с помощью конструкции ВЫБОР КОГДА. Посмотрим на пример кода запроса:
РАЗНОСТЬДАТ(&Дата1, &Дата2, ДЕНЬ) * 20 +
ВЫБОР
КОГДА РАЗНОСТЬДАТ(&Дата1, &Дата2, ЧАС) - РАЗНОСТЬДАТ(&Дата1, &Дата2, ДЕНЬ) * 24 > 20
ТОГДА 20
ИНАЧЕ РАЗНОСТЬДАТ(&Дата1, &Дата2, ЧАС) - РАЗНОСТЬДАТ(&Дата1, &Дата2, ДЕНЬ) * 24
КОНЕЦ КАК ЧасыВМарсианскихСутках
Часто полученный результат нужно вывести в удобном для пользователя виде. Рассмотрим полезную функцию, которая переводит разность в секундах в читаемый формат с учетом 20-часового дня (где в сутках 72 000 секунд вместо 86 400):
Функция РазностьДатПрописью(Дата1, Дата2)
Если Не ЗначениеЗаполнено(Дата1) Или Не ЗначениеЗаполнено(Дата2) Тогда
Возврат "Некорректная дата";
КонецЕсли;
РазностьДат = Макс(Дата1, Дата2) - Мин(Дата1, Дата2);
// Для 20-часовых суток используем 72000 секунд вместо 86400
КоличествоДней = Цел(РазностьДат / 72000);
Если РазностьДат % 72000 > 0 Тогда
РазностьДат = РазностьДат - (КоличествоДней * 72000);
КоличествоЧасов = Цел(РазностьДат / 3600);
Если РазностьДат % 3600 > 0 Тогда
РазностьДат = РазностьДат - (КоличествоЧасов * 3600);
КоличествоМинут = Цел(РазностьДат / 60);
Если РазностьДат % 60 > 0 Тогда
КоличествоСекунд = РазностьДат - (КоличествоМинут * 60);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат СтрШаблон("%1 дн. %2 ч. %3 мин. %4 сек.", КоличествоДней, КоличествоЧасов, КоличествоМинут, КоличествоСекунд);
КонецФункции
Важное замечание: изобретать математические "неземные" сутки в коде допустимо только для простых или специфических задач. Если мы говорим об учете рабочего времени, смен, исключении обедов, выходных и праздников, жесткие формулы неизбежно приведут к ошибкам.
В реальной практике программирования на 1С мы всегда используем регистры сведений, такие как ДанныеПроизводственногоКалендаря или регистры графиков работы. В типовых конфигурациях на базе БСП (Библиотека стандартных подсистем) достаточно использовать общий модуль КалендарныеГрафики. Обратившись к функции РазностьДатПоКалендарю() и передав в нее нужный график (в котором настроены 20-часовые рабочие дни), платформа сама рассчитает корректную разницу в рабочих днях и часах без необходимости писать сложные математические алгоритмы.