Как в 1С автоматически рассчитать количество дней и ночей между двумя датами с учетом времени?

Программист Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

При разработке систем учета на проходных, парковках или в гостиничных комплексах часто возникает задача раздельного подсчета времени нахождения объекта на территории. В частности, требуется определить, сколько "дневных" и сколько "ночных" смен провел посетитель на объекте. В 1С:Предприятие работа с датами имеет свои особенности, которые мы подробно разберем в этой статье.

Постановка задачи и анализ исходных данных

Рассмотрим классическую ситуацию: у нас есть документ, в котором пользователь указывает дату и время въезда, а также дату и время выезда. Наша цель — автоматически заполнить поля КолвоДней и КолвоНочей по следующим правилам:

  1. День: период с 08:00 до 20:00.
  2. Ночь: период с 20:01 до 07:59.

Проанализируем пример: если заезд осуществлен 15.02.2024 в 08:00, а выезд 16.02.2024 в 23:00, система должна зафиксировать 2 дня и 2 ночи. Основная сложность здесь заключается в том, что тип Дата в 1С всегда содержит и дату, и время одновременно, а арифметические операции с ними возвращают результат в секундах.

Способ 1. Реализация через цикл и проверку интервалов

Для начинающих разработчиков наиболее понятным будет алгоритм "перебора" временных отрезков. Рассмотрим, как можно реализовать этот расчет в обработчике события ПриИзменении для поля времени выезда.

Суть метода заключается в создании цикла, который "шагает" по периодам и проверяет, к какому типу относится текущий отрезок времени. Разберем пример кода:


&НаКлиенте
Процедура ВремяВыездаПриИзменении(Элемент)
    // Обнуляем предыдущие результаты
    Объект.КолвоДней = 0;
    Объект.КолвоНочей = 0;
    
    // Объединяем дату и время в полноценные переменные типа Дата
    // Используем Формат для корректного преобразования
    СтрВъезд = Формат(Объект.ДатаВъезда, "ДФ=ггггММдд") + Формат(Объект.ВремяВъезда, "ДФ=ЧЧммсс");
    Въезд = Дата(СтрВъезд);
    
    СтрВыезд = Формат(Объект.ДатаВыезда, "ДФ=ггггММдд") + Формат(Объект.ВремяВыезда, "ДФ=ЧЧммсс");
    Выезд = Дата(СтрВыезд);
    
    // Проверка на корректность ввода
    Если Выезд < Въезд Тогда
        Сообщить("Время выезда не может быть раньше времени въезда!");
        Возврат;
    КонецЕсли;

    ТекущийМомент = Въезд;
    // Логика перебора периодов
    Пока ТекущийМомент < Выезд Цикл
        ЧасНачала = Час(ТекущийМомент);
        
        // Определяем, день сейчас или ночь (граница 08:00 и 20:00)
        Если ЧасНачала >= 8 И ЧасНачала < 20 Тогда
            Объект.КолвоДней = Объект.КолвоДней + 1;
            // Переходим к следующему периоду (вечеру)
            ТекущийМомент = НачалоДня(ТекущийМомент) + 20 * 3600; 
        Иначе
            Объект.КолвоНочей = Объект.КолвоНочей + 1;
            // Переходим к следующему утру
            Если ЧасНачала >= 20 Тогда
                ТекущийМомент = НачалоДня(ТекущийМомент + 86400) + 8 * 3600;
            Иначе
                ТекущийМомент = НачалоДня(ТекущийМомент) + 8 * 3600;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

В данном примере мы используем функцию НачалоДня() и добавляем к ней количество секунд (3600 секунд в часе), чтобы получить точные границы 08:00 и 20:00. Это позволяет избежать ошибок при переходе через полночь.

Способ 2. Математический расчет без циклов

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

Рассмотрим вариант кода, который вычисляет количество 12-часовых периодов:


// Вспомогательная функция для определения, является ли время дневным
ЗаехалДнем = ?(Час(Въезд) >= 8 И Час(Въезд) < 20, 1, 0);
ВыехалДнем = ?(Час(Выезд) >= 8 И Час(Выезд) < 20, 1, 0);

ВремяСекунд = Выезд - Въезд;
// Вычисляем общее количество 12-часовых блоков
ЧислоПериодов = Цел(ВремяСекунд / (60 * 60 * 12));

Объект.КолвоДней = Цел(ЧислоПериодов / 2) + ЗаехалДнем;
Объект.КолвоНочей = Цел(ЧислоПериодов / 2) + (1 - ЗаехалДнем);

Важный нюанс: при таком подходе нужно отдельно обрабатывать остатки от деления, если время выезда не кратно 12 часам. Проанализируем ситуацию: если машина заехала в 07:00 (ночь) и выехала в 09:00 (день), общая разница всего 2 часа, но по факту она затронула и ночной, и дневной периоды.

Профессиональные рекомендации по разработке

Рассмотрим подробнее несколько правил хорошего тона при написании подобного функционала в 1С:

  1. Вынос логики на сервер: Расчеты лучше производить не в модуле формы &НаКлиенте, а в модуле объекта или общем модуле. Это позволит использовать этот же алгоритм при загрузке данных из внешних систем или в отчетах.
  2. Использование Библиотеки Стандартных Подсистем (БСП): Если ваша конфигурация построена на БСП, посмотрите функции в модуле ОбщегоНазначения. Там есть готовые инструменты для работы с датами.
  3. Тип данных Время: Помните, что если на форме вы разделили Дату и Время на два разных поля, в коде их нужно соединять. В 1С поле с типом Дата (составом "Время") на самом деле хранит дату 01.01.0001 плюс указанное время. Чтобы получить корректную дату со временем, используйте конструкцию: Объект.ДатаВъезда + (Объект.ВремяВъезда - Дата(1,1,1)).
  4. Граничные условия: Обращайте внимание на строгость неравенств. Что считать моментом 20:00:00? Это конец дня или начало ночи? В коде лучше использовать >= для начала периода и < для конца.

Обработка ошибок ввода

Выясним, какие ошибки чаще всего допускают пользователи. Самая частая — ввод времени выезда раньше времени въезда. Всегда добавляйте проверку в начале процедуры. Также полезно использовать функцию ЗначениеЗаполнено(), чтобы не пытаться производить арифметические действия с пустыми датами, что приведет к ошибке исполнения кода.

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

← На главную