При разработке систем учета на проходных, парковках или в гостиничных комплексах часто возникает задача раздельного подсчета времени нахождения объекта на территории. В частности, требуется определить, сколько "дневных" и сколько "ночных" смен провел посетитель на объекте. В 1С:Предприятие работа с датами имеет свои особенности, которые мы подробно разберем в этой статье.
Рассмотрим классическую ситуацию: у нас есть документ, в котором пользователь указывает дату и время въезда, а также дату и время выезда. Наша цель — автоматически заполнить поля КолвоДней и КолвоНочей по следующим правилам:
Проанализируем пример: если заезд осуществлен 15.02.2024 в 08:00, а выезд 16.02.2024 в 23:00, система должна зафиксировать 2 дня и 2 ночи. Основная сложность здесь заключается в том, что тип Дата в 1С всегда содержит и дату, и время одновременно, а арифметические операции с ними возвращают результат в секундах.
Для начинающих разработчиков наиболее понятным будет алгоритм "перебора" временных отрезков. Рассмотрим, как можно реализовать этот расчет в обработчике события ПриИзменении для поля времени выезда.
Суть метода заключается в создании цикла, который "шагает" по периодам и проверяет, к какому типу относится текущий отрезок времени. Разберем пример кода:
&НаКлиенте
Процедура ВремяВыездаПриИзменении(Элемент)
// Обнуляем предыдущие результаты
Объект.КолвоДней = 0;
Объект.КолвоНочей = 0;
// Объединяем дату и время в полноценные переменные типа Дата
// Используем Формат для корректного преобразования
СтрВъезд = Формат(Объект.ДатаВъезда, "ДФ=ггггММдд") + Формат(Объект.ВремяВъезда, "ДФ=ЧЧммсс");
Въезд = Дата(СтрВъезд);
СтрВыезд = Формат(Объект.ДатаВыезда, "ДФ=ггггММдд") + Формат(Объект.ВремяВыезда, "ДФ=ЧЧммсс");
Выезд = Дата(СтрВыезд);
// Проверка на корректность ввода
Если Выезд < Въезд Тогда
Сообщить("Время выезда не может быть раньше времени въезда!");
Возврат;
КонецЕсли;
ТекущийМомент = Въезд;
// Логика перебора периодов
Пока ТекущийМомент < Выезд Цикл
ЧасНачала = Час(ТекущийМомент);
// Определяем, день сейчас или ночь (граница 08:00 и 20:00)
Если ЧасНачала >= 8 И ЧасНачала < 20 Тогда
Объект.КолвоДней = Объект.КолвоДней + 1;
// Переходим к следующему периоду (вечеру)
ТекущийМомент = НачалоДня(ТекущийМомент) + 20 * 3600;
Иначе
Объект.КолвоНочей = Объект.КолвоНочей + 1;
// Переходим к следующему утру
Если ЧасНачала >= 20 Тогда
ТекущийМомент = НачалоДня(ТекущийМомент + 86400) + 8 * 3600;
Иначе
ТекущийМомент = НачалоДня(ТекущийМомент) + 8 * 3600;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В данном примере мы используем функцию НачалоДня() и добавляем к ней количество секунд (3600 секунд в часе), чтобы получить точные границы 08:00 и 20:00. Это позволяет избежать ошибок при переходе через полночь.
Для оптимизации работы программы, особенно если расчетов много, лучше использовать математический подход. Выясним причину, почему это эффективнее: циклы могут нагружать систему, если интервал нахождения составляет несколько месяцев. Математика же работает мгновенно.
Рассмотрим вариант кода, который вычисляет количество 12-часовых периодов:
// Вспомогательная функция для определения, является ли время дневным
ЗаехалДнем = ?(Час(Въезд) >= 8 И Час(Въезд) < 20, 1, 0);
ВыехалДнем = ?(Час(Выезд) >= 8 И Час(Выезд) < 20, 1, 0);
ВремяСекунд = Выезд - Въезд;
// Вычисляем общее количество 12-часовых блоков
ЧислоПериодов = Цел(ВремяСекунд / (60 * 60 * 12));
Объект.КолвоДней = Цел(ЧислоПериодов / 2) + ЗаехалДнем;
Объект.КолвоНочей = Цел(ЧислоПериодов / 2) + (1 - ЗаехалДнем);
Важный нюанс: при таком подходе нужно отдельно обрабатывать остатки от деления, если время выезда не кратно 12 часам. Проанализируем ситуацию: если машина заехала в 07:00 (ночь) и выехала в 09:00 (день), общая разница всего 2 часа, но по факту она затронула и ночной, и дневной периоды.
Рассмотрим подробнее несколько правил хорошего тона при написании подобного функционала в 1С:
&НаКлиенте, а в модуле объекта или общем модуле. Это позволит использовать этот же алгоритм при загрузке данных из внешних систем или в отчетах.ОбщегоНазначения. Там есть готовые инструменты для работы с датами.Дата (составом "Время") на самом деле хранит дату 01.01.0001 плюс указанное время. Чтобы получить корректную дату со временем, используйте конструкцию: Объект.ДатаВъезда + (Объект.ВремяВъезда - Дата(1,1,1)).>= для начала периода и < для конца.Выясним, какие ошибки чаще всего допускают пользователи. Самая частая — ввод времени выезда раньше времени въезда. Всегда добавляйте проверку в начале процедуры. Также полезно использовать функцию ЗначениеЗаполнено(), чтобы не пытаться производить арифметические действия с пустыми датами, что приведет к ошибке исполнения кода.
Подводя итог, можно сказать, что выбор метода зависит от ваших навыков. Для простой задачи в небольшой конфигурации достаточно первого способа с циклом. Для высоконагруженных систем или конфигураций уровня ERP следует использовать математические методы или механизмы производственных календарей.