¿Debería MySQL tener su zona horaria establecida en UTC?

149

Pregunta de seguimiento de /server/191331/should-servers-have-their-timezone-set-to-gmt-utc

¿Debería establecerse la zona horaria MySQL en UTC o debería ser la misma zona horaria que el servidor o PHP? (Si no es UTC)

¿Cuáles son los pros y los contras?

Timo Huovinen
fuente
stackoverflow.com/a/1650406/175071 comparte buenas razones para usar UTC
Timo Huovinen
UTC no es una zona horaria. UTC es un estándar, GMT es una zona horaria. zachholman.com/talk/utc-is-enough-for-everyone-right
agoldev
Otra gran razón para usar UTC dba.stackexchange.com/questions/161416/…
Timo Huovinen

Respuestas:

533

Parece que no importa qué zona horaria esté en el servidor siempre y cuando tenga la hora establecida correctamente para la zona horaria actual, conozca la zona horaria de las columnas de fecha y hora que almacena y esté al tanto de los problemas con el horario de verano.

Por otro lado, si tiene el control de las zonas horarias de los servidores con los que trabaja, puede tener todo configurado en UTC internamente y nunca preocuparse por las zonas horarias y el horario de verano.

Aquí hay algunas notas que recopilé sobre cómo trabajar con zonas horarias como una forma de hoja de referencia para mí y para otros que podrían influir en la zona horaria que la persona elegirá para su servidor y cómo almacenará la fecha y la hora.

Hoja de referencia de zona horaria MySQL

Notas:

  1. Cambiar la zona horaria no cambiará la fecha y hora o la marca de tiempo almacenadas , pero seleccionará una fecha y hora diferente de las columnas de la marca de tiempo
  2. ¡Advertencia! UTC tiene segundos intermedios, estos se ven como '2012-06-30 23:59:60' y se pueden agregar al azar, con 6 meses de anticipación, debido a la desaceleración de la rotación de la tierra
  3. GMT confunde segundos, por eso se inventó UTC.

  4. ¡Advertencia! diferentes zonas horarias regionales pueden producir el mismo valor de fecha y hora debido al horario de verano

  5. La columna de fecha y hora solo admite fechas 1970-01-01 00:00:01 a 2038-01-19 03:14:07 UTC, debido a una limitación .
  6. Internamente, una columna de marca de tiempo de MySQL se almacena como UTC, pero al seleccionar una fecha, MySQL la convertirá automáticamente a la zona horaria de la sesión actual.

    Al almacenar una fecha en una marca de tiempo, MySQL asumirá que la fecha está en la zona horaria de la sesión actual y la convertirá a UTC para su almacenamiento.

  7. MySQL puede almacenar fechas parciales en columnas de fecha y hora, como "2013-00-00 04:00:00"
  8. MySQL almacena "0000-00-00 00:00:00" si configura una columna de fecha y hora como NULL, a menos que establezca específicamente la columna para permitir null cuando la cree.
  9. Lee esto

Para seleccionar una columna de marca de tiempo en formato UTC

No importa en qué zona horaria se encuentre la sesión MySQL actual:

SELECT 
CONVERT_TZ(`timestamp_field`, @@session.time_zone, '+00:00') AS `utc_datetime` 
FROM `table_name`

También puede establecer el servidor o la zona horaria de sesión global o actual en UTC y luego seleccionar la marca de tiempo de esta manera:

SELECT `timestamp_field` FROM `table_name`

Para seleccionar la fecha y hora actual en UTC:

SELECT UTC_TIMESTAMP();
SELECT UTC_TIMESTAMP;
SELECT CONVERT_TZ(NOW(), @@session.time_zone, '+00:00');

Resultado de ejemplo: 2015-03-24 17:02:41

Para seleccionar la fecha y hora actual en la zona horaria de la sesión

SELECT NOW();
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP();

Para seleccionar la zona horaria que se configuró cuando se inició el servidor

SELECT @@system_time_zone;

Devuelve "MSK" o "+04: 00" para la hora de Moscú, por ejemplo, hay (o hubo) un error de MySQL donde, si se configura en un desplazamiento numérico, no se ajustaría el horario de verano

Para obtener la zona horaria actual

SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);

Volverá a las 02:00:00 si su zona horaria es +2: 00.

Para obtener la marca de tiempo UNIX actual (en segundos):

SELECT UNIX_TIMESTAMP(NOW());
SELECT UNIX_TIMESTAMP();

