Как в запросе 1С преобразовать секунды в часы и минуты
Скорее всего, каждый профессиональный разработчик 1С сталкивался с ситуацией, когда заказчик хочет необычное, нестандартное и удобное решение задачи. Ответы на многие вопросы можно найти в Интернете. Но не на все. И иногда для решения задачи приходится мыслить не совсем стандартно. Ну и нахождение решений для подобных задач повышает статус разработчика в глазах заказчика, повышает самооценку самого разработчика, и, самое приятное, позволяет заработать деньги. В этой статье я поделюсь опытом решения одной такой задачи.
- Описание
- Подробнее
Описание
Есть у меня один клиент. Владеет автосервисом с кучей сервисных постов. Для тех, кто не в курсе, сервисный пост — это оборудованное место, где мастер обслуживает автомобиль. Так вот, захотел однажды этот клиент узнать, сколько времени занимает обслуживание машин на каждом сервисном посту и в целом. Казалось бы, что может быть проще. Есть документ, который фиксирует номер сервисного поста, время поступления авто на сервис и время окончания работ. Бери, пиши несложный запрос, вычисляй разность дат заезда и выезда в секундах, дели на 3600 и получай результат в часах. А не тут то и было. Оказалось, что есть очень много мелких работ, на которые уходит от 2 до 15 минут, и клиенту очень неудобно видеть такие цифры, как, например, 0.1 часа, 0.25 часа и т.п. Ему нужно время в формате ЧЧ:ММ, без дней, чистые часы и минуты. «Хм…», — сказал я и полез почитать умные мысли других людей по этому поводу.
Решение усложнял тот момент, что отчет был написан не на СКД, а использовал известный многим программистам механизм отчета «Список / кросс-таблица», который работает на построителе отчетов. Итак, поиск результатов не принес. Вариантов предлагалось множество, но конкретного рабочего ответа я не получил. Поэтому «ноу-хау» пришлось придумывать самому.
Поскольку мой дорогой клиент хотел видеть время в формате Часы:Минуты, то какое-либо использование даты не подходило банально потому, что в дате не может быть больше 24 часов. Выносить всю кухню из запроса для внешних расчетов тоже очень не хотелось. И я понял, что поможет мне только число с плавающей запятой. Если примем факт, что до запятой мы будем хранить часы, а после запятой будут минуты, то задача очень даже может иметь решение.
Итак, мои «6 шагов к успеху» стали примерно такими:
- Представляем результат в виде числа с плавающей запятой.
- До запятой храним часы, после — минуты.
- Вычисляем часы.
- Вычисляем минуты.
- Складываем часы и минуты в единый результат.
- При выводе результата используем формат числа, а именно настройку «Разделитель дробной части».
Наверное понятно, что самые большие трудности были на 3 и 4 шаге. Функции языка запросов для работы с числами и их преобразования достаточно скудный в 1С. Пришлось много анализировать. Конечно, проще всего было вычислить часы:
ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦКак мы все знаем, функция запроса ВЫРАЗИТЬ() округляет число, чем мы и воспользуемся. Получаем целое число часов. Оно может быть совпасть с реальным, если системе нечего округлять, или быть больше на единицу, когда система выполнила округление. Если произошло округление, то из полученного числа вычитаем единицу, в противном случае берем неизменный результат функции ВЫРАЗИТЬ().
Логично предположить, что количество минут тоже вычисляется аналогичным способом, только уже используя часть запроса с вычислением часов:
ВЫБОР
КОГДА (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) * 60 <= СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
ТОГДА ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦЗатем полученные два числа нужно привести к одному. Для этого объединяем две части путем сложения, но кроме этого, результат второго запроса нужно поделить на 100, чтобы оно перешло в дробную часть. Этот кусок должен размещаться в части вычисления итогов:
ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ +
ВЫБОР
КОГДА (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) * 60 <= СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
ТОГДА ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ / 100И наконец, оформляем результат в коде. Где общий отчет — отчет-объект «Список / кросс-таблица».
ОбщийОтчет.ЗаполнитьПоказатели("ДлительностьРабот", "Длительность работ, Часы:Минуты", Истина, "ЧДЦ=2; ЧРД=:");И в нашем конечном отчете видим такой результат:

Наверняка, что-то похожее можно реализовать и для СКД. И скорее всего на СКД это будет даже проще. Моя публикация не претендует на Нобелевскую премию. Я думаю, что кому-нибудь она окажется полезной, поэтому публикую. Всем удачи.
P.S. В комментарии был задан вопрос «На последнем скрине в первом посту 1:00 и 0:01, дали в сумме 1:02. Этому отчету можно верить?». Отчету можно верить. В тексте статьи я забыл добавить, что в отчете может быть некоторая визуальная погрешность, но только из-за того, что остаток секунд, отбрасывается.
P.P.S. Более краткое и красивое решение предложил ildarovich, за что ему большой плюс и огромная благодарность:
РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, &ДлительностьВСекундах), ЧАС)
+ МИНУТА(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, &ДлительностьВСекундах)) / 100

