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

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

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

В чем заключается типичная ошибка при расчете интервалов?

Проанализируем классическую ситуацию: необходимо рассчитать время между 26.01.2017 11:25:00 и 26.01.2017 15:15:00. Фактически между этими событиями прошло 3 часа и 50 минут.

Если мы используем функцию запроса РАЗНОСТЬДАТ(..., МИНУТА), то получим 230 минут. При попытке перевести это значение в часы простым делением на 60 (230 / 60) получается результат 3,833333.... При округлении такого числа платформа возвращает 4 часа, а если попытаться записать результат в числовое поле как «3.50», мы смешиваем десятичную и шестидесятиричную системы счисления:

  1. Десятичная дробь: 3,5 часа — это ровно 3 часа и 30 минут (половина часа).
  2. Шестидесятиричная система: 3 часа 50 минут — это 3 + 50/60 = 3,833 часа.

Важное предостережение: никогда не храните и не выводите время в числовом виде с точкой (например, 3.50 как обозначение «3 часа 50 минут»). Такие числа невозможно корректно складывать: при суммировании 3.50 и 1.20 система вернет 4.70, тогда как правильная сумма длительностей составляет 5 часов и 10 минут. Для отображения времени пользователю всегда следует использовать строковый формат ("03:50"), а для расчетов — хранить точное количество минут или секунд.

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

Способ 1. Расчет и форматирование на встроенном языке

Если расчет выполняется в модуле объекта, менеджера или формы, наиболее точным и простым подходом является прямое вычитание дат. Разность двух значений типа Дата в 1С дает число секунд.

Разберем по шагам, как получить целое количество часов и остаток минут с помощью целочисленного деления и оператора остатка от деления %:


ДатаНачала = Дата(2017, 01, 26, 11, 25, 00);
ДатаОкончания = Дата(2017, 01, 26, 15, 15, 00);

// Разность дат возвращает длительность в секундах
РазницаВСекундах = ДатаОкончания - ДатаНачала; // 13 800 секунд

// Вычисляем составляющие времени
Часы = Цел(РазницаВСекундах / 3600);
Минуты = Цел((РазницаВСекундах % 3600) / 60);

// Формируем строковое представление с лидирующими нулями
СтрокаВремени = Формат(Часы, "ЧЦ=2; ЧО=0; ЧН=00") + ":" + Формат(Минуты, "ЧЦ=2; ЧО=0; ЧН=00");
// Результат: "03:50"

Способ 2. Применение формата даты для коротких интервалов (до 24 часов)

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


Секунды = ДатаОкончания - ДатаНачала;
ПредставлениеВремени = Формат(Дата(1, 1, 1) + Секунды, "ДФ=""ЧЧ:мм""");

Ограничение метода: этот способ работает только для интервалов менее 24 часов (менее 86 400 секунд). Если продолжительность смены или наряда превысит сутки, дата перейдет на следующий день, а маска "ЧЧ:мм" покажет только остаток часов от текущих суток (например, для 25 часов отобразится 01:00, а целые сутки будут потеряны). Для произвольных интервалов используйте первый способ.

Способ 3. Вычисление часов и минут в языке запросов

В языке запросов 1С отсутствует операция получения остатка от деления (%), а функция ВЫРАЗИТЬ(Число КАК ЧИСЛО) выполняет банковское округление до ближайшего четного, а не отбрасывание дробной части. Чтобы получить целое число часов (аналог функции Цел() для положительных чисел), из частного вычитают 0.5 перед округлением.

Посмотрим на пример запроса, вычисляющего общее число минут, целые часы и остаток минут:


ВЫБРАТЬ
    Наряд.Ссылка КАК Наряд,
    РАЗНОСТЬДАТ(Наряд.ВремяНачалаФактическое, Наряд.ВремяЗавершенияФактическое, МИНУТА) КАК ВсегоМинут,
    ВЫРАЗИТЬ((РАЗНОСТЬДАТ(Наряд.ВремяНачалаФактическое, Наряд.ВремяЗавершенияФактическое, МИНУТА) / 60) - 0.5 КАК ЧИСЛО(10, 0)) КАК ЦелыхЧасов,
    РАЗНОСТЬДАТ(Наряд.ВремяНачалаФактическое, Наряд.ВремяЗавершенияФактическое, МИНУТА) - 
        ВЫРАЗИТЬ((РАЗНОСТЬДАТ(Наряд.ВремяНачалаФактическое, Наряд.ВремяЗавершенияФактическое, МИНУТА) / 60) - 0.5 КАК ЧИСЛО(10, 0)) * 60 КАК ОстатокМинут
ИЗ
    Документ.НарядНаПроизводство КАК Наряд

Полученные поля можно вывести в отчет или объединить в строку на уровне СКД или постобработки результата запроса.

Способ 4. Реализация в Системе компоновки данных (СКД)

В отчетах на СКД разделять логику хранения данных и пользовательского представления удобнее всего через механизмы компоновки:

  1. Вычисляемые поля: в выражениях СКД доступна функция Цел(). Если в запросе выбрана разность в минутах (ВсегоМинут), то в вычисляемом поле можно задать выражение:

    Строка(Цел(ВсегоМинут / 60)) + ":" + Формат(ВсегоМинут - Цел(ВсегоМинут / 60) * 60, "ЧЦ=2; ЧО=0; ЧН=00")

  2. Выражение представления: поле ресурса в запросе можно оставить числом (например, секунды или минуты), чтобы платформа могла автоматически рассчитывать правильные итоги по группировкам. А в поле «Выражение представления» вызвать экспортную функцию общего модуля, которая превратит число секунд в строку вида "3 ч 50 мин". Это сохранит возможность подведения математически верных итогов в отчете.

Если в конфигурации используется Библиотека стандартных подсистем (БСП), для таких преобразований рекомендуется вызывать готовые методы работы со строками интервалов времени (например, функции из общего модуля СтроковыеФункцииКлиентСервер), которые корректно учитывают переход через 24 часа и формируют читаемое представление длительности.

← На главную