MySQL

La hora de MySQL se puede ver afectada por los cambios que los entes gubernamentales hacen a la hora local de los países pero también por la necesidad que tienen algunas regiones de adelantar o retrasar su hora dependiendo de la época del año para aprovechar la luz del día.

Lo ideal en estas situaciones es que MySQL haga el cambio del día y hora de forma automática, esto viene dado por la utilización de las zonas horarias (time zones en inglés). MySQL mantiene una serie de tablas en la base de datos mysql para almacenar esta información: time_zone, time_zone_name, time_zone_leap_second, time_zone_transition, y time_zone_transition_type.

Cargar la información de las zonas

Durante la instalación MySQL no carga información alguna en dichas tablas, puedes comprobarlo haciendo la siguiente consulta:

MariaDB [(none)]> SELECT COUNT(*) FROM mysql.time_zone_name;

+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+

Si tu sistema operativo soporta la base de datos zoneinfo, el cual es un conjunto de archivos binarios que describen la información de las zonas, lo recomendable es cargarlas con la utilidad mysql_tzinfo_to_sql. Ejemplos de tales sistemas son: Linux, MacOS en algunas versiones, FreeBSD, Solaris. Ellos por lo general la mantienen en el directorio /usr/share/zoneinfo.

Puede ser que la ruta no coincida en tu sistema operativo así que todavía puedes intentar con el siguiente comando para determinar su ubicación:

ls -la /etc/localtime

lrwxrwxrwx 1 root root 37 oct 16 09:29 /etc/localtime -> ../usr/share/zoneinfo/America/Caracas

Ahora procedemos a indicarle la ubicación a la utilidad mysql_tzinfo_to_sql para cargar todas las zonas disponibles allí. La utilidad genera un script SQL así que debemos usar el comando pipe para que estas instrucciones se ejecuten desde MySQL y llenen las tablas adecuadas, el comando de consola es el siguiente:

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p mysql

Comprueba que has tenido éxito, repitiendo la consulta de las zonas y deben tener una cuenta mayor que cero. Después de esto es necesario que reinicies el servidor MySQL para que tome los cambios.

Si tu sistema operativo no ofrece soporte para zoneinfo sino que tiene una base de datos de zonas propia tal como Windows, entonces debes usar un script que contiene las instrucciones SQL que permiten cargar las tablas con la información más reciente de la base de datos de zoneinfo.

  • El procedimiento para cargar las tablas varía ligeramente:
  • Descarga el script desde https://dev.mysql.com/downloads/timezones.html
  • Escoge el archivo que contenga el sufijo sql en su nombre de archivo o file_name Por ejemplo : timezone_2019c_posix_sql.zip
  • Desempaquétalo
  • Carga las tablas con la siguiente instrucción: mysql -u root -p mysql < file_name

Ver los nombres de las zona en zoneinfo

En el directorio donde está la base de datos zoneinfo puedes ver las zonas disponibles. Puedes determinar el nombre de una zona mediante su path relativo, por ejemplo: America/Caracas, US/Eastern. Como puedes imaginar, generalmente antepones el nombre del continente seguido por el nombre de la ciudad excepto en aquellos casos como Brazil, US que contienen varias zonas.

Si no posees la base de datos zoneinfo puedes encontrar el nombre de una zona haciendo la consulta directamente en las tablas de MySQL, por ejemplo:

MariaDB [(none)]> SELECT * FROM mysql.time_zone_name WHERE name LIKE '%Caracas';

+-----------------------+--------------+
| Name                  | Time_zone_id |
+-----------------------+--------------+
| America/Caracas       |           90 |
| posix/America/Caracas |          697 |
| right/America/Caracas |         1304 |
+-----------------------+--------------+

Consultar la información en zoneinfo

Puesto que son archivos binarios la información debe ser consultada con una utilidad, en Linux es zdump:

Veamos un ejemplo simplificado para el año 2019 en Europe/Madrid:

zdump -v Europe/Madrid | grep 2019
Europe/Madrid  Sun Mar 31 00:59:59 2019 UT = Sun Mar 31 01:59:59 2019 CET isdst=0 gmtoff=3600
Europe/Madrid  Sun Mar 31 01:00:00 2019 UT = Sun Mar 31 03:00:00 2019 CEST isdst=1 gmtoff=7200
Europe/Madrid  Sun Oct 27 00:59:59 2019 UT = Sun Oct 27 02:59:59 2019 CEST isdst=1 gmtoff=7200
Europe/Madrid  Sun Oct 27 01:00:00 2019 UT = Sun Oct 27 02:00:00 2019 CET isdst=0 gmtoff=3600

Puedes apreciar claramente el inicio del horario de verano CEST con el cambio de las dos primeras líneas en el campo isdst=1 (es DST igual a verdadero) el 31 de Marzo de 2019 a las 2:00 de la madrugada en el horario de invierno o CET. También puedes ver la hora correspondiente en UTC, y el desplazamiento de UTC en segundos en el campo gmtoff (gmt offset).

Comprobar un cambio de hora legal

Veamos el cambio más reciente en Venezuela, haré la consulta de la base de datos en Linux pero si no es tu caso con revisar el anuncio del cambio de hora en tu país será suficiente.

zdump -v America/Caracas | grep 2016
America/Caracas  Sun May  1 06:59:59 2016 UT = Sun May  1 02:29:59 2016 -0430 isdst=0 gmtoff=-16200
America/Caracas  Sun May  1 07:00:00 2016 UT = Sun May  1 03:00:00 2016 -04 isdst=0 gmtoff=-14400

Lo que interesa es demostrar que MySQL está preparado para el cambio del día domingo 1ro de Mayo de 2016 a las 2:30 de la madrugada. Para lo cuál utilizaré la función CONVERT_TZ en la siguiente instrucción:

SELECT 
  CONVERT_TZ('2016-05-01 02:30:00', 'America/Caracas', 'America/Caracas') AS time1,
  CONVERT_TZ('2016-05-01 03:00:00', 'America/Caracas', 'America/Caracas') AS time2;

+---------------------+---------------------+
| time1               | time2               |
+---------------------+---------------------+
| 2016-05-01 03:00:00 | 2016-05-01 03:00:00 |
+---------------------+---------------------+

El resultado debe ser el mismo para que la comprobación sea exitosa.

Pasada esa fecha ya no hay más ajuste de hora legal por lo tanto la hora debe cambiar porque son momentos diferentes, por ejemplo:

SELECT 
  CONVERT_TZ('2016-05-01 03:30:00', 'America/Caracas', 'America/Caracas') AS time1,
  CONVERT_TZ('2016-05-01 04:00:00', 'America/Caracas', 'America/Caracas') AS time2;

+---------------------+---------------------+
| time1               | time2               |
+---------------------+---------------------+
| 2016-05-01 03:30:00 | 2016-05-01 04:00:00 |
+---------------------+---------------------+

Comprobar un cambio DST

Verificaremos que MySQL está preparado para hacer el cambio de la hora para ello haremos un ejemplo cuando de inicio el horario de verano en España:

SELECT 
  CONVERT_TZ('2019-03-31 02:00:00', 'Europe/Madrid', 'Europe/Madrid') AS time1,
  CONVERT_TZ('2019-03-31 03:00:00', 'Europe/Madrid', 'Europe/Madrid') AS time2;

+---------------------+---------------------+
| time1               | time2               |
+---------------------+---------------------+
| 2019-03-31 03:00:00 | 2019-03-31 03:00:00 |
+---------------------+---------------------+

Observa que no hay diferencia entre la hora a las 2:00 que a las 3:00.

Mantener actualizada la información de las zonas

Tu sistema operativo debe proporcionar una actualización o service pack para mantener la hora de tu sistema actualizada de forma regular.

En linux, debes hacerlo actualizando el paquete tzdata:

sudo apt-get update
sudo apt-get upgrade tzdata

Este paquete se encarga de descargar las actualizaciones que tiene la base de datos zoneinfo.

Cada vez que tengas necesidad de actualizar las zoneinfo también es necesario que vuelvas a repetir el procedimiento de carga de la información de las zonas horarias de MySQL para que se mantenga sincronizada con la de tu sistema operativo.

Espero que este post te haya proporcionado suficiente información para resolver el problema de mantener actualizada la hora del reloj de tu servidor MySQL ante los cambios de hora legal y DST, pero si estás interesado en profundizar aún más tus conocimientos sobre el manejo de SQL te recomiendo que veas nuestro libro Aprende SQL de Styde donde encontrarás unos 20 capítulos que aumentarán tu confianza al momento que tengas que afrontar proyectos que requieran del manejo de base de datos.

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.