Работа с датами в системе 1С:Предприятие часто вызывает трудности у начинающих разработчиков. Основная причина заключается в том, что тип данных Дата в 1С всегда содержит и календарный день, и время с точностью до секунды. Если мы видим в интерфейсе «01.01.2023», для платформы это на самом деле «01.01.2023 00:00:00». В этой статье мы подробно разберем, как правильно обрабатывать такие данные в запросах, как отсекать временную составляющую и как корректно сравнивать периоды.
Рассмотрим стандартную ситуацию: у нас есть документ, созданный 23.05.2018 в 15:42:35, и запись в регистре сведений, которая действует до 23.05.2018. Если мы попытаемся сравнить их напрямую через оператор равенства или даже через некоторые условия диапазона, мы можем получить пустой результат. Выясним причину: документ со временем 15:42:35 уже «больше», чем дата в регистре, если там сохранено значение 00:00:00. Именно поэтому, когда время документа становится хотя бы на одну секунду больше полночи (00:00:01), стандартные условия сравнения могут перестать работать.
Для того чтобы «привести» дату к определенному стандарту внутри запроса, мы используем специальные функции. Проанализируем их работу:
НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) — эта функция сбрасывает время в 00:00:00. Это идеальный способ «отсечь» время, оставив только дату.КОНЕЦПЕРИОДА(Дата, ДЕНЬ) — эта функция устанавливает время в 23:59:59. Она незаменима, когда нужно включить весь день целиком в условие поиска.Посмотрим на пример из практики, когда нам нужно найти записи в регистре, в диапазон которых попадает дата документа:
ГДЕ
&ДатаДокумента МЕЖДУ Регистр.НачалоПериода И КОНЕЦПЕРИОДА(Регистр.КонецПериода, ДЕНЬ)
В данном случае, даже если в поле КонецПериода регистра записана дата с нулевым временем, функция КОНЕЦПЕРИОДА расширит границу до последней секунды дня, и документ, созданный в середине дня, попадет в выборку.
Часто при выполнении запроса в консоли или программно разработчик видит в два раза больше результатов, чем ожидал. Проанализируем ситуацию: если мы делаем ВНУТРЕННЕЕ СОЕДИНЕНИЕ документа с регистром только по сотруднику, 1С выберет все документы этого сотрудника, существующие в базе. Чтобы ограничить выборку текущим документом, необходимо обязательно добавить условие по ссылке.
Рассмотрим правильный вариант запроса, который решает проблему лишних строк и некорректного сравнения времени:
ВЫБРАТЬ
ПроцентУдержаний.Регистратор,
ПроцентУдержаний.ФИО,
ПроцентУдержаний.Процент,
Ведомость.Ссылка КАК ДокументСсылка
ИЗ
Документ.ВедомостьРаздачиПищи КАК Ведомость
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПроцентУдержанийЗаПитание КАК ПроцентУдержаний
ПО Ведомость.Список.ФИОСотрудника = ПроцентУдержаний.ФИО
ГДЕ
Ведомость.Ссылка = &Ссылка
И &Дата МЕЖДУ ПроцентУдержаний.НачалоПериода И КОНЕЦПЕРИОДА(ПроцентУдержаний.КонецПериода, ДЕНЬ)
Здесь важно обратить внимание на два момента. Во-первых, фильтр Ведомость.Ссылка = &Ссылка гарантирует, что мы работаем только с данными одного конкретного документа. Во-вторых, использование КОНЕЦПЕРИОДА для правой границы диапазона регистра позволяет корректно сопоставить время.
Хотя функции приведения периода работают быстро, хорошим тоном в разработке на 1С считается подготовка параметров до передачи их в запрос. Разберем по шагам, как это сделать в программном коде:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ &ДатаПараметр МЕЖДУ Т.Начало И Т.Конец";
// Вместо того чтобы вычислять НАЧАЛОПЕРИОДА в запросе,
// передадим уже подготовленное значение начала дня.
Запрос.УстановитьПараметр("ДатаПараметр", НачалоДня(Объект.Дата));
Это не только упрощает чтение кода запроса, но и в некоторых случаях позволяет SQL-серверу более эффективно использовать индексы, так как над полем-параметром не производятся вычисления.
Опытные разработчики часто советуют использовать классические знаки сравнения (>=, <=) вместо оператора МЕЖДУ. Это связано с тем, что МЕЖДУ включает обе границы (и начало, и конец). Если вам нужно строго исключить следующую дату, можно использовать конструкцию:
ГДЕ
ДатаПроверки >= НачалоПериода
И ДатаПроверки < &ДатаСледующегоДня
Где &ДатаСледующегоДня — это НачалоДня(НужнаяДата) + 86400 (количество секунд в сутках). Такой подход считается наиболее «чистым» с точки зрения математической логики исключения пересечений.
Выясним еще одну важную деталь: как быть, если дата конца периода в регистре не заполнена (пустая дата), что часто означает «бессрочно»? В 1С пустая дата — это 01.01.0001 00:00:00. Если вы примените к ней КОНЕЦПЕРИОДА, она все равно останется пустой датой, и условие МЕЖДУ не сработает. В таких случаях структуру ГДЕ следует дополнить:
ГДЕ
&Дата >= ПроцентУдержаний.НачалоПериода
И (ПроцентУдержаний.КонецПериода = ДАТАВРЕМЯ(1, 1, 1)
ИЛИ &Дата <= КОНЕЦПЕРИОДА(ПроцентУдержаний.КонецПериода, ДЕНЬ))
Подведем итог: для корректного отсечения времени и сравнения дат всегда используйте НАЧАЛОПЕРИОДА или КОНЕЦПЕРИОДА, не забывайте фильтровать запросы по ссылке на объект, чтобы избежать дублей, и по возможности подготавливайте параметры в коде 1С с помощью функций НачалоДня() или КонецДня().