Перейти к содержимому

Дата сохранилась не та - разбор причин

Дата уехала на несколько часов, не разобралась или попала не в тот период выборки. Разбираем причины по убыванию частоты.

С чего начать

Смотрим, включены ли часовые пояса и какой пояс у пользователя:

use Bitrix\Main\Type\DateTime;
$dt = new DateTime();
printf("пользователю=%s серверное=%s\n", $dt->toString(), (clone $dt)->disableUserTime());
// расхождение означает, что пояса включены и перевод времени работает
// в базе дата лежит в серверном поясе независимо от вывода

Разница между двумя строками объясняет большую часть жалоб «время не то». Платформа переводит время в пояс пользователя при выводе, а в базе хранит его в серверном поясе.

Разбираем пользовательский ввод правильно:

$dt = DateTime::tryParse($request->get('date'), 'DD.MM.YYYY HH:MI:SS');
if ($dt === null) {
$errors[] = 'Дата введена в неизвестном формате';
}
$server = DateTime::createFromUserTime('25.11.2025 18:00'); // ввод в поясе пользователя
// конструктор при неразобранном формате бросает исключение, а разбор возвращает null

Разбор строки и создание объекта ведут себя по-разному при неверном формате. Пользовательский ввод разбирают безопасным способом, а перевод в серверный пояс делают отдельным вызовом.

Проверяем границы периода в фильтре:

$rows = ElementTable::getList([
'filter' => [
'>=DATE_CREATE' => new DateTime('01.11.2025 00:00:00', 'd.m.Y H:i:s'),
'<DATE_CREATE' => new DateTime('01.12.2025 00:00:00', 'd.m.Y H:i:s'),
],
])->fetchAll();
// верхнюю границу берут строгой: иначе в выборку попадает лишний день

Период задают полуоткрытым интервалом. Нестрогая верхняя граница с началом суток захватывает записи следующего дня, а с концом суток теряет записи последних секунд.

Причины

  1. Время переводится в пояс пользователя примерно 30% случаев

    ПризнакВ интерфейсе одно время, в журнале и в базе другое, разница ровно в несколько часов.

    ПроверкаСравниваем вывод с переводом в пояс пользователя и без него.

    Что делатьДля журналов и служебных записей перевод отключают: там нужно именно серверное время.

  2. Пользовательский ввод разобран неверно примерно 25% случаев

    ПризнакДата сохраняется пустой или страница падает с ошибкой при вводе в другом формате.

    ПроверкаСмотрим, чем разбирается строка и указан ли ожидаемый формат.

    Что делатьРазбираем ввод безопасным методом с проверкой результата, а не конструктором объекта.

  3. Границы периода заданы неточно примерно 20% случаев

    ПризнакВ отчёт попадают записи соседнего дня или теряются записи последнего дня.

    ПроверкаСмотрим операторы сравнения у обеих границ периода и время в них.

    Что делатьБерём полуоткрытый интервал: нижняя граница нестрогая, верхняя строгая.

  4. Дата хранится строкой в своём формате примерно 15% случаев

    ПризнакСортировка по дате идёт как по тексту, фильтр по периоду не работает.

    ПроверкаСмотрим тип поля в таблице и то, что в него пишется.

    Что делатьХраним дату полем даты: строковое поле не умеет ни сравниваться, ни сортироваться правильно.

  5. Время потерялось при переводе в дату примерно 10% случаев

    ПризнакУ записи полночь вместо реального времени события.

    ПроверкаСмотрим, каким типом создаётся значение: только дата или дата со временем.

    Что делатьБерём тип с временем там, где время важно: тип даты нормализует время к началу суток.

Частые вопросы

Почему в журнале время отличается от интерфейса?

При включённых часовых поясах вывод переводится в пояс пользователя, а в журнал попадает то, что напечатали. Для служебных записей перевод отключают явно.

Как безопасно разобрать дату из формы?

Методом разбора с указанием ожидаемого формата и проверкой на пустой результат. Конструктор при неизвестном формате бросает исключение и роняет страницу.

Как правильно задать период в фильтре?

Полуоткрытым интервалом: от начала первого дня включительно до начала следующего дня строго. Так не теряются записи последних секунд суток.

Почему у даты без времени появилось 18:00?

Разбор текста без времени подставляет время окончания рабочего дня из настроек календаря. Если время важно, его задают явно.

Что хранить в базе: дату или дату со временем?

То, что нужно бизнесу: срок действия - датой, факт события - датой со временем. Тип даты нормализует время к нулю и незаметно теряет часы.

Смежное

Первоисточник