Даты в коде - объект, часовой пояс, фильтр по периоду
Разбираемся с датами так, чтобы отчёт «за вчера» сходился у всех, а фильтр по периоду не терял записи последнего дня.
Решение
Работаем с датой объектом:
use Bitrix\Main\Type\DateTime;
$from = new DateTime('01.08.2026 00:00:00', 'd.m.Y H:i:s');$to = (clone $from)->add('1 month'); // прибавление понимает словами$isPast = $from < new DateTime(); // объекты сравниваются напрямуюС датой работают объектом, а не строкой. Строка зависит от формата и языка сайта, а объект складывается, сравнивается и уходит в запрос без пересборки формата на каждом шаге.
Пишем и читаем дату в базе:
SyncTable::add(['DATE_START' => new DateTime()]); // объект, а не строка$row = SyncTable::getList([ 'filter' => ['>=DATE_START' => $from, '<DATE_START' => $to],])->fetch();echo $row['DATE_START']->format('d.m.Y H:i'); // из базы тоже приходит объектВ поле даты кладут объект, а не отформатированный текст. Тогда сравнение в фильтре работает по времени, а не по совпадению символов, и запись с другим форматом не теряется.
Отключаем сдвиг часового пояса:
\CTimeZone::Disable();$rows = OrderTable::getList(['select' => ['ID', 'DATE_INSERT']])->fetchAll();\CTimeZone::Enable();// в отчётах и обменах пояс отключают: там нужны одинаковые числа для всехПоказ сдвигается поясом пользователя, а хранение - нет. Отчёт, посчитанный под менеджером из другого пояса, отличается от такого же отчёта под администратором ровно на этот сдвиг.
Считаем границы периода:
$dayStart = new DateTime(date('d.m.Y') . ' 00:00:00', 'd.m.Y H:i:s');$dayEnd = (clone $dayStart)->add('1 day');// фильтр «больше или равно началу и строго меньше следующего дня»// так последняя секунда суток не теряется и не задваиваетсяГраницы периода задают двумя явными отметками. Условие «меньше конца суток» с временем 23:59:59 теряет записи последней секунды, а строгое сравнение со следующим днём - нет.
Даты из внешних систем приводят к своему виду сразу на входе. Чужой формат и чужой пояс, доехавшие до базы как есть, потом ищутся по всему проекту.
Дату без времени и дату со временем стоит различать в голове и в коде. День рождения и момент оформления заказа ведут себя по-разному ровно на границе суток.
Отчёты по времени полезно подписывать поясом. Строка «время московское» под таблицей снимает половину вопросов к цифрам.
Типичные проблемы
Фильтр по дате не находит записи.
В фильтре строка в одном формате, а в базе дата в другом. Объект даты снимает вопрос формата целиком.
Отчёт «за вчера» у всех разный.
Пояс пользователя сдвигает границы суток при выводе. В отчётах пояс отключают явно.
Из периода выпали заказы последней минуты.
Верхняя граница задана временем 23:59:59 вместо начала следующего дня. Сравнение делают строгим и по следующей отметке.
Дата из обмена уехала на несколько часов.
Внешняя система прислала время в своём поясе, а его приняли как серверное. Чужие даты приводят к своему поясу на входе.
Сравнение дат работает через раз.
Даты сравниваются как строки, а не как объекты. У строк порядок символов, а не времени.
Частые вопросы
В каком времени дата лежит в базе?
Во времени сервера, без пояса пользователя. Сдвиг добавляется на выводе.
Когда отключать сдвиг пояса?
В отчётах, обменах и всём, что должно давать одинаковые числа. На витрине сдвиг обычно нужен.
Как хранить дату без времени?
Отдельным типом поля для даты. Иначе время само по себе начнёт участвовать в сравнениях.
Что делать с датами из внешнего API?
Разбирать по известному формату и приводить к своему поясу сразу. Хранить чужой формат не нужно.
Смежное
-
Даты и время - оглавление подтемы
-
Рабочий календарь: график, праздники, расчёт срока - расчёты с выходными и праздниками
-
Дата сохранилась не та: разбор причин - когда дата уехала на несколько часов
-
Отчёты по заказам: агрегаты, группировка, выгрузка - где границы суток важнее всего
-
Дата, число и HTML в свойствах: хранение, вывод, фильтр - даты в свойствах инфоблоков
-
Свой агент и задание по расписанию: создание, шаг, защита - код, зависящий от времени запуска
-
Ядро D7 - устройство ядра целиком
-
Вывод дат на витрине: падежи, форматы сайта, относительное время - что происходит после расчёта
-
Запись на услугу: слоты, занятость, защита от двойной брони - отрезки времени в бронировании услуг
-
Дата и время изнутри: хранение, пояса, форматы - откуда берётся сдвиг между базой и экраном