В рамках работы с системой компоновки данных (СКД) 1С часто возникает необходимость фильтрации данных не только по дате, но и по точному времени. Рассмотрим ситуацию, когда нужно классифицировать записи по временным интервалам, например, для учета приема пищи сотрудников.
Представим, что сотрудники приходят на прием пищи (завтрак, обед, ужин), отмечаются в 1С, и программа фиксирует точное время отметки. Эти данные пишутся в регистр сведений. Наша задача — определить, к какому приему пищи относится каждая отметка, исходя из заданных временных интервалов. Изначально интервалы заданы по часам, но затем требуется учесть и минуты.
Исходные интервалы:
Обновленные интервалы с учетом минут:
Отчет формируется на конкретную дату, и нам нужно корректно классифицировать каждую отметку по времени.
Начнем с простого случая, когда отбор требуется выполнить только по часам, без учета минут. Для этого в запросе СКД мы будем использовать функцию ЧАС(), которая извлекает часовую компоненту из поля типа "Дата и время".
Рассмотрим пример запроса, который классифицирует отметки по часам. Мы используем оператор ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ для создания нового вычисляемого поля, которое будет содержать наименование приема пищи.
Пример кода для отбора по часам:
ВЫБРАТЬ
РегистрСведений.ДатаВремяОтметки,
ВЫБОР
КОГДА ЧАС(РегистрСведений.ДатаВремяОтметки) >= 8 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 10 ТОГДА "Завтрак"
КОГДА ЧАС(РегистрСведений.ДатаВремяОтметки) >= 13 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 15 ТОГДА "Обед"
КОГДА ЧАС(РегистрСведений.ДатаВремяОтметки) >= 18 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 20 ТОГДА "Ужин"
ИНАЧЕ "Не вовремя"
КОНЕЦ КАК ПриемПищи
ИЗ
РегистрСведений.ОтметкиСотрудников КАК РегистрСведений
В этом примере мы предполагаем, что поле с датой и временем в регистре сведений называется РегистрСведений.ДатаВремяОтметки. Мы последовательно проверяем часовую компоненту на попадание в заданные интервалы. Обратите внимание, что условие ЧАС(...) < N означает, что отметка ровно в N:00 уже не попадает в данный интервал. Если бы интервал был "с 08:00 до 10:00 включительно", то последнему условию < 10 следовало бы заменить на <= 10.
Когда требуется учесть минуты, простого сравнения часов уже недостаточно. Мы должны дополнить условия, используя также функцию МИНУТА(), которая извлекает минутную компоненту из поля типа "Дата и время".
Давайте разберем, как составить комбинированное условие для интервала, например, с 13:30 до 15:30. Нам нужно учесть три сценария для каждой границы интервала:
МИНУТА(...) >= 30).МИНУТА(...) <= 30).Эти сценарии объединяются операторами И и ИЛИ для формирования комплексного условия.
Пример кода для отбора по часам и минутам:
ВЫБРАТЬ
РегистрСведений.ДатаВремяОтметки,
ВЫБОР
// Завтрак: с 08:00 до 10:00 (часы без изменений)
КОГДА ЧАС(РегистрСведений.ДатаВремяОтметки) >= 8 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 10 ТОГДА "Завтрак"
// Обед: с 13:30 до 15:30
КОГДА (ЧАС(РегистрСведений.ДатаВремяОтметки) > 13 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 15) // Между 13:59 и 15:00
ИЛИ (ЧАС(РегистрСведений.ДатаВремяОтметки) = 13 И МИНУТА(РегистрСведений.ДатаВремяОтметки) >= 30) // От 13:30
ИЛИ (ЧАС(РегистрСведений.ДатаВремяОтметки) = 15 И МИНУТА(РегистрСведений.ДатаВремяОтметки) <= 30) // До 15:30
ТОГДА "Обед"
// Ужин: с 18:30 до 20:30
КОГДА (ЧАС(РегистрСведений.ДатаВремяОтметки) > 18 И ЧАС(РегистрСведений.ДатаВремяОтметки) < 20) // Между 18:59 и 20:00
ИЛИ (ЧАС(РегистрСведений.ДатаВремяОтметки) = 18 И МИНУТА(РегистрСведений.ДатаВремяОтметки) >= 30) // От 18:30
ИЛИ (ЧАС(РегистрСведений.ДатаВремяОтметки) = 20 И МИНУТА(РегистрСведений.ДатаВремяОтметки) <= 30) // До 20:30
ТОГДА "Ужин"
ИНАЧЕ "Не вовремя"
КОНЕЦ КАК ПриемПищи
ИЗ
РегистрСведений.ОтметкиСотрудников КАК РегистрСведений
Этот подход является более точным и наглядным по сравнению с некоторыми другими методами, так как мы напрямую оперируем часами и минутами, что делает код более читаемым и понятным. Однако, если границы интервалов станут еще более сложными (например, с учетом секунд), или количество интервалов очень велико, это условие может стать громоздким.
Еще одним надежным и универсальным методом является приведение временной части даты к общему числовому значению — количеству секунд, прошедших с начала дня. Это позволяет сравнивать время как обычные числа, что значительно упрощает логику, особенно при сложных интервалах или большой их количестве.
Для этого мы используем функцию РАЗНОСТЬДАТ(). Сначала мы определяем начало дня для нашей даты с помощью НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), затем вычисляем разницу в секундах между началом дня и текущим временем отметки. Полученное значение мы сравниваем с границами интервалов, также выраженными в секундах.
Шаги:
МЕЖДУ.Пример кода для отбора по секундам от начала дня:
ВЫБРАТЬ
РегистрСведений.ДатаВремяОтметки,
ВЫБОР
// Завтрак: с 08:00 (8*3600=28800) до 10:00 (10*3600=36000), т.е. до 09:59:59
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА) МЕЖДУ (8 * 3600) И (10 * 3600 - 1)
ТОГДА "Завтрак"
// Обед: с 13:30 (13*3600+30*60=48600) до 15:30 (15*3600+30*60=55800), т.е. до 15:29:59
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА) МЕЖДУ (13 * 3600 + 30 * 60) И (15 * 3600 + 30 * 60 - 1)
ТОГДА "Обед"
// Ужин: с 18:30 (18*3600+30*60=66600) до 20:30 (20*3600+30*60=73800), т.е. до 20:29:59
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА) МЕЖДУ (18 * 3600 + 30 * 60) И (20 * 3600 + 30 * 60 - 1)
ТОГДА "Ужин"
ИНАЧЕ "Не вовремя"
КОНЕЦ КАК ПриемПищи
ИЗ
РегистрСведений.ОтметкиСотрудников КАК РегистрСведений
Обратите внимание на - 1 в конечной границе интервала. Это необходимо, если мы хотим, чтобы конечная точка интервала (например, ровно 10:00:00 или 15:30:00) не попадала в текущий интервал, а скорее до него. Если интервал строго "до 10:00", то (10 * 3600 - 1) означает до 09:59:59. Если условие "до 10:00 включительно", то (10 * 3600). В нашем случае "с ... до ..." обычно подразумевает, что конечная точка не включена, поэтому -1 является корректным. Однако, всегда уточняйте требование к включению границ интервалов.
Этот метод является одним из самых строгих и часто используемых при необходимости сравнения только временных частей даты, игнорируя день, месяц и год. Мы "отрезаем" дату от времени, приводя его к началу эпохи (01.01.0001), что позволяет сравнивать временные интервалы напрямую как объекты типа "Дата".
Для этого мы сначала получаем количество секунд от начала дня (как в предыдущем методе с использованием РАЗНОСТЬДАТ()), а затем добавляем это количество секунд к дате 01.01.0001 с помощью функции ДОБАВИТЬКДАТЕ(). Границы интервалов также формируем от 01.01.0001 с помощью функции ДАТАВРЕМЯ(Год, Месяц, День, Час, Минута, Секунда).
Пример кода для отбора по "нулевой" дате:
ВЫБРАТЬ
РегистрСведений.ДатаВремяОтметки,
ВЫБОР
// Приведение текущего времени к дате 01.01.0001 для сравнения только временной части
КОГДА ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА))
МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 8, 0, 0) И ДАТАВРЕМЯ(1, 1, 1, 9, 59, 59) // С 08:00:00 до 09:59:59
ТОГДА "Завтрак"
КОГДА ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА))
МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 13, 30, 0) И ДАТАВРЕМЯ(1, 1, 1, 15, 29, 59) // С 13:30:00 до 15:29:59
ТОГДА "Обед"
КОГДА ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА))
МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 18, 30, 0) И ДАТАВРЕМЯ(1, 1, 1, 20, 29, 59) // С 18:30:00 до 20:29:59
ТОГДА "Ужин"
ИНАЧЕ "Не вовремя"
КОНЕЦ КАК ПриемПищи
ИЗ
РегистрСведений.ОтметкиСотрудников КАК РегистрСведений
Здесь ДАТАВРЕМЯ(1, 1, 1, ЧАС, МИНУТА, СЕКУНДА) создает полную дату, но поскольку мы всегда используем 01.01.0001, фактически сравниваются только временные компоненты. Этот метод считается самым строгим и читаемым для сравнения временных интервалов, поскольку он позволяет оперировать с временными точками как с полноценными датами. Опять же, важно корректно определить границы интервалов, включая или исключая секунды в зависимости от требований (например, "до 10:00:00" это до 09:59:59).
Если логика определения временных интервалов становится слишком громоздкой в основном запросе или требуется ее повторное использование в различных частях отчета, мы можем вынести ее в вычисляемые поля на уровне схемы компоновки данных. Это позволяет сохранить запрос более простым и переложить сложную логику на саму СКД.
Для этого мы переходим в настройки СКД, на вкладку "Вычисляемые поля" и создаем новое поле. В его выражении мы можем использовать те же функции и конструкции, что и в запросе (Час(), Минута(), РазностьДат(), ДобавитьКДате(), ДатаВремя(), ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ и т.д.).
Преимущества этого подхода:
Мы можем создать вычисляемое поле, например, с именем "ВремяОтметкиБезДаты", используя выражение:
ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(РегистрСведений.ДатаВремяОтметки, ДЕНЬ), РегистрСведений.ДатаВремяОтметки, СЕКУНДА))
Это поле будет содержать временную часть отметки, приведенную к "нулевой" дате. А затем, мы можем создать второе вычисляемое поле, например, "ПриемПищи", и в его выражении уже использовать более чистую логику сравнения:
ВЫБОР
КОГДА ВремяОтметкиБезДаты МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 8, 0, 0) И ДАТАВРЕМЯ(1, 1, 1, 9, 59, 59) ТОГДА "Завтрак"
КОГДА ВремяОтметкиБезДаты МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 13, 30, 0) И ДАТАВРЕМЯ(1, 1, 1, 15, 29, 59) ТОГДА "Обед"
КОГДА ВремяОтметкиБезДаты МЕЖДУ ДАТАВРЕМЯ(1, 1, 1, 18, 30, 0) И ДАТАВРЕМЯ(1, 1, 1, 20, 29, 59) ТОГДА "Ужин"
ИНАЧЕ "Не вовремя"
КОНЕЦ
Такой подход делает схему компоновки данных более модульной и легче управляемой.
Мы рассмотрели несколько подходов к организации отбора по времени в СКД, начиная от простых сравнений по часам и заканчивая более точными методами с учетом минут и секунд. Выбор конкретного метода зависит от требуемой точности, сложности логики интервалов и предпочтений в читаемости кода. Использование функций ЧАС(), МИНУТА(), РАЗНОСТЬДАТ() и ДАТАВРЕМЯ(), а также возможность выноса сложной логики в вычисляемые поля СКД, предоставляют широкие возможности для решения подобных задач в 1С. Всегда анализируйте свои требования к точности и производительности, чтобы выбрать наиболее подходящий вариант.