Условия фильтрации в выборках 1С‑Битрикс: операторы, даты свойств и интервалы активности
Операторы в ключах фильтра
В большинстве выборок Битрикс первый параметр фильтра — ассоциативный массив; префикс перед именем поля задаёт оператор сравнения.
!— не равно<— меньше<=— меньше или равно>— больше>=— больше или равно><— значение между двумя границами (массив из двух строк)!><— вне интервала- значение
falseбез префикса — «пусто / не задано» в трактовке конкретного поля
Состав ключей нужно проверять по документации сущности: для свойств типа список, привязка к элементам и т. д. возможны исключения и составные ключи вида PROPERTY_*.
Форматы даты: свойства против «активности»
Даты в пользовательских свойствах инфоблока (не системные поля элемента) обычно сравнивают в том виде, в каком они лежат в базе после сохранения, чаще Y-m-d H:i:s. Для границ «начала» и «конца активности» ядро ориентируется на строку в региональном формате вида d.m.Y H:i:s.
Пример: элементы не старше 31 дня по свойству PAY_DATE:
$anchor = new \DateTime('now', new \DateTimeZone('UTC'));
$anchor->modify('-31 day');
$arFilter['>=PROPERTY_PAY_DATE'] = $anchor->format('Y-m-d H:i:s');Для DATE_CREATE и схожих полей смешивание форматов легко даёт «пустую» выборку; приведите строку к тому же шаблону, что возвращает ваша конвертация в админке/импорте.
$now = new \DateTime();
$arFilter = [
'>DATE_CREATE' => $now->modify('-31 day')->format('d.m.Y H:i:s'),
];Новости за выбранный день или месяц
Если датой анонса служит только «начало активности», а «конец» используется лишь для снятия с показа, удобно строить полуинтервал через оператор «между» по DATE_ACTIVE_FROM.
$bitrixDatetime = 'd.m.Y H:i:s';
$year = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : 0;
$month = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : 0;
$day = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : 0;
$arFilter = ['IBLOCK_ID' => $newsIblockId];
switch (true) {
case $year > 0 && $month > 0 && $day > 0:
$periodStart = new \DateTime(
sprintf('%04d-%02d-%02d 00:00:00', $year, $month, $day),
new \DateTimeZone('UTC')
);
$periodEnd = (clone $periodStart)->modify('+1 day -1 second');
break;
case $year > 0 && $month > 0:
$periodStart = new \DateTime(
sprintf('%04d-%02d-01 00:00:00', $year, $month),
new \DateTimeZone('UTC')
);
$periodEnd = (clone $periodStart)->modify('+1 month -1 second');
break;
default:
$periodStart = $periodEnd = null;
}
if ($periodStart && $periodEnd) {
$arFilter['><DATE_ACTIVE_FROM'] = [
$periodStart->format($bitrixDatetime),
$periodEnd->format($bitrixDatetime),
];
}События с датой начала и окончания
Когда интервал задаётся связкой DATE_ACTIVE_FROM / DATE_ACTIVE_TO, выбор элементов «которые идут в этот календарный день» сводится к пересечению отрезков: начало действия не позже конца дня, конец действия не раньше начала дня.
$bitrixDatetime = 'd.m.Y H:i:s';
$arFilter = [
'IBLOCK_ID' => $promoIblockId,
'ACTIVE' => 'Y',
];
$dayStart = new \DateTime(
sprintf('%04d-%02d-%02d 00:00:00', (int)$_REQUEST['year'], (int)$_REQUEST['month'], (int)$_REQUEST['day']),
new \DateTimeZone('UTC')
);
$dayTail = (clone $dayStart)->modify('+1 day -1 second');
$arFilter['>=DATE_ACTIVE_TO'] = $dayStart->format($bitrixDatetime);
$arFilter['<=DATE_ACTIVE_FROM'] = $dayTail->format($bitrixDatetime);Инвертировать условие «ничего не пересекается» легче на уровне SQL или отдельного слоя приложения — не перегружайте один и тот же массив взаимоисключающими ветками без комментария.
Календарь на месяц и прямой SQL
Чтобы собрать сетку «какие даты месяца заняты» по нескольким инфоблокам с активностью только по ACTIVE_FROM, один проход через GetList с тысячами элементов дороже, чем агрегат в базе с узким интервалом дат.
Строитель запроса обязан экранировать идентификаторы и подставлять границы только из вашего кода, а не из $_REQUEST напрямую: пример ниже упрощённо показывает идею, в реальности используйте #IBLOCK_IDS# с помощью $DB->ForSql или подготовленные выражения.
$monthStart = new \DateTime(sprintf('%04d-%02d-01 00:00:00', $calendarYear, $calendarMonth));
$monthEnd = (clone $monthStart)->modify('+1 month -1 second');
$fromSql = $DB->ForSql($monthStart->format('Y-m-d H:i:s'));
$toSql = $DB->ForSql($monthEnd->format('Y-m-d H:i:s'));
$sql = sprintf(
"SELECT EXTRACT(DAY FROM ACTIVE_FROM) AS `day`, NAME, PREVIEW_PICTURE"
. " FROM b_iblock_element"
. " WHERE ACTIVE = 'Y' AND IBLOCK_ID IN (%s)"
. " AND ACTIVE_FROM BETWEEN '%s' AND '%s'"
. " ORDER BY SORT",
implode(',', array_map('intval', $calendarIblockIds)),
$fromSql,
$toSql
);
$res = $DB->Query($sql, false);
while ($row = $res->Fetch()) {
// группируйте $row по дню календаря
}Если нужно знать, попадает ли длинное событие на каждый день месяца, добавляются выражения вида пересечения по дню — но объём генерации календаря растёт; часто достаточно выбрать кандидатов SQL и уже в PHP классифицировать строки недели.
Итого
- Следите за форматом строки под тип поля: свойство,
DATE_*системное, активность элемента. - Интервалы «за день/месяц» унифицируют через два
DateTime(границы периода) и оператор><там, где возможно. - Сложные календари и отчётность по месяцу оправданы прямым SQL только при аккуратной санитизации и ограниченном интервале дат.
- Подробности преобразования меток времени между форматами и часовыми поясами вынесите в общий справочник по датам на проекте — это снижает дубли в фильтрах.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии