При разработке в среде 1С:Предприятие 8.3 часто возникает задача вычисления разницы между двумя датами и вывода полученного интервала в удобном для пользователя текстовом формате (например, «49 час. 23 мин. 12 сек.»). Это критически важно для систем учета рабочего времени, расчетов трудозатрат программистов, фиксации времени доставки или простоя оборудования. В этой статье мы подробно разберем, как решить эту задачу, избежав типичных ошибок переполнения, рассмотрим использование готовых системных функций и коснемся правильного проектирования бизнес-логики.
Самый простой и надежный способ — это прямая работа с секундами. В платформе 1С разность двух значений типа Дата возвращает количество секунд. Мы можем легко перевести эти секунды в часы, минуты и секунды, используя целочисленное деление и получение остатка от деления.
Рассмотрим по шагам формулу, предложенную в начале обсуждения, и оптимизируем её. В исходном сообщении автор использовал громоздкие вычитания. Мы можем существенно упростить код, применив оператор остатка от деления %:
// Исходные данные
ДатаНачала = '20190101122431'; // 01.01.2019 12:24:31
ДатаОкончания = '20190102090347'; // 02.01.2019 09:03:47
РазностьВСекундах = ДатаОкончания - ДатаНачала;
// Выполняем математический расчет
Часов = Цел(РазностьВСекундах / 3600);
Минут = Цел((РазностьВСекундах % 3600) / 60);
Секунд = РазностьВСекундах % 60;
// Формируем результирующую строку
ВремяФормат = "" + Часов + " час. " + Минут + " мин. " + Секунд + " сек.";
Этот алгоритм работает безупречно при любой разности дат, будь то 5 минут или 5000 часов. В нем нет ограничений на переполнение разрядности времени, так как мы не привязываемся к структуре календаря.
Некоторые разработчики пытаются использовать свойства объекта Дата для получения нужных составляющих. Идея заключается в том, чтобы прибавить полученную разность секунд к «пустой» дате (началу времен) и затем получить часы, минуты и секунды встроенными функциями платформы:
// Получаем секунды
ВремяСек = ДатаОкончания - ДатаНачала;
_Дата = Дата(1, 1, 1) + ВремяСек;
// Пробуем получить представление
ВремяФормат = "" + Час(_Дата) + " час. " + Минута(_Дата) + " мин. " + Секунда(_Дата) + " сек.";
В чем главная проблема этого метода? Он работает корректно только в том случае, если разность дат не превышает 24 часа. Если разность составит, например, 26 часов, то функция Час(_Дата) вернет 2 (так как время перейдет на следующий день — 2 января 0001 года).
В Сообщении 10 форума была предложена формула для обхода этого лимита:
ВремяФормат = "" + ((День(_Дата)-1) * 24 + Час(_Дата)) + " час. " + ...
Проанализируем эту формулу детально. На первый взгляд она решает проблему: мы берем день месяца, вычитаем единицу (так как отсчет дней начинается с 1 января), умножаем на 24 часа и прибавляем часы текущего дня. Однако здесь кроется критическая ошибка, которая проявится при длительных интервалах!
Функция День(_Дата) возвращает день месяца (число от 1 до 31). Как только разность дат превысит 31 день, дата «перевалит» на февраль (01.02.0001). При этом функция День(_Дата) снова вернет 1! В результате расчетов мы безвозвратно потеряем 31 день (744 часа). Поэтому использовать объект Дата для расчета произвольных интервалов времени напрямую через свойства дней и часов категорически не рекомендуется.
Если вы работаете в любой современной типовой конфигурации 1С (такой как УТ, ERP, КА, ЗУП, УНФ), вам не нужно писать собственные алгоритмы для форматирования интервалов. В составе Библиотеки стандартных подсистем (БСП) уже есть готовые функции, которые сделают всю работу за вас и даже красиво склоняют слова.
Рассмотрим обращение к общему модулю БСП НапоминанияПользователяКлиентСервер. Функция ПредставлениеВремени идеально решает нашу задачу:
РазностьВСекундах = ДатаОкончания - ДатаНачала;
// Параметры: Секунды, ВыводитьДни, ВыводитьСекунды
СтрокаВремени = НапоминанияПользователяКлиентСервер.ПредставлениеВремени(РазностьВСекундах, Истина, Истина);
// Результат будет выглядеть так: "1 день 20 часов 39 минут 16 секунд"
Использование БСП делает ваш код чище, надежнее и гарантирует, что интерфейс программы будет выглядеть профессионально.
Если вам нужно вывести разность дат в отчете, подготовленном с помощью Системы компоновки данных (СКД), делать расчет строкового представления в самом запросе крайне неэффективно. В языке запросов 1С нет полноценной математической функции вычисления остатка от деления для дробных чисел, и запрос превратится в нагромождение условий.
Проанализируем правильный подход:
РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, СЕКУНДА).МойОбщийМодуль.ФорматироватьСекунды(ВычисляемоеПоле).Таким образом, в базе данных у вас будут храниться точные числовые значения секунд, по которым СКД сможет легко подводить итоги (суммировать время работы по проектам), а конечный пользователь на экране увидит красивую понятную строку времени.
Как справедливо заметили участники обсуждения на форуме, простое математическое вычитание ДатаОкончания - ДатаНачала в реальных задачах автоматизации учета практически никогда не отражает действительность.
Представьте ситуацию: программист взял задачу в работу в пятницу в 17:00, а закрыл её в понедельник в 10:00. Физическая разница составляет 65 часов. Но работал ли он все эти 65 часов? Конечно нет. По стандартному рабочему графику (с 9:00 до 18:00, за вычетом выходных) чистое время работы составило всего 2 часа (1 час в пятницу и 1 час в понедельник).
Чтобы реализовать корректный учет рабочего времени в 1С, вам потребуется задействовать следующие механизмы:
Применяя данные подходы, вы сможете построить точную и справедливую систему учета рабочего времени, которая автоматизирует расчеты с клиентами и сотрудниками без ошибок и погрешностей.