Para obtener la columna de marca de tiempo como marca de tiempo UNIX

SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`

Para obtener una columna de fecha y hora UTC como marca de tiempo UNIX

SELECT UNIX_TIMESTAMP(CONVERT_TZ(`utc_datetime`, '+00:00', @@session.time_zone)) FROM `table_name`

Obtenga una fecha y hora de zona horaria actual de un entero de marca de tiempo de UNIX positivo

SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`

Obtenga una fecha y hora UTC de una marca de tiempo UNIX

SELECT CONVERT_TZ(FROM_UNIXTIME(`unix_timestamp_int`), @@session.time_zone, '+00:00') 
FROM `table_name`

Obtenga una fecha y hora de zona horaria actual a partir de un entero de marca de tiempo UNIX negativo

SELECT DATE_ADD('1970-01-01 00:00:00',INTERVAL -957632400 SECOND) 

Hay 3 lugares donde se puede establecer la zona horaria en MySQL:

Nota: una zona horaria se puede configurar en 2 formatos:

  1. un desplazamiento desde UTC: '+00: 00', '+10: 00' o '-6: 00'
  2. como zona horaria con nombre: 'Europa / Helsinki', 'EE. UU. / Este' o 'MET'

Las zonas horarias con nombre solo se pueden usar si las tablas de información de zona horaria en la base de datos mysql se han creado y completado.

en el archivo "my.cnf"

default_time_zone='+00:00'

o

timezone='UTC'

@@ global.time_zone variable

Para ver en qué valor están configurados

SELECT @@global.time_zone;

Para establecer un valor, use cualquiera de los dos:

SET GLOBAL time_zone = '+8:00';
SET GLOBAL time_zone = 'Europe/Helsinki';
SET @@global.time_zone='+00:00';

@@ session.time_zone variable

SELECT @@session.time_zone;

Para configurarlo, use cualquiera de los dos:

SET time_zone = 'Europe/Helsinki';
SET time_zone = "+00:00";
SET @@session.time_zone = "+00:00";

tanto "@@ global.time_zone variable" como "@@ session.time_zone variable" podrían devolver "SYSTEM", lo que significa que usan la zona horaria establecida en "my.cnf".

Para que los nombres de zonas horarias funcionen (incluso para la zona horaria predeterminada), debe configurar las tablas de información de zonas horarias que deben rellenarse: http://dev.mysql.com/doc/refman/5.1/en/time-zone-support. html

Nota: no puede hacer esto ya que devolverá NULL:

SELECT 
CONVERT_TZ(`timestamp_field`, TIMEDIFF(NOW(), UTC_TIMESTAMP), '+00:00') AS `utc_datetime` 
FROM `table_name`

Configurar tablas de zona horaria mysql

Para CONVERT_TZtrabajar, necesita que se llenen las tablas de zonas horarias

SELECT * FROM mysql.`time_zone` ;
SELECT * FROM mysql.`time_zone_leap_second` ;
SELECT * FROM mysql.`time_zone_name` ;
SELECT * FROM mysql.`time_zone_transition` ;
SELECT * FROM mysql.`time_zone_transition_type` ;

Si están vacíos, complételos ejecutando este comando

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

si este comando le da el error " datos demasiado largos para la columna 'abreviatura' en la fila 1 ", entonces podría ser causado por un carácter NULO al final de la abreviatura de la zona horaria

la solución es ejecutar esto

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
(if the above gives error "data too long for column 'abbreviation' at row 1")
mysql_tzinfo_to_sql /usr/share/zoneinfo > /tmp/zut.sql

echo "SET SESSION SQL_MODE = '';" > /tmp/mysql_tzinfo_to.sql
cat /tmp/zut.sql >> /tmp/mysql_tzinfo_to.sql

mysql --defaults-file=/etc/mysql/my.cnf --user=verifiedscratch -p mysql < /tmp/mysql_tzinfo_to.sql

(asegúrese de que las reglas dst de sus servidores estén actualizadas zdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/ )

Vea el historial completo de transición DST (horario de verano) para cada zona horaria

SELECT 
tzn.Name AS tz_name,
tztt.Abbreviation AS tz_abbr,
tztt.Is_DST AS is_dst,
tztt.`Offset` AS `offset`,
DATE_ADD('1970-01-01 00:00:00',INTERVAL tzt.Transition_time SECOND)  AS transition_date
FROM mysql.`time_zone_transition` tzt
INNER JOIN mysql.`time_zone_transition_type` tztt USING(Time_zone_id, Transition_type_id)
INNER JOIN mysql.`time_zone_name` tzn USING(Time_zone_id)
-- WHERE tzn.Name LIKE 'Europe/Moscow' -- Moscow has weird DST changes
ORDER BY tzt.Transition_time ASC

