При построении отчетов, группировке интервалов и подготовке аналитических выборок нередко возникает задача округлить дату до целого часа по правилам математического округления: если количество минут равно или превышает 30, округляем в большую сторону (к началу следующего часа), а если меньше 30 — отбрасываем минуты и округляем в меньшую сторону (к началу текущего часа).
Разберем различные подходы к решению этой задачи на языке запросов 1С:Предприятие, а также рассмотрим особенности обработки дат во встроенном языке.
Проанализируем базовый вариант, предложенный на форуме. В нем используется ветвление с помощью оператора ВЫБОР и стандартных функций работы с датами:
ВЫБОР
КОГДА МИНУТА(&Дата) >= 30
ТОГДА ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&Дата, ЧАС), СЕКУНДА, 1)
ИНАЧЕ НАЧАЛОПЕРИОДА(&Дата, ЧАС)
КОНЕЦ
Разберем по шагам логику работы данного выражения:
МИНУТА(&Дата) извлекает текущее значение минут (от 0 до 59).КОНЕЦПЕРИОДА(&Дата, ЧАС) (получаем значение вида 01:59:59).ДОБАВИТЬКДАТЕ(..., СЕКУНДА, 1), что дает ровно 02:00:00.ИНАЧЕ, где функция НАЧАЛОПЕРИОДА(&Дата, ЧАС) отсекает минуты и секунды, возвращая 01:00:00.Рассмотрим более компактный и производительный способ, не требующий ветвления CASE WHEN на стороне СУБД. Суть метода заключается в предварительном сдвиге исходной даты вперед на половину целевого интервала (на 30 минут) с последующим получением начала часа:
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Дата, МИНУТА, 30), ЧАС)
Посмотрим на пример работы этого алгоритма:
01:29:59, после добавления 30 минут мы получаем 01:59:59. Функция НАЧАЛОПЕРИОДА(..., ЧАС) возвращает 01:00:00 (округление вниз).01:30:00, после сдвига получается 02:00:00. Функция НАЧАЛОПЕРИОДА вернет 02:00:00 (округление вверх).01:45:00, после прибавления 30 минут получаем 02:15:00, и начало часа даст 02:00:00.Этот вариант короче в написании, легче читается и эффективнее выполняется сервером баз данных.
Если требуется округлить дату не строго до часа, а до произвольного шага (например, 15, 30 минут или 2 часа), используем пересчет разницы секунд относительно фиксированной базовой даты:
ДОБАВИТЬКДАТЕ(
ДАТАВРЕМЯ(2000, 1, 1),
СЕКУНДА,
ВЫРАЗИТЬ((РАЗНОСТЬДАТ(ДАТАВРЕМЯ(2000, 1, 1), &Дата, СЕКУНДА) + 1800) / 3600 КАК ЧИСЛО(15, 0)) * 3600
)
В этой формуле число 3600 — это размер интервала в секундах (1 час), а 1800 — половина этого интервала (30 минут). Для шага в 15 минут достаточно заменить эти коэффициенты на 900 и 450 соответственно.
При работе с операциями над датами в запросах необходимо учитывать следующие особенности платформы 1С:
ДОБАВИТЬКДАТЕ и КОНЕЦПЕРИОДА платформы корректно обрабатывают переход между днями (например, с 23:40 на 00:00 следующего дня) и смену календарного года.МИНУТА(&Дата) >= 30 секунды игнорируются. Если требуется строго учитывать время вплоть до секунды (например, 01:29:59), метод со смещением ДОБАВИТЬКДАТЕ(&Дата, МИНУТА, 30) или сдвиг по секундам работает математически точнее.31.12.3999 23:59:59. Прибавление минут к граничным датам может приводить к ошибке переполнения.Если нет жесткой необходимости выполнять группировку или фильтрацию непосредственно в тексте запроса, дату можно округлить во встроенном языке 1С при постобработке данных. Во встроенном языке дата поддерживает прямое сложение с количеством секунд:
ОкругленнаяДата = НачалоЧаса(ИсходнаяДата + 1800);
Здесь к значению переменной ИсходнаяДата прибавляется 1800 секунд (30 минут), а затем функция НачалоЧаса() отсекает лишние минуты и секунды, возвращая требуемый результат.