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

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

Иногда при разработке на платформе 1С возникает специфическая задача: нужно получить разность между двумя датами в часах, но при этом считать полные сутки не как 24 часа, а как заданное количество рабочих часов (например, 20 часов). Рассмотрим подробнее, как мы можем реализовать такой алгоритм во встроенном языке и в языке запросов.

Фундаментальные отличия расчетов: код и запрос

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

Математический расчет во встроенном языке (Алгоритм с хвостиками)

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

  1. Определяем границу вторых суток (начало дня первой даты + 24 часа).
  2. Вычисляем количество полных суток между датами.
  3. Считаем остаток часов в первый день.
  4. Считаем остаток часов в последний день.

Посмотрим на пример реализации данного алгоритма:


Дата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-часовые рабочие дни), платформа сама рассчитает корректную разницу в рабочих днях и часах без необходимости писать сложные математические алгоритмы.

← На главную