Гид по технологиям

Работа с датами и временем в MySQL

6 min read Базы данных Обновлено 15 Apr 2026
MySQL: работа с датами и временем
MySQL: работа с датами и временем

Используйте дату и время правильно в SQL: календарь и часы на экране

Даты и время — критичные данные в любом приложении: логи, события, билеты, финансовые операции. Правильная модель хранения и набор простых приёмов работы в базе данных упрощают разработку, обеспечивают корректность отчётов и уменьшают ошибки, связанные с часовыми поясами.

Основные понятия

  • UTC — всемирное координированное время, рекомендованный стандарт для хранения временных значений. Коротко: храните в UTC, отображайте в локальном времени.
  • TIMESTAMP — тип данных MySQL, который хранит точки времени и учитывает смещение сессии при отображении (конвертируется из/в время с учётом временной зоны сервера/сессии). Используйте, если хотите, чтобы СУБД помогала с конвертацией между зонами.
  • DATETIME — хранит «сырую» дату и время без привязки к часовому поясу; остаётся неизменным при извлечении. Подходит для времён без привязки к UTC (например, расписание открытий).

Работа с часовыми поясами

Важно стандартизировать рабочий протокол: при каждом подключении к базе переключайте часовую зону на UTC.

SET TIME_ZONE = '+0:00'

Пояснения:

  • Такой подход упрощает логику — все значения в базе в одном масштабе времени.
  • Для отображения локального времени используйте CONVERT_TZ(). Пример: если у вас в базе UTC, а пользователь на PST (UTC−08:00):
SELECT CONVERT_TZ('2021-02-04 21:47:23', '+0:00', '-8:00');

Результат: 2021-02-04 13:47:23.

Советы при работе с часовыми поясами:

  • Держите конфигурацию часовой зоны приложения и БД в документации проекта.
  • Для веб‑сервисов чаще всего проще сохранять в UTC и конвертировать в UI.
  • Если нужно вычислять интервалы «в локальном времени» (например, дневные отчёты по локальному дню пользователя), конвертируйте значения в локальную зону до группировок/фильтрации.

Important: В MySQL TIMESTAMP участвует в автоматической пересылке значений в соответствии с сессионной TZ, а DATETIME — нет. Выберите тип осознанно.

Добавление и вычитание интервалов

Для арифметики используйте DATE_ADD() и DATE_SUB(). Первый аргумент — исходная дата, второй — интервал в формате interval <число> <единица>.

Примеры:

SELECT DATE_SUB(now(), interval 2 week);
SELECT DATE_ADD('2021-02-07 11:52:06', interval 3 day);

Если нужно получить записи за последние 45 минут:

SELECT * FROM logins WHERE login_date >= DATE_SUB(now(), interval 45 minute);

Хорошая практика: используйте интервалные функции для читабельности, но помните про оптимизацию (см. раздел про индексы).

Разница между датами

  • DATEDIFF(a, b) возвращает количество дней между a и b (возвращает целое число дней).
SELECT DATEDIFF(now(), '2020-12-15');
  • Чтобы получить разницу в секундах, можно использовать TO_SECONDS():
SELECT TO_SECONDS(now()) - TO_SECONDS('2021-02-05 11:56:41');

Для других единиц (минуты, часы) можно комбинировать или использовать TIMESTAMPDIFF(unit, datetime1, datetime2).

SELECT TIMESTAMPDIFF(SECOND, '2021-02-05 11:56:41', now());

Извлечение частей даты

MySQL предоставляет набор функций для получения составляющих даты/времени. Все они принимают один аргумент — дату/время.

SELECT MONTH('2021-02-11 15:27:52');
SELECT HOUR(now());
SELECT DAYOFYEAR('2021-07-15 12:00:00');

Список часто используемых функций:

