Блог / Статьи

Полезная информация для вашего хостинга

Как привести в порядок время в MySQL, PHP и системе

Как привести в порядок время в MySQL, PHP и системе

В мире веб-разработки есть немало «тихих убийц» — тех незаметных на первый взгляд проблем, которые способны подорвать стабильность даже самого продуманного проекта. Одной из таких коварных ловушек является рассинхронизация временных зон между операционной системой, PHP и MySQL. На первый взгляд, всё работает: сайт отображается, заказы поступают, база данных наполняется. Но стоит заглянуть чуть глубже — и выясняется, что события в логах происходят «в будущем», заказы фиксируются с задержкой в несколько часов, а CRON-задачи запускаются вовсе не тогда, когда должны. Всё это — не магия, не баг, не «странное поведение сервера», а прямое следствие того, что каждый компонент системы живёт в своём часовом поясе.

В этой статье мы погрузимся в суть проблемы, научимся диагностировать её на ранней стадии, и главное — разберёмся, как полностью синхронизировать время между MySQL, PHP и операционной системой. Вы получите пошаговый алгоритм, готовые примеры кода и практические рекомендации, которые помогут избежать временного хаоса на любом хостинге — от локального сервера до облачного кластера.

Суть временного разлада: почему одни часы спешат, а другие — отстают

Операционная система, PHP и MySQL — это три независимых «организма», каждый из которых по-своему понимает, что такое «сейчас». Даже если они работают на одном физическом сервере, их представление о текущем времени может кардинально отличаться.

  • Операционная система (например, Ubuntu, CentOS или Debian) хранит системное время и часовую зону в файле /etc/localtime и управляется через timedatectl. По умолчанию она может быть установлена в UTC или в локальном поясе (например, Europe/Moscow).
  • PHP не наследует автоматически системную зону. Он полагается либо на параметр date.timezone в файле php.ini, либо на функцию date_default_timezone_set() в коде. Если ни то, ни другое не задано — PHP выдаст предупреждение и будет использовать UTC.
  • MySQL, в свою очередь, имеет собственную систему управления временными зонами. При старте он может использовать либо системную зону (если указано SYSTEM), либо значение по умолчанию из конфигурации. Но даже при значении SYSTEM MySQL не всегда корректно считывает информацию из ОС — особенно если не загружена таблица временных зон (mysql.time_zone%).

В результате типичная картина такова: сервер работает в Europe/Moscow (+03:00), PHP настроен на ту же зону, но MySQL по умолчанию использует UTC (+00:00). Выполняя запрос SELECT NOW();, вы получаете время, отстающее на три часа от того, что показывает date('Y-m-d H:i:s') в PHP. Это не ошибка — это несогласованность конфигураций.

time01

Диагностика: как понять, где именно «сломалось» время

Прежде чем что-то исправлять, нужно точно определить, кто и какое время показывает. Ниже — пошаговая инструкция для полной диагностики.

1. Проверка системного времени

Выполните в терминале: 

date
timedatectl

 Команда date покажет текущее локальное время сервера. timedatectl даст расширенную информацию, включая часовую зону и синхронизацию с NTP.

2. Проверка времени в PHP

Через командную строку:

php -r "echo date('Y-m-d H:i:s T');"

Или создайте временный файл time.php:

Запустите его через браузер или CLI. Обратите внимание на аббревиатуру часового пояса (например, MSK или UTC).

3. Проверка времени в MySQL

Подключитесь к MySQL и выполните:

SELECT NOW() AS mysql_now,
       @@global.time_zone AS global_tz,
       @@session.time_zone AS session_tz;

Если в колонках global_tz или session_tz стоит SYSTEM — MySQL пытается использовать системную зону. Если +00:00 — он работает в UTC. Сравните значение mysql_now с тем, что показывает PHP. Разница в несколько часов — верный признак рассинхронизации.

time04

Как временно настроить часовую зону в MySQL

Если вам нужно срочно привести MySQL в соответствие с PHP (например, для отладки или временного решения), можно изменить часовую зону «на лету» без перезапуска сервера.

Подключитесь к MySQL от имени пользователя с правами SUPER (обычно это root) и выполните:

SET GLOBAL time_zone = '+03:00';

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

SET GLOBAL time_zone = 'Europe/Moscow';

⚠️ Важно: для использования именованных зон (вроде Europe/Moscow) в MySQL должна быть загружена таблица временных зон. Проверить это можно так:

SELECT * FROM mysql.time_zone_name LIMIT 5;

Если таблица пуста, её нужно загрузить с помощью утилиты mysql_tzinfo_to_sql:

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql

После установки глобальной зоны все новые сессии будут использовать её. Уже открытые сессии — нет. Чтобы изменить зону только для текущей сессии, используйте:

SET time_zone = 'Europe/Moscow';

Проверьте результат:

SELECT NOW();

Теперь время должно совпадать с PHP (при условии, что PHP тоже настроен правильно).

Как навсегда зафиксировать часовую зону в MySQL

Временное решение — это хорошо, но после перезапуска MySQL настройки сбросятся. Чтобы избежать этого, нужно прописать зону в конфигурационном файле.

Найдите основной конфигурационный файл MySQL. Расположение зависит от дистрибутива:

  • Ubuntu/Debian: /etc/mysql/my.cnf или /etc/mysql/mysql.conf.d/mysqld.cnf
  • CentOS/RHEL: /etc/my.cnf

Откройте файл и в секцию [mysqld] добавьте строку:

[mysqld]
default_time_zone = 'Europe/Moscow'

Или, если вы предпочитаете смещение:

[mysqld]
default_time_zone = '+03:00'

Сохраните файл и перезапустите MySQL:

sudo systemctl restart mysql

или

sudo systemctl restart mysqld

После перезапуска проверьте настройку:

SELECT @@global.time_zone;

Если всё сделано верно, вы увидите Europe/Moscow.

Синхронизация PHP и MySQL: единая стратегия времени

Чтобы избежать будущих проблем, нужно привести к единому знаменателю все три компонента: ОС, PHP и MySQL.

Шаг 1: Установите правильную зону на уровне ОС

sudo timedatectl set-timezone Europe/Moscow

Это обеспечит корректное системное время для всех служб, включая cron.

Шаг 2: Настройте PHP

Вариант A — через php.ini:

date.timezone = "Europe/Moscow"

После изменения перезагрузите веб-сервер (Apache/Nginx) или PHP-FPM:

sudo systemctl reload apache2
# или
sudo systemctl reload php8.2-fpm

Вариант B — через код (менее надёжно, но допустимо для небольших проектов):

Шаг 3: Настройте MySQL

Как описано выше — через конфигурационный файл с последующим перезапуском.

Теперь все три компонента работают в одной временной реальности. Выполните финальную проверку:

# Система
date

# PHP
php -r "echo date('Y-m-d H:i:s T');"

# MySQL
mysql -e "SELECT NOW(), @@global.time_zone;"

Если все три значения совпадают (с учётом формата), вы победили временной хаос.

Почему это критически важно: последствия игнорирования временных зон

На первый взгляд, разница в несколько часов — это «мелочь». Но на практике она может привести к серьёзным последствиям:

  • Некорректная аналитика: пользователь зашёл в 18:00, а в логах записано 15:00 — метрики поведения искажаются.
  • Сбои в логике приложения: условия вроде «если сейчас после 18:00 — показать акцию» перестают работать.
  • Проблемы с CRON: задача запланирована на 02:00 по серверу, но PHP думает, что сейчас 05:00 — и пропускает выполнение.
  • Ошибки при работе с TIMESTAMP: в MySQL тип TIMESTAMP автоматически конвертируется в UTC при сохранении и обратно при выборке — но только если зоны настроены правильно. Иначе данные искажаются.
  • Сложности при миграции: перенос сайта на другой сервер с иной зоной приведёт к полному временному коллапсу, если не учитывать настройки.

В системах, где важна юридическая или финансовая точность (онлайн-кассы, CRM, бухгалтерские модули), такие ошибки могут повлечь за собой штрафы, недоверие клиентов и даже судебные разбирательства.

Профессиональные советы: как работать со временем без риска

Даже если вы настроили всё идеально, стоит придерживаться следующих практик:

  1. Храните всё время в UTC. Это золотое правило. Сохраняйте даты в базе в UTC, а конвертируйте в локальную зону только при отображении. Это особенно актуально для международных проектов.
  2. Используйте DATETIME вместо TIMESTAMP, если не нужна автоматическая конвертация. DATETIME хранит «как есть», без привязки к зоне.
  3. Применяйте CONVERT_TZ() в MySQL для явного перевода:
    SELECT CONVERT_TZ('2025-10-16 12:00:00', '+00:00', 'Europe/Moscow');
  4. Для Docker-контейнеров монтируйте системные файлы времени:
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
  5. Не полагайтесь на SYSTEM в MySQL. Лучше явно указать зону в конфиге — это делает поведение предсказуемым.
  6. Тестируйте на разных серверах. Локальная разработка часто ведётся в другом поясе, чем продакшен. Используйте виртуальные машины или контейнеры с идентичной конфигурацией.

time05

Заключение: восстановление временного порядка

Рассинхронизация времени между MySQL, PHP и операционной системой — это не таинственная ошибка, а вполне предсказуемая ситуация, вызванная отсутствием единообразия в настройках. Как только вы осознаете, что каждый компонент управляет временем независимо, проблема перестаёт быть пугающей.

Следуйте простому алгоритму:

  1. Проверьте текущее время во всех трёх слоях.
  2. Установите единую часовую зону на уровне ОС.
  3. Пропишите её в php.ini.
  4. Задайте default_time_zone в конфигурации MySQL.
  5. Перезапустите службы и убедитесь, что всё совпадает.

И помните: одна строка SQL — SELECT NOW(), @@global.time_zone, @@session.time_zone; — может спасти вас от часов отладки и недель путаницы в логах. Настройте время один раз — и забудьте о нём навсегда. Потому что в идеальном веб-проекте часы должны идти не просто точно, а в унисон.