Фильтр по свойству дата/время в CIBlockElement::GetList

Я уже довольно давно пишу код под битрикс и насоздавал уже много разных инфоблоков с разным набором свойств. Довольно часто приходилось использовать собственные свойства типа Дата/Время, но до недавних пор не приходилось делать по ним выборок.

Описание проблемы

Хотим получить выборку всех элементов, у которых свойство типа Дата/время, равна определённому дню. Пишем вот такой код (свериться с мануалом):

CIBlockElement::GetList(
    Array(
        'ID' => 'ASC'
    ),
    Array(
        'IBLOCK_ID' => 35,
        'PROPERTY_DATE'=> ConvertTimeStamp($date->getTimestamp())
    ),
    false,
    false,
    Array(
        'ID'
    )
);

Здесь ConvertTimeStamp($date->getTimestamp()) возвращает дату в формате DD.MM.YYYY
Результатом такого запроса будет пустая выборка. Ладно, пробуем выбрать по диапазону:

CIBlockElement::GetList(
    Array(
        'ID' => 'ASC'
    ),
    Array(
	'IBLOCK_ID' => 35,
	'>=PROPERTY_DATE' => ConvertTimeStamp($startDate->getTimestamp(), 'FULL'),
	'<=PROPERTY_DATE' => ConvertTimeStamp($endDate->getTimestamp(), 'FULL')
	),
    false,
    false,
    Array(
        'ID'
    )
);

Здесь ConvertTimeStamp($startDate->getTimestamp(),’FULL’) возвращает дату в формате DD.MM.YYYY HH:MI:SS

И опять ничего не получается. Хотя записи точно есть и попадают в нужный промежуток.

Решение проблемы

Логичным было бы взять и посмотреть, что же там на самом деле хранится в БД и как оно там запрашивается? Свойства типа Дата/время в БД хранятся в формате YYYY-MM-DD HH:MI:SS т.е. вот так 2014-11-25 12:00:00. Системные поля этого типа у инфоблоков также хранятся в этом формате (DATE_CREATE, TIMESTAMP_X и другие).

Но вот тут есть нюанс, для системных полей API конвертирует дату из формата сайта в формат БД, и никаких проблем не возникает (разве что нельзя выбирать все записи за день, задав фильтр только по DD.MM.YYYY, обязательно нужно задавать диапазон от начала до конца дня)

Для пользовательских свойств типа Дата/время конвертации не происходит. И в запрос к БД попадает ровно то, что было отправлено:

WHERE
1=1 AND ( ((((BE.IBLOCK_ID = 35)))) AND ((((FPS0.PROPERTY_959 >= 
'25.11.2014 00:00:00')))) AND ((((FPS0.PROPERTY_959 <= '25.11.2014 23:59:59')))) )
 AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))

Теперь понятно, почему ничего не выбиралось. Выход из ситуации есть, и он довольно простой – все даты нужно конвертировать в формат времени БД функцией CDatabase::CharToDateFunction, но она возвращает результат обрамлённый в одинарные кавычки, так что их нужно будет тримить. Вот так:

trim(CDatabase::CharToDateFunction('25.11.2014 00:00:00'),"\'");

Теперь, вот такой фильтр будет работать:

CIBlockElement::GetList(
    Array(
        'ID' => 'ASC'
    ),
    Array(
	'IBLOCK_ID' => 35,
	'>=PROPERTY_DATE' => trim(CDatabase::CharToDateFunction(ConvertTimeStamp(
$startDate->getTimestamp(), 'FULL')), "\'"),
	'<=PROPERTY_DATE' => trim(CDatabase::CharToDateFunction(ConvertTimeStamp(
$endDate->getTimestamp(), 'FULL')), "\'")
	),
    false,
    false,
    Array(
        'ID'
    )
);

Послесловие

Об этой проблеме я написал в битрикс. И они даже добавили в документацию информацию об этом баге, но предложенное ими решение date("Y-m-d") не будет работать, т.к. выборка должна происходить по полному формату даты.

2 комментария

Тимур
Александр

Спасибо, очень дельная статья, и не пришлось велосипед изобретать :)

Ответить

Ваш отзыв

logo