- SECOND()
- MINUTE()
- HOUR()
- DAY()
- WEEK() - Номер недели 0 - 52.
- MONTH()
- QUARTER() - Номер квартала 1 - 4.
- YEAR()
- DAYOFYEAR() - Номер дня в году (например, 15 сентября = 258 в невисокосный год).
- LAST_DAY() - Последний день месяца.
- DATE() - Дата в формате YYYY-MM-DD (без времени).
- TIME() - Время в формате HH:MI:SS (без даты).
- TO_DAYS() - Количество дней с A.D. 0.
- TO_SECONDS() - Количество секунд с A.D. 0.
- UNIX_TIMESTAMP() - Количество секунд с эпохи (1 янв. 1970).

Пример: получить месяц и год создания пользователей:

SELECT id, MONTH(created_at), YEAR(created_at) FROM users;

Группировка по периоду даты

Дата-функции полезны для агрегации. Пример: суммарная выручка по месяцам в 2020 году:

SELECT MONTH(created_at) AS month, SUM(amount) AS total
FROM orders
WHERE created_at BETWEEN '2020-01-01 00:00:00' AND '2020-12-31 23:59:59'
GROUP BY MONTH(created_at);

Важно: для сохранения производительности индексов лучше избегать применения YEAR() или других функций к индексируемым полям в WHERE. Вместо этого используйте BETWEEN или явные границы диапазонов.

Производительность: индексы и функции

Проблема: выражения вида WHERE DATE(created_at) = ‘2021-02-01’ делают индекс по created_at бесполезным, потому что функция применяется к столбцу. Решения:

  • Преобразуйте условие в диапазон:
WHERE created_at >= '2021-02-01 00:00:00' AND created_at < '2021-02-02 00:00:00'
  • Создайте функциональный индекс (если поддерживается вашей версией MySQL / MariaDB).
  • Храните отдельное поле с нормализованной датой (например, created_date DATE) и индексируйте его, если вы часто выполняете фильтрацию по дате без времени.

Ошибки и когда методы не подходят

  • Хранение локального времени в DATETIME без информации о временной зоне делает невозможным корректное восстановление локального времени, если пользователь переезжает.
  • Конвертировать все даты в локальную зону в БД неудобно при распределённой системе; лучше выполнять конвертацию в приложении.
  • Для исторических времён, затронутых переходом на летнее время или изменением правил TZ (например, правительственные изменения), хранение UTC + метаданных о локальной зоне полезнее простого локального времени.

Альтернативные подходы и модели зрелости

  • Хранение UTC + временная зона пользователя: полный контроль, позволяет корректно восстанавливать локальное время и учитывать переходы DST.
  • Хранение UTC только: простая модель для большинства приложений, подходит когда важна последовательность событий.
  • Хранение DATETIME локально: подходит для расписаний (например, «магазин открывается в 09:00 по местному времени»), но требует аккуратного проектирования при миграциях.

Уровни зрелости №:

  • Начальный: хранить в локальном времени (риск).
  • Базовый: хранить в UTC, конвертировать на клиенте.
  • Продвинутый: хранить UTC + зону пользователя, логировать исходный offset и использовать TZ-базы (IANA) при расчётах.

Быстрая памятка — cheat sheet функций

-- Переключить сессию на UTC
SET TIME_ZONE = '+0:00';

-- Перевести время в другой часовой пояс
SELECT CONVERT_TZ(now(), '+0:00', '+03:00');

-- Добавить/вычесть интервалы
SELECT DATE_ADD(now(), interval 1 day);
SELECT DATE_SUB(now(), interval 2 week);

-- Разница
SELECT DATEDIFF('2021-02-10', '2021-02-01');
SELECT TIMESTAMPDIFF(SECOND, '2021-02-05 11:56:41', now());

-- Извлечь часть
SELECT YEAR(created_at), MONTH(created_at), DAY(created_at) FROM events;

Проверки и критерии приёмки

  • Результаты выборки за границы дня совпадают с ожидаемым (проверить на нескольких временных зонах).
  • Индекс по created_at используется (подтвердить через EXPLAIN) при запросах по диапазону.
  • Конвертация времени для UI даёт те же значения, что и тестовые примеры для DST и стандартного времени.

Ролевые чек-листы

Разработчик:

  • Сохранять время в UTC.
  • Использовать DATE_ADD/DATE_SUB и TIMESTAMPDIFF.
  • Не применять функции к индексным столбцам в WHERE.

DBA:

  • Проверять использование индексов через EXPLAIN.
  • Настроить системную временную зону и инструкцию по переключению сессий.
  • Обеспечить корректные timezone tables (LOAD DATA) если требуется CONVERT_TZ по имени зоны.

Аналитик/BI:

  • Использовать BETWEEN для фильтрации по периодам.
  • Проверять смещение времён при агрегации по дням/месяцам.

Мини‑методология для миграции времени в UTC

  1. Проинвентаризировать все таблицы со временными полями.
  2. Определить, какие поля уже в UTC, а какие в локальном времени (по коду и документации).
  3. Для локальных DATETIME: определить смещения, конвертировать в UTC и сохранять обратно, пометив операцию миграции в changelog.
  4. Обновить код, чтобы при подключении выполнять SET TIME_ZONE = ‘+0:00’.
  5. Прогнать тесты на выборки границ (DST, переход года).

Безопасность и конфиденциальность

Отметьте, что временные метки могут считаться персональными данными, если по ним можно идентифицировать поведение пользователя. При требованиях GDPR/локального законодательства:

  • Минимизируйте хранение времени, если оно не нужно.
  • Установите политики удаления и ретеншна для логов.

Примеры отказов и крайние случаи

  • Если приложение работает в нескольких странах и важно сохранять местное время события (напр., отметка присутствия), храните UTC + original_offset или IANA timezone, чтобы можно было корректно восстановить локальное представление.
  • Для финансовых расчётов используйте фиксированные интервалы и тестируйте на границах кварталов/лет.

Визуальное руководство для выбора подхода (decision tree)

flowchart TD
  A[Нужно ли сохранять локальное временное представление события?] -->|Да| B[Хранить UTC + временную зону пользователя]
  A -->|Нет| C[Хранить UTC]
  B --> D{Требуется автоматическая конверсия в БД?}
  D -->|Да| E[Использовать TIMESTAMP и CONVERT_TZ]
  D -->|Нет| F[Хранить DATETIME UTC и конвертировать в приложении]
  C --> G{Нужна агрегированная аналитика по локальным дням?}
  G -->|Да| H[Перед агрегацией конвертировать в локальную зону]
  G -->|Нет| I[Оставить в UTC]

Краткое резюме

  • Всегда выбирать и документировать модель хранения времени (рекомендуется UTC).
  • Понимать разницу между TIMESTAMP и DATETIME.
  • Использовать DATE_ADD/DATE_SUB, CONVERT_TZ и TIMESTAMPDIFF для операций с временем.
  • Не применять функции к индексируемым столбцам в WHERE — вместо этого использовать диапазоны.

Notes: Регулярно проверяйте поведение запросов на границах времени (переходы на летнее/зимнее время, изменение правил TZ) и включайте тесты для таких сценариев.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Списки в Twitter: как организовать ленту
Социальные сети

Списки в Twitter: как организовать ленту

Как проверить, следит ли работодатель за ноутбуком
Безопасность

Как проверить, следит ли работодатель за ноутбуком

Инструменты рисования Markup на iPhone и iPad
iOS

Инструменты рисования Markup на iPhone и iPad

Скачивание на Netflix: как сохранять фильмы и сериалы
Руководство

Скачивание на Netflix: как сохранять фильмы и сериалы

Обновить DualSense по беспроводной связи на PS5
Игры

Обновить DualSense по беспроводной связи на PS5

Как сжать видео для отправки по почте
Руководство

Как сжать видео для отправки по почте