Выборка (фильтрация) в Битриксе

Условия выборки

"!" - не равно
"<" - меньше
"<=" - меньше либо равно
">" - больше
">=" - больше либо равно
"><" - между
"!><" - не между
false - без значения

По дате

Фильтрация по пользовательским свойствам типа "Дата"

:!: При поиске по не встроенным полям (НЕ «начало активности», НЕ «конец активности») обязательно конвертировать дату в формат хранения YYYY-MM-DD HH:MI:SS (DateTime::format('Y-m-d H:i:s')), например выбор записей не старше 31 дня:

property-date-search.php
$dateNow = new DateTime('now', new DateTimeZone('UTC'));
$dateNow->modify('-31 day');
$arFilter['>=PROPERTY_PAY_DATE'] = $dateNow->format('Y-m-d H:i:s');

Фильтрация по "началу" и "концу активности"

Наоборот, для полей «начало активности» и «конец активности» следует использовать формат DD.MM.YYYY HH:MI:SS (DateTime::format('d.m.Y H:i:s')).

Фильтрация по дате создания

Выбрать записи не старше 31 дня:

date-create-filter.php
$now = new DateTime();
$arFilter = array(">DATE_CREATE" => $now->modify('-31 day')->format('d.m.Y H:i:s'));

Выбор новостей за день или за месяц

Если у новости ее датой является «начало активности», и нет продолжительности по времени, то есть «конец активности» несет смысл только снятия ее с публикации, то выбор новостей за день или месяц происходит так:

calendar.php
<?php
$BITRIX_DATETIME_FORMAT = 'd.m.Y H:i:s';
 
$activeYear = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : null;
$activeMonth = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : null;
$activeDay = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : null;
$activeCompany = isset($_REQUEST['company']) ? (int)$_REQUEST['company'] : null;
 
$arFilter = array("IBLOCK_ID"=>BlockId::_()->news);
 
switch(true) {
  case isset($activeDay) && isset($activeMonth) && isset($activeYear): // новости за день
    $dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $activeYear, $activeMonth, $activeDay), new DateTimeZone('UTC'));
    $dateEnd = clone $dateBegin;
    $dateEnd->modify('+1 day -1 second');
    break;
  case isset($activeMonth) && isset($activeYear): // новости за месяц
    $dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $activeYear, $activeMonth, $activeDay), new DateTimeZone('UTC'));
    $dateEnd = clone $dateBegin;
    $dateEnd->modify('+1 month -1 second');
    break;
  default:
}
 
if (isset($dateBegin) && isset($dateEnd)) {
  $arFilter['><DATE_ACTIVE_FROM'] = array($dateBegin->format($BITRIX_DATETIME_FORMAT), $dateEnd->format($BITRIX_DATETIME_FORMAT));
}
 
$GLOBALS['arFilter'] = $arFilter;

Выбор событий с интервалом

Допустим, событие (акция) имеет продолжительность, которую указываем в полях «начало активности» и «конец активности», без учета времени, только дата.

«Конец активности» в данном случае служит для указания конца интервала времени, а не снятия с публикации (хотя такое тоже возможно).

Тогда выбор текущего события выглядит так:

event.php
$BITRIX_DATETIME_FORMAT = 'd.m.Y H:i:s';
 
$arFilter = array("IBLOCK_ID" => $this->arParams['IBLOCK_ID'], "ACTIVE"=>"Y");
$arSort = array("SORT" => "DESC");
$arSelect = array("IBLOCK_ID", "ID", "CODE", "NAME", "PREVIEW_TEXT", "PREVIEW_PICTURE", "ACTIVE_FROM", "ACTIVE_TO", "TAGS");
 
// для архива событий на определенную дату
$dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $_REQUEST['year'], $_REQUEST['month'], $_REQUEST['day']), new DateTimeZone('UTC'));
 
// для актуальных событий
$dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', date('Y'), date('m'), date('d')), new DateTimeZone('UTC'));
 
$arDayFilter = $arFilter;
$dateEnd = clone $dateBegin;
$dateBegin->modify('+1 day -1 second');
$arDayFilter['<=DATE_ACTIVE_FROM'] = $dateBegin->format($BITRIX_DATETIME_FORMAT);
$arDayFilter['>=DATE_ACTIVE_TO'] = $dateEnd->format($BITRIX_DATETIME_FORMAT);
 
$itemsListDay = CIBlockElement::GetList($arSort, $arDayFilter, false, false, $arSelect);

Быстрый выбор новостей в календарь за месяц из нескольких инфоблоков

Высокая скорость обеспечивается прямым обращением к базе данных. В качестве даты новости - поле «Начало активности».

$this-\>arParams['IBLOCK_ID'] - массив номеров инфоблоков

month_calendar.php
class BAEventsEngine {
 
  const MYSQL_DATETIME_FORMAT = 'Y-m-d H:i:s';
 
  public function getDates($year, $month) {
    global $DB;
    $dates = array();
    $monthDates = array();
    $this->calcStartEndDate($year, $month);
    $query = "SELECT EXTRACT(DAY FROM `ACTIVE_FROM`) AS `day`, `PREVIEW_PICTURE` as `pic`, `NAME` as `name`, `ACTIVE_TO` as `act_to`
              FROM `b_iblock_element`
              WHERE IBLOCK_ID IN (".implode(', ', (array)$this->arParams['IBLOCK_ID']).")
              AND ACTIVE = 'Y'
              AND (ACTIVE_FROM BETWEEN '".$this->dateBegin->format(self::MYSQL_DATETIME_FORMAT)."' AND '".$this->dateEnd->format(self::MYSQL_DATETIME_FORMAT)."')
              ORDER BY SORT";
    $CDatabaseRes = $DB->Query($query, false, '');
    if ($CDatabaseRes->SelectedRowsCount() > 0) {
      while ($result = $CDatabaseRes->Fetch()) {
        $day =& $dates[$result['day']];
        if(!isset($day)) $day = array();
 
        if(!isset($day['pic'])) {
          $day['pic'] = $result['pic'];
        }
 
        if($result['act_to'] == null) {
          $monthDates[100][] = $result;
        } else {
          $monthDates[$result['day']][] = $result;
        }
 
      }
      unset($day);
      ksort($monthDates);
    }
    return array($dates, $monthDates);
  }
 
  private $dateBegin, $dateEnd;
 
  private function calcStartEndDate($year, $month) {
    $this->dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $year, $month, 1), new DateTimeZone('UTC'));
    $this->dateEnd = clone($this->dateBegin);
    $this->dateEnd->modify("+1 month -1 second");
  }
 
}

Если события в таком календаре имеют продолжительность, возможно узнать, активно ли событие активность в данный день. Перед $query вставить:

$dayActivites = $this->isDaysActiveCond($this->dateBegin);

Модифицировать $query:

$query = "SELECT EXTRACT(DAY FROM `ACTIVE_FROM`) AS `day`, `PREVIEW_PICTURE` as `pic`, `NAME` as `name`, `ACTIVE_TO` as `act_to`, {$dayActivites} ..."
private function isDaysActiveCond($date) {
  $cond = array();
  $numDaysInMonth = $date->format('t');
 
  $year = $date->format('Y');
  $month = $date->format('m');
 
  for($i = 1; $i <= $numDaysInMonth; $i++) {
    $day = sprintf('%1$02d', $i);
    $cond[] = "(`ACTIVE_FROM` <= '{$year}-{$month}-{$day} 23:59:59' AND `ACTIVE_TO` >= '{$year}-{$month}-{$day} 00:00:00') AS `day_{$i}`";
  }
  return implode(', ', $cond);
}

Таким образом в выборку попадут поля day_1, day_2, ..., содержащие признак активности события в этот день (1).