Разность дат, а как быстрее?

Определение пересечения периодов (числовых множеств)
Супер-пупер быстрая конкатенация(сложение) строк

Я думаю, каждый разработчик сталкивался с вычислением разности дат в днях, минутах или секундах. На платформе 1С существует два способа вычисления разности дат:
  • С помощью языка запросов;
  • С помощью арифметических операций.
Разберем первый способ. Для вычисления разности дат используется стандартная функция языка запросов РАЗНОСТЬДАТ, она имеет три параметра: первый параметр - вычитаемая дата; второй параметр - исходная дата; третий параметр - тип разности, одно из: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, КВАРТАЛ, ГОД. Но помним, что функция рассчитывает календарную разницу между двумя датами, поэтому ее нельзя использовать в местах, где необходимо рассчитать количество банковских или рабочих дней между двумя датами.

Напишем функцию:
Функция ПолучитьРазностьДатВДняхЗапрос(НачалоПериода, КонецПериода)

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РАЗНОСТЬДАТ(&ДатаНачалаПериода, &ДатаОкончанияПериода, ДЕНЬ) КАК Результат";

Запрос.УстановитьПараметр("ДатаНачалаПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("ДатаОкончанияПериода", НачалоДня(КонецПериода));

РезультатЗапроса = Запрос.Выполнить();

ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

КоличествоДней = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
КоличествоДней = ВыборкаДетальныеЗаписи.Результат;
КонецЦикла;

Возврат КоличествоДней;

КонецФункции


Из плюсов данного способа можно отметить: простота реализации, не надо ничего делить и вычитать; третий параметр функции языка запросов, отвечающий за тип разности, можно параметризировать, то есть можно легко получить разность дат не только в днях, но и в минутах, часах и так далее.
Из минусов стоит отметить исполнение функции только на стороне сервера, так как используется объект Запрос.

Во втором способе нужно уметь делить и знать сколько секунд в минуте, минут в часах и так далее. Если на платформе 1С произвести вычитание над датами, то получим целое число - это разница между датами в секундах. Если исходная дата больше чем вычитаемая, то получим положительное количество секунд, если исходная дата меньше чем вычитаемая, то отрицательное количество секунд.
Напишем функцию, которая будет вычислять разность между двумя датами в днях:
Функция ПолучитьРазностьДатВДнях(НачалоПериода, КонецПериода)

РазностьВСекундах = НачалоДня(КонецПериода) - НачалоДня(НачалоПериода);

КоличествоДней = Окр(РазностьВСекундах / 60 / 60 / 24);

Возврат КоличествоДней;

КонецФункции

Хочу заметить, что как и в первом способе, рассчитывается календарная разница между датами. Из плюсов хочу отметить исполнение функции в любом контексте, из минусов - для параметризации типа разности придется написать сочинение из условий или как-то так.

На практике мне встретился тот факт, что программисты предпочитают пользоваться первым способом, и я так же последовал этому мейнстриму и применил первый способ в одном из тяжелых алгоритмов, где вычисление разницы между двумя датами в днях выполнялось тысячи раз.

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

Привожу демонстрацию замера производительности системы при вычислении разница дат обоими способами, выполнив 10000 раз (см. рисунок 1).
Рисунок 1. Замер производительности вычисления разности дат
Как видите, почти 70% от общего времени уходит на первый способ, и 30% на второй. Будьте внимательны, и проверяйте то, что советуют на просторах интернета.

Комментарии

  1. В первом способе идет обращение к кластеру серверов, после чего идет преобразование запроса для СУБД и его выполнение. Во втором идет элементарный расчет средствами сервера. Результат закономерен и он будет еще более очевидным если разнести Кластер и СУБД по разным машинам.

    ОтветитьУдалить
    Ответы
    1. Спасибо за ваши разъяснения. Теперь буду учитывать этот момент при разработке!

      Удалить
  2. "из минусов - для параметризации типа разности придется написать сочинение из условий или как-то так."

    Вовсе, нет, вот функция, которая работает как РазностьДат() в запросе, возможно что-то подобное есть и в БСП.

    Функция РазностьДат(Дата1, Дата2, Период) Экспорт
    Шаг = Новый Структура("Год, Квартал, Месяц, Неделя, День, Час, Минута, Секунда", 12, 3, 1, -604800, -86400, -3600, -60, -1);
    Возврат Цел(?(Шаг[Период] > 0, Год(Дата2) * 12 + Месяц(Дата2) - 1, ’00010101′ - Дата2) / Шаг[Период])
    - Цел(?(Шаг[Период] > 0, Год(Дата1) * 12 + Месяц(Дата1) - 1, ’00010101′ - Дата1) / Шаг[Период])
    КонецФункции

    ОтветитьУдалить
    Ответы
    1. Какое просто решение, использовать структуру с предопределенными элементами для параметризации типа разности, спасибо за подсказку!

      Удалить
  3. Запрос в цикле - не лучший способ реализации. Я вычисляю даты запросом, один раз, если мне нужен один заковыристый результат, а если есть множество, то я все делаю в запросе и циклю только исходную выборку или таблицу. Никаких запросов в цикле.

    ОтветитьУдалить

Отправить комментарий