Как рассчитать рабочее время между двумя датами с учетом графика в 1С 7.7?

Программист 1С v7.7 Управленческий учет IT и автоматизация бизнеса
← На главную

При автоматизации учета выполненных работ, заявок в службу поддержки или производственных процессов часто возникает задача: рассчитать реальное затраченное рабочее время между датой/временем начала и датой/временем окончания. При этом необходимо исключить нерабочие ночные часы, обеденные перерывы и выходные дни (субботу и воскресенье).

Разберем особенности платформы 1С:Предприятие 7.7 и пошагово рассмотрим алгоритмы решения этой задачи.

Особенности работы с датой и временем в 1С:Предприятии 7.7

В отличие от платформы 1С 8.x, где тип Дата содержит и дату, и время, а разность двух дат возвращает секунды, в платформе 1С 7.7 реализована иная модель:

  1. Тип Дата хранит исключительно календарный день. Разность Дата2 - Дата1 возвращает количество календарных дней в виде целого числа.
  2. Время хранится отдельно: либо в виде строки формата "ЧЧ:ММ:СС", либо числом (секунды от полуночи).
  3. Для определения дня недели применяется встроенная функция НомерДняНедели(Дата), которая возвращает число от 1 (понедельник) до 7 (воскресенье).

Для математических расчетов строковое время необходимо предварительно перевести в секунды (или часы). Посмотрим на вспомогательную функцию преобразования:


Функция ВремяВСтраницуСекунд(СтрВремя)
    Если ПустаяСтрока(СтрВремя) = 1 Тогда
        Возврат 0;
    КонецЕсли;
    Часы = Число(Лев(СтрВремя, 2));
    Минуты = Число(Сред(СтрВремя, 4, 2));
    Секунды = Число(Прав(СтрВремя, 2));
    Возврат Часы * 3600 + Минуты * 60 + Секунды;
КонецФункции

Алгоритм подневного расчета с фильтрацией выходных

Рассмотрим универсальный и надежный способ расчета — подневный обход периода в цикле. Данный подход исключает ошибки при переходе через выходные дни и позволяет гибко учитывать границы смен.

Разберем логику работы алгоритма по шагам:

  1. Задаем константы рабочего дня: время начала смены (например, 09:00:00) и время окончания (например, 18:00:00).
  2. Нормализуем входящее время: если старт произошел раньше начала рабочего дня, прижимаем его к началу смены; если позже конца смены — рабочий интервал для этого дня будет нулевым.
  3. Запускаем цикл по всем дням от даты начала до даты окончания.
  4. Внутри цикла проверяем день недели через НомерДняНедели(ТекДата). Если день равен 6 или 7, мы пропускаем итерацию.
  5. Если дата начала и окончания совпадают, рассчитываем разницу между временем окончания и временем начала внутри рабочего окна.
  6. Если даты разные: для первого дня берем остаток времени от старта до конца смены, для последнего — от начала смены до времени завершения, а для всех промежуточных рабочих дней начисляем полную длительность смены.

Проанализируем реализацию функции на встроенном языке 1С 7.7:


Функция РассчитатьРабочееВремяВСекундах(ДатаНач, ВремяНачСтр, ДатаКон, ВремяКонСтр)
    Перем ТекДата, ИтогСекунд;
    
    // Задаем границы рабочего дня в секундах (например, с 09:00 до 18:00)
    НачРабДня = ВремяВСтраницуСекунд("09:00:00");
    КонРабДня = ВремяВСтраницуСекунд("18:00:00");
    ДлинаСмены = КонРабДня - НачРабДня;
    
    ВремяНач = ВремяВСтраницуСекунд(ВремяНачСтр);
    ВремяКон = ВремяВСтраницуСекунд(ВремяКонСтр);
    
    // Нормализация времени старта и финиша относительно рабочего окна
    ВремяНач = Макс(ВремяНач, НачРабДня);
    ВремяНач = Мин(ВремяНач, КонРабДня);
    
    ВремяКон = Макс(ВремяКон, НачРабДня);
    ВремяКон = Мин(ВремяКон, КонРабДня);
    
    ИтогСекунд = 0;
    
    // Обработка случая, когда начало и окончание приходятся на один день
    Если ДатаНач = ДатаКон Тогда
        Если НомерДняНедели(ДатаНач) < 6 Тогда
            Если ВремяКон > ВремяНач Тогда
                ИтогСекунд = ВремяКон - ВремяНач;
            КонецЕсли;
        КонецЕсли;
        Возврат ИтогСекунд;
    КонецЕсли;
    
    // Обход интервала дат
    Для ТекДата = ДатаНач По ДатаКон Цикл
        // Пропускаем субботу (6) и воскресенье (7)
        Если НомерДняНедели(ТекДата) >= 6 Тогда
            Продолжить;
        КонецЕсли;
        
        Если ТекДата = ДатаНач Тогда
            // Первый рабочий день: от времени старта до конца смены
            ИтогСекунд = ИтогСекунд + (КонРабДня - ВремяНач);
        ИначеЕсли ТекДата = ДатаКон Тогда
            // Последний рабочий день: от начала смены до времени финиша
            ИтогСекунд = ИтогСекунд + (ВремяКон - НачРабДня);
        Иначе
            // Промежуточный рабочий день: полная смена
            ИтогСекунд = ИтогСекунд + ДлинаСмены;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ИтогСекунд;
КонецФункции

Использование штатных календарей платформы

Если в конфигурации используется компонента «Расчет» (доступна в комплексных конфигурациях и конфигурации «Зарплата и Кадры 7.7»), задачу можно существенно упростить с помощью метаданного объекта Календари.

Объект Календарь хранит информацию о праздниках, предпраздничных днях и официальных переносах выходных. Рассмотрим пример работы с методами календаря:


Календ = СоздатьОбъект("Календарь.Основной");
// Получение количества отработанных дней за период
ВсегоДней = Календ.Дней(ДатаНач, ДатаКон);
// Получение точного количества рабочих часов по графику
ВсегоЧасов = Календ.Часов(ДатаНач, ДатаКон);

Если компонента «Расчет» отсутствует, а в базе нет производственного календаря, базовый подневный цикл с функцией НомерДняНедели() является наиболее стабильным и простым решением, не требующим усложнения структуры метаданных конфигурации.

← На главную