Новая версия функции рассчета рабочих часов между двумя датами с учетом бизнес-календаря. Предыдущие версии содержали ошибку.
Итак напомню.
Бизнес календарь имеет 3 секции (лучше посмотреть в CardManager)
1. Основная информация (только одно поле Name, понятно для чего оно нужно)
2. Года (Years). Имеет отдно поле Year в котором указывается год
2.1 Подсекция Days. Имеет поля Day - номер дня от начала года и Type тип
2.1.1 Подсекция WorkTime. Поля StartTime и EndTime - временной диапазон
3. DefaultTimeSettings. Поля StartTime и EndTime - временной диапазон
В календаре можно делать несколько настроек.
1. Настрока рабочего времени по умолчанию.
Данная настройка будет храниться в секции DefaultTimeSettings. Строки секции будут содержать временные интервалы.
2. Настройка конкретного дня.
В этом случае будет создана строка в секции Years, в подсекции Days, в подсекции WorkTime с указанием интервала.
Например, 02.01.2009 должен быть рабочим днем, с рабочим времением с 12:00 до 15:00.
При сохранении данные будут хранится следующим образом (названия секций в квадратных скобках):
[Years]: Year = 2009
[Days]: Day = 2
[WorkTime]: StartTime=12:00, EndTime=15:00
3. Настройка календаря по умолчанию
Данные настройки будут храниться в той же секции Years (аналогично п.2), но в году 1796.
Если для конкретного дня нет сохраненных настроек, берется следующее рабочее время:
Рабочие дни: с понедельника по пятницу
Рабочий график: с 9:00 до 13:00, и с 14:00 до 18:00
Скрипт состоит из 3х функций
1. Проверка, является ли данный день рабочим FIsWorkTime
2. Ф-ция рассчета длительности рабочего времени для одного дня FGetDayDuration
3. Основная ф-ция рассчета длительности между двумя датами FBusinessHours2
Алгоритм следующий.
Ф-ция FBusinessHours2 вычисляет при помощи FGetDayDuration длительность рабочего времени в первом дне интервала (до конца первого рабочего дня). Длительность рабочего времени в последнем дне интервала (от начала рабочего дня до конечного времени), и длительность во всех днях между начальным и конечным.
Поскольку данные хранятся для конкретных дней, а не для всех, то единственным способом подсчета является перебор всех дней в цикле. Это крайне не оптимальный способ. Альтернативным вариантом было бы периодическое создание "карты" рабочего расписания для абсолютно всех дней в интервале, с хранением времени в UTC в отдельной таблице. Тогда расчет длительности можно будет выполнить несколькими SELECT'ами за доли секунды.
Ф-ция FBusinessHours2 может возвращать данные в минутах или часах в зависимости от 3-го параметра @Mode.
В чем же была ошибка?
При рассчете диапазона неучитывался високосный год, из-за этого все даты после февраля были смещены.
В скрипт добавлена новая функция, которая учитывает данный сдвиг.
Скачать скрипт
Замечательный скрипт.
ОтветитьУдалитьПри его использовании следует иметь в виду, что конечная дата не должна быть больше начальной, иначе будет неправильный расчет. Можно добавить внутрь проверку и поменять даты местами. Можно также поставить знак минус, чтобы подчеркнуть, что это отрицательное значение. Но это уже исходя из конкретной задачи.
Есть несколько замечаний к этому скрипту:
ОтветитьУдалить1. Если не использовать SET DATEFIRST 1 перед вызовом функции, то результат не всегда верный
2. У скрипта есть явные проблемы с расчетом длительности, если дата начала и дата конца находятся в разных годах. К примеру, стандартный календарь, без всяких настроек. Нужно рассчитать длительность с 2009-12-28 09:00:00 до 2010-01-04 20:00:00. Всего 6 полных рабочих дней, но скрипт возвращает результат 960 минут или 16 часов, вместо 48 часов.
Если расчет длительности между годами не важен, можно его использовать с учетом п.1
Огромная просьба к выкладываемым функциям. Можно ли прикладывать также пример скрипта для вызова функции? Тогда проблем с SET DATEFIRST 1 вообще не будет.
ОтветитьУдалитьСпасибо