CONVERT_TZ también aplica los cambios de horario de verano necesarios según las reglas de las tablas anteriores y la fecha que utiliza.

Nota:
Según los documentos , el valor que establezca para time_zone no cambia, si lo configura como "+01: 00", por ejemplo, time_zone se establecerá como un desplazamiento de UTC, que no sigue DST, por lo que Se mantendrá igual durante todo el año.

Solo las zonas horarias nombradas cambiarán el horario durante el horario de verano.

Las abreviaturas como CETsiempre serán en invierno y en CESTverano, mientras que +01: 00 siempre serán en UTChora + 1 hora y ambas no cambiarán con el horario de verano.

La systemzona horaria será la zona horaria de la máquina host donde está instalado mysql (a menos que mysql no pueda determinarlo)

Puede leer más sobre trabajar con DST aquí

preguntas relacionadas:

Fuentes:

Timo Huovinen
fuente
Entonces, si tengo mi tipo de columna establecido en marca de tiempo. Y mi zona horaria es +12: 00, y quiero actualizar una columna usando una fecha / hora basada en utc, ¿hay alguna forma de incluir la zona horaria en la declaración de actualización, o debería usar convert_tz? P.ej. tableconjunto de actualizaciones modified= '2016-07-07 08:10 +00: 00'
bumperbox
2
@bumperbox mysql siempre asume que la fecha que le está dando a la columna de marca de tiempo está en la misma zona horaria que el servidor mysql, por lo que debe convertir su fecha de su zona horaria +12: 00 a la zona horaria de su servidor mysql para la actualización. Es por eso que uso UTC en el servidor mysql y convierto cualquier fecha a UTC antes de almacenarlo.
Timo Huovinen
55
Una de las mejores y más informativas respuestas que he encontrado en años de uso de SO. Gracias.
Mitya
¡¡¡ADVERTENCIA!!! Cualquier uso o conversión de la hora local en una zona horaria DST será incorrecto por una hora durante una hora cada año al final del horario de verano. Esto afecta UNIX_TIMESTAMP(NOW());, así como todos sus usos de CONVERT_TZ()donde uno de los parámetros es `@@ session.time_zone. Para convertir de manera confiable las horas UTC en marcas de tiempo UNIX, básicamente debe configurar primero la sesión time_zone.
Doin
1
@Flimm totalmente correcto, olvidé arreglar eso hace algún tiempo.
Timo Huovinen
3

Este es un ejemplo de trabajo:

jdbc:mysql://localhost:3306/database?useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Europe/Moscow
tatka
fuente
2

PHP y MySQL tienen sus propias configuraciones de zona horaria predeterminadas. Debe sincronizar el tiempo entre su base de datos y la aplicación web, de lo contrario podría ejecutar algunos problemas.

Lea este tutorial: Cómo sincronizar sus zonas horarias PHP y MySQL


fuente
Básicamente son dos líneas de código: ¡ date_default_timezone_set("America/Los_Angeles");y mysql_query("SET time_zone='" . date('P', time()) . "'");funcionó con mucha elegancia!
Noumenon el
3
@Noumenon ¡Cuidado con eso! Me he estado rascando la cabeza esta mañana porque eso es exactamente lo que estaba haciendo, y algunos de mis tiempos están libres por una hora. Lo que sospecho es que usar una zona horaria con nombre es más preciso cuando está involucrado el horario de verano. Si usa America / New_York, MySQL conoce el horario de verano y almacenará las fechas adecuadamente. Si lo configura en -04: 00 como lo tiene aquí, no tendrá en cuenta el cálculo del horario de verano.
nathanb
1
Verifique si mysql sabe sobre el horario de verano correctamente, las reglas del horario de verano se actualizan regularmente y las tablas de mysql relacionadas también necesitan actualizarse (ver arriba al final de mi respuesta)
Timo Huovinen
1

Los pros y los contras son prácticamente idénticos, depende de si quieres esto o no.

Tenga cuidado, si la zona horaria de MySQL difiere de la hora de su sistema (por ejemplo, PHP), comparar la hora o imprimir con el usuario implicará algunos ajustes.

alandarev
fuente