Ядро D7 отказалось от «сырых» строк с датами в пользу объектов-обёрток. Правильная работа с типами Bitrix\Main\Type\DateTime и полями ORM избавляет от ошибок часовых поясов, форматирования и приведения значений при записи в базу.
Объекты Date и DateTime вместо строк
В D7 дата — это не строка формата сайта, а объект. Основные классы лежат в пространстве имён Bitrix\Main\Type:
Bitrix\Main\Type\Date— только дата, без времени;Bitrix\Main\Type\DateTime— дата и время (наследникDate);
Оба класса — обёртки над PHP \DateTime, поэтому поддерживают арифметику через add() и относительные интервалы. Создание из строки в формате сайта и из произвольного формата различается:
use Bitrix\Main\Type\DateTime;
$now = new DateTime(); // текущий момент
$dt = DateTime::createFromUserTime('16.07.2026 12:30:00');
$fromFormat = new DateTime('2026-07-16 12:30:00', 'Y-m-d H:i:s');
$dt->add('3 days')->add('-2 hours');
createFromUserTime() учитывает часовой пояс пользователя, а конструктор — нет. Для значений, введённых человеком, используйте именно фабричный метод.Часовые пояса и хранение времени
Если в настройках сайта включены часовые пояса (Настройки → Настройки продукта → Часовой пояс), ядро хранит время в БД в серверном поясе, а показывает в пользовательском. Ключевые функции:
| Функция / метод | Назначение |
|---|---|
\CTimeZone::Enable()/Disable() | Временно включить/выключить сдвиг пояса для блока кода |
DateTime::createFromUserTime() | Разобрать ввод пользователя с учётом его пояса |
$dt->toUserTime() | Получить копию, сдвинутую в пояс текущего пользователя |
Типичная ошибка — сравнивать даты из ORM и «руками» собранные без учёта пояса. Перед пакетными операциями (импорт, обмен) пояс часто отключают:
\CTimeZone::Disable();
// массовая запись дат в БД в серверном времени
\CTimeZone::Enable();
getList с new DateTime(), оба объекта уже в серверном времени — дополнительный сдвиг приведёт к смещению на несколько часов.Скалярные типы полей ORM
В ORM тип значения задаёт класс поля из Bitrix\Main\ORM\Fields. Именно поле отвечает за приведение значения при чтении из БД и при записи. Основные скалярные типы:
| Класс поля | PHP-тип после выборки |
|---|---|
IntegerField | int |
FloatField | float |
StringField | string |
BooleanField | bool (по values-маппингу) |
DateField | Bitrix\Main\Type\Date |
DatetimeField | Bitrix\Main\Type\DateTime |
EnumField | string из фиксированного набора |
TextField | string (long text) |
Тип объявляется в методе getMap() таблицы. Обратите внимание: имена классов D7 (Bitrix\Main\ORM\Fields\DatetimeField) вытеснили старые Entity\DatetimeField, которые оставлены для совместимости.
BooleanField и EnumField: значения по правилам
BooleanField в Битрикс не хранит настоящий SQL BOOLEAN — он маппит два строковых значения на false и true. Это удобно для привычного битриксового 'N'/'Y':
(new BooleanField('ACTIVE'))
->configureValues('N', 'Y')
->configureDefaultValue('N');
EnumField ограничивает множество допустимых значений — при валидации значение вне списка отклоняется:
(new EnumField('STATUS'))
->configureValues(['NEW', 'PAID', 'DONE']);
- для
BooleanFieldпервое значение вconfigureValuesсоответствуетfalse, второе —true; EnumFieldне приводит регистр — храните значения консистентно;- оба типа проверяются при
add()/update()и бросаютResultс ошибкой.
0/1, используйте configureValues(0, 1) — маппинг работает и с числами.Запись и чтение значений через ORM
При добавлении записи вы передаёте объекты нужного типа, а поле само сериализует их в SQL. Пример с датой и булевым полем:
use Bitrix\Main\Type\DateTime;
OrderTable::add([
'ACTIVE' => true,
'STATUS' => 'NEW',
'DATE_CREATE' => new DateTime(),
'SUM' => 1990.50,
]);
При выборке значения приходят уже типизированными — не нужно вручную парсить строки:
$row = OrderTable::getRow(['filter' => ['=ID' => 42]]);
$row['DATE_CREATE']->format('d.m.Y'); // объект DateTime
var_dump($row['ACTIVE']); // bool(true)
- чтобы отфильтровать по интервалу дат, передавайте объекты
DateTime, а не строки; - для сравнения «сегодня» удобно
Bitrix\Main\Type\Dateбез времени; - если нужно чистое SQL-выражение (
NOW()), используйтеBitrix\Main\DB\SqlExpression.
SqlExpression и приведение на стороне БД
Иногда значение должна вычислить сама база — например, проставить текущую метку времени средствами СУБД. Для этого есть Bitrix\Main\DB\SqlExpression:
use Bitrix\Main\DB\SqlExpression;
LogTable::add([
'MESSAGE' => 'sync',
'TS' => new SqlExpression('NOW()'),
]);
Плейсхолдеры %s экранируются, что защищает от инъекций:
new SqlExpression('DATE_ADD(?#, INTERVAL 1 DAY)', 'DATE_CREATE');
SqlExpression, после add() объект даты в PHP автоматически не появится — перечитайте запись, если нужен типизированный результат.Итог
Типизация — фундамент предсказуемого кода на D7. Работайте с датами как с объектами Date/DateTime, помните про createFromUserTime() и часовые пояса, а в getMap() подбирайте поле под смысл данных: BooleanField с маппингом, EnumField с валидацией, SqlExpression для вычислений на стороне БД. Так вы избегаете классических багов со сдвигом времени и «строковыми» датами.
Мы в B2Bsite проектируем модель данных на D7 с корректной типизацией и работой поясов при интеграциях с 1С и внешними сервисами. Если нужен обмен, где даты и статусы приходят из разных систем без искажений, поможем спроектировать надёжный слой хранения и синхронизации.
Частые вопросы
Чем Bitrix\Main\Type\DateTime отличается от PHP DateTime?
Класс Битрикс — обёртка над PHP \DateTime с поддержкой форматов сайта, часовых поясов пользователя и удобных методов add() с относительными интервалами. Внутри он использует стандартный \DateTime, но добавляет интеграцию с настройками портала.
Как правильно разобрать дату, введённую пользователем?
Используйте DateTime::createFromUserTime(), который учитывает часовой пояс текущего пользователя. Обычный конструктор трактует строку в серверном времени и может дать смещение.
Почему после выборки дата приходит объектом, а не строкой?
Потому что тип поля DatetimeField в getMap() сам приводит значение из БД к объекту DateTime. Это поведение ORM: поле отвечает за сериализацию и десериализацию значения.
Как хранить булев флаг 'Y'/'N' через ORM?
Объявите поле как BooleanField и вызовите configureValues('N', 'Y'). Первое значение маппится на false, второе на true, и после выборки вы получаете настоящий bool.
Когда нужен SqlExpression вместо объекта даты?
Когда значение должна вычислить сама СУБД — например NOW() или DATE_ADD. SqlExpression вставляет выражение в запрос с экранированием плейсхолдеров, защищая от инъекций.
Нужно ли отключать часовые пояса при массовом импорте?
Часто да: CTimeZone::Disable() перед пакетной записью гарантирует, что даты лягут в серверном времени без пользовательского сдвига. После блока обязательно вызовите Enable().
Чем Date отличается от DateTime в ORM?
Date хранит только календарную дату без времени и маппится на DateField, а DateTime хранит дату и время и маппится на DatetimeField. Date — родительский класс для DateTime.
Поможем с настройкой и поддержкой Для разработчиков
Поможем с настройкой, доработкой и поддержкой Для разработчиков.