Al trabajar con MySQL, es común toparse con inconvenientes relacionados con la zona horaria: visualización incorrecta de fechas, desfase respecto a la zona horaria local (UTC+8), diferencias entre la hora que devuelve la aplicación y la almacenada en la base de datos, etc. Todos estos problemas suelen originarse en los parámetros de zona horaria de la base de datos. Este artículo explica en detalle los parámetros clave y cómo manejarlos.
1. Parámetro log_timestamps
En primer lugar, cabe aclarar que log_timestamps no afecta la zona horaria de la base de datos; solo influye en la marca de tiempo de ciertos registros. Controla la zona horaria mostrada en los archivos de log (error log, slow log, general log), pero no afecta los registros escritos en tablas (mysql.general_log, mysql.slow_log).
Es un parámetro global que se puede modificar dinámicamente. Por defecto usa UTC, por lo que las horas en los logs aparecen 8 horas atrasadas respecto a la hora de Beijing. Se puede cambiar a SYSTEM para que use la zona horaria del sistema operativo. A continuación se muestra su funcionamiento y cómo modificarlo:
-- Ver valor actual
SHOW GLOBAL VARIABLES LIKE 'log_timestamps';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| log_timestamps | UTC |
+----------------+-------+
-- Generar un slow query
SELECT SLEEP(10), NOW();
+-----------+---------------------+
| sleep(10) | now() |
+-----------+---------------------+
| 0 | 2020-06-24 17:12:40 |
+-----------+---------------------+
-- Contenido del slow log (hora UTC)
# Time: 2020-06-24T09:12:50.555348Z
# Query_time: 10.000354 Lock_time: 0.000000
SET timestamp=1592989960;
SELECT SLEEP(10), NOW();
-- Cambiar a SYSTEM
SET GLOBAL log_timestamps = SYSTEM;
-- Volver a generar slow query
SELECT SLEEP(10), NOW();
-- Ahora el slow log muestra hora correcta (UTC+8)
# Time: 2020-06-24T17:13:54.514413+08:00
2. Parámetro time_zone
El parámetro time_zone define la zona horaria de cada sesión. Tiene alcance global y de sesión, y se puede modificar en caliente. Su valor predeterminado es SYSTEM, que hereda la zona horaria del sistema operativo (system_time_zone). Afecta la visualización y almacenamiento de valores sensibles a la zona horaria, como las funciones NOW(), CURTIME(), y los valores de columnas TIMESTAMP. No afecta a DATE, TIME ni DATETIME, porque estos tipos no se convierten al almacenarse. Las columnas TIMESTAMP se guardan internamente como UTC y se convierten según la zona horaria configurada al recuperarse.
Ejemplo que ilustra el cambio de time_zone:
-- Ver zona horaria del sistema y MySQL
SHOW GLOBAL VARIABLES LIKE '%time_zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | SYSTEM |
+------------------+--------+
SELECT NOW();
+---------------------+
| now() |
+---------------------+
| 2020-06-28 14:31:12 |
+---------------------+
-- Crear tabla de prueba
CREATE TABLE test_timezone (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
dt_col DATETIME,
ts_col TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO test_timezone (dt_col, ts_col) VALUES
('2020-06-01 17:30:00', '2020-06-01 17:30:00'),
(NOW(), NOW());
SELECT * FROM test_timezone;
+----+---------------------+---------------------+
| id | dt_col | ts_col |
+----+---------------------+---------------------+
| 1 | 2020-06-01 17:30:00 | 2020-06-01 17:30:00 |
| 2 | 2020-06-28 14:34:55 | 2020-06-28 14:34:55 |
+----+---------------------+---------------------+
-- Cambiar a UTC
SET GLOBAL time_zone = '+0:00';
SET time_zone = '+0:00';
SELECT NOW();
+---------------------+
| now() |
+---------------------+
| 2020-06-28 06:36:16 |
+---------------------+
SELECT * FROM test_timezone;
+----+---------------------+---------------------+
| id | dt_col | ts_col |
+----+---------------------+---------------------+
| 1 | 2020-06-01 17:30:00 | 2020-06-01 09:30:00 |
| 2 | 2020-06-28 14:34:55 | 2020-06-28 06:34:55 |
+----+---------------------+---------------------+
-- Restaurar a UTC+8
SET GLOBAL time_zone = '+8:00';
SET time_zone = '+8:00';
SELECT * FROM test_timezone;
+----+---------------------+---------------------+
| id | dt_col | ts_col |
+----+---------------------+---------------------+
| 1 | 2020-06-01 17:30:00 | 2020-06-01 17:30:00 |
| 2 | 2020-06-28 14:34:55 | 2020-06-28 14:34:55 |
+----+---------------------+---------------------+
Para hacer el cambio permanente, agregue default_time_zone = '+8:00' en la sección [mysqld] del archivo de configuración de MySQL.
3. Problemas comunes y soluciones
Una mala configuración de zona horaria puede provocar varios inconvenientes. A continuación se describen los más frecuentes y cómo resolverlos:
3.1 La hora interna de MySQL no coincide con la hora local
Verifique primero la hora y zona horaria del sistema. Luego, revise time_zone en MySQL y ajústelo a '+8:00' (o la zona deseada).
3.2 Diferencias de 8 horas entre la aplicación Java y la base de datos
Lo más probable es que la zona horaria de la aplicación y la base de datos no coincidan. Para unificar la hora de Beijing, agregue serverTimezone=Asia/Shanghai en la cadena de conexión JDBC y, en MySQL, configure time_zone = '+8:00'.
3.3 Diferencias de 13 o 14 horas
Este problema surge por una interpretación conflictiva de la abreviatura "CST" entre JDBC y MySQL. CST puede significar:
- Hora estándar central de EE. UU. (UTC–05:00 o UTC–06:00)
- Hora estándar central de Australia (UTC+09:30)
- Hora estándar de China (UTC+08:00)
- Hora estándar de Cuba (UTC–04:00)
MySQL interpreta CST como UTC+08:00 (cuando hereda del sistema), pero JDBC lo interpreta como hora central de EE. UU., generando una diferencia de 13 horas (o 14 en horario de invierno). La solución es evitar el uso de CST: configure explícitamente time_zone = '+8:00' en MySQL y serverTimezone=Asia/Shanghai en la conexión JDBC.
3.4 Cómo prevenir problemas de zona horaria
- Asegúrese de que la zona horaria del sistema operativo sea correcta.
- Especifique la zona horaria en la cadena JDBC, consistente con la de la base de datos.
- Fije
time_zonea un valor explícito como'+8:00'en lugar de dejarlo como SYSTEM (que usa CST). - Mantenga la misma zona horaria en todas las instancias de la base de datos de los diferentes entornos.
Incluso si actualmente no se presentan problemas, se recomienda cambiar time_zone de SYSTEM a un valor fijo como '+8:00'. Esto evita la conversión de zona horaria mediante llamadas al sistema que involucran un bloqueo global (__libc_lock_lock), lo que puede degradar el rendimiento en entornos concurrentes. Al usar un valor fijo, MySQL realiza la conversión internamente, mejorando el rendimiento.