O MySQL deve ter seu fuso horário definido como UTC?

149

Pergunta de acompanhamento de /server/191331/should-servers-have-their-timezone-set-to-gmt-utc

O fuso horário do MySQL deve estar definido como UTC ou o mesmo fuso horário que o servidor ou PHP está definido? (Se não for UTC)

Quais são os prós e contras?

Timo Huovinen
fonte
stackoverflow.com/a/1650406/175071 compartilha boas razões para usar o UTC
Timo Huovinen 14/03
UTC não é um fuso horário. UTC é um padrão, GMT é um fuso horário. zachholman.com/talk/utc-is-enough-for-everyone-right
agoldev
Outro ótimo motivo para usar o UTC dba.stackexchange.com/questions/161416/…
Timo Huovinen

Respostas:

533

Parece que não importa qual fuso horário está no servidor, desde que você tenha o horário definido para o fuso horário atual, saiba o fuso horário das colunas de data e hora armazenadas e esteja ciente dos problemas com o horário de verão.

Por outro lado, se você tiver o controle dos fusos horários dos servidores com os quais trabalha, poderá definir tudo internamente para o UTC e nunca se preocupar com fusos horários e horário de verão.

Aqui estão algumas anotações que coletei sobre como trabalhar com fusos horários como uma forma de cheatsheet para mim e para outras pessoas que podem influenciar o fuso horário que a pessoa escolherá para o servidor e como armazenará data e hora.

Cheatsheet do MySQL Timezone

Notas:

  1. Alterar o fuso horário não alterará o horário ou o carimbo de data e hora armazenados , mas selecionará um horário diferente das colunas do carimbo de data / hora
  2. Aviso! O UTC tem segundos bissextos, eles se parecem com '2012-06-30 23:59:60' e podem ser adicionados aleatoriamente, com 6 meses de aviso prévio, devido à desaceleração da rotação da Terra
  3. GMT confunde segundos, e é por isso que o UTC foi inventado.

  4. Aviso! fusos horários regionais diferentes podem produzir o mesmo valor de data e hora devido ao horário de verão

  5. A coluna timestamp suporta apenas datas 1970-01-01 00:00:01 a 2038-01-19 03:14:07 UTC, devido a uma limitação .
  6. Internamente, uma coluna de registro de data e hora do MySQL é armazenada como UTC, mas ao selecionar uma data, o MySQL o converterá automaticamente no fuso horário da sessão atual.

    Ao armazenar uma data em um registro de data e hora, o MySQL assumirá que a data está no fuso horário da sessão atual e a converterá em UTC para armazenamento.

  7. O MySQL pode armazenar datas parciais em colunas de data e hora, elas se parecem com "2013-00-00 04:00:00"
  8. O MySQL armazena "0000-00-00 00:00:00" se você definir uma coluna de data e hora como NULL, a menos que você defina especificamente a coluna para permitir nulo ao criá-la.
  9. Leia isso

Para selecionar uma coluna de carimbo de data / hora no formato UTC

não importa em que fuso horário está a sessão atual do MySQL:

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

Você também pode definir o fuso horário da sessão global ou global ou atual como UTC e selecionar o carimbo de data e hora da seguinte forma:

SELECT `timestamp_field` FROM `table_name`

Para selecionar a data e hora atual no UTC:

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

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

Para selecionar o horário atual no fuso horário da sessão

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

Para selecionar o fuso horário definido quando o servidor foi iniciado

SELECT @@system_time_zone;

Retorna "MSK" ou "+04: 00" para o horário de Moscou, por exemplo, existe (ou houve) um erro do MySQL em que, se definido como um deslocamento numérico, não ajustaria o horário de verão

Para obter o fuso horário atual

SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);

Ele retornará 02:00:00 se o seu fuso horário for +2: 00.

Para obter o registro de data e hora atual do UNIX (em segundos):

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

Para obter a coluna do registro de data e hora como um registro de data e hora do UNIX

SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`

Para obter uma coluna de data e hora UTC como um carimbo de data / hora UNIX

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

Obter uma data e hora atuais do fuso horário de um número inteiro de carimbo de data / hora UNIX positivo

SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`

Obter uma data e hora UTC de um registro de data e hora UNIX

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

Obter uma data e hora do fuso horário atual de um número inteiro de carimbo de data e hora UNIX negativo

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

Existem 3 locais onde o fuso horário pode ser definido no MySQL:

Nota: Um fuso horário pode ser definido em 2 formatos:

  1. um deslocamento do UTC: '+00: 00', '+10: 00' ou '-6: 00'
  2. como um fuso horário nomeado: 'Europa / Helsinque', 'EUA / Leste' ou 'MET'

Os fusos horários nomeados podem ser usados ​​apenas se as tabelas de informações do fuso horário no banco de dados mysql tiverem sido criadas e preenchidas.

no arquivo "my.cnf"

default_time_zone='+00:00'

ou

timezone='UTC'

variável global@time_zone @@

Para ver em que valor eles estão definidos

SELECT @@global.time_zone;

Para definir um valor para ele, use um dos seguintes:

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

variável @@ session.time_zone

SELECT @@session.time_zone;

Para configurá-lo, use um dos seguintes:

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

ambas as variáveis ​​"@@ global.time_zone" e "@@ session.time_zone" podem retornar "SYSTEM", o que significa que eles usam o fuso horário definido em "my.cnf".

Para que os nomes de fuso horário funcionem (mesmo para o fuso horário padrão), você deve configurar suas tabelas de informações de fuso horário: http://dev.mysql.com/doc/refman/5.1/en/time-zone-support. html

Nota: você não pode fazer isso, pois retornará NULL:

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

Configurar tabelas de fuso horário do mysql

Para CONVERT_TZfuncionar, você precisa que as tabelas de fuso horário sejam preenchidas

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` ;

Se estiverem vazios, preencha-os executando este comando

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

se esse comando fornecer o erro " dados muito longos para a coluna 'abreviação' na linha 1 ", poderá ser causado por um caractere NULL sendo anexado no final da abreviação do fuso horário

a correção é para executar isso

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

(verifique se as regras de dst dos servidores estão atualizadas zdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/ )

Veja o histórico completo de transição DST (Horário de Verão) para cada fuso horário

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 também aplica as alterações necessárias no horário de verão com base nas regras nas tabelas acima e na data em que você usa.

Nota:
De acordo com os documentos , o valor que você definiu para o time_zone não muda; se você o define como "+01: 00", por exemplo, o time_zone será definido como um deslocamento do UTC, que não segue o horário de verão. permanecerá o mesmo o ano todo.

Somente os fusos horários nomeados mudarão o horário durante o horário de verão.

Abreviações como CETsempre serão de inverno e CESTverão e +01: 00 sempre serão de UTC+ 1 hora e ambas não serão alteradas com o horário de verão.

O systemfuso horário será o fuso horário da máquina host em que o mysql está instalado (a menos que o mysql não consiga determiná-lo)

Você pode ler mais sobre como trabalhar com o horário de verão aqui

Perguntas relacionadas:

Fontes:

Timo Huovinen
fonte
Portanto, se eu tiver meu tipo de coluna definido como carimbo de data e hora. E meu time_zone é +12: 00, e eu quero atualizar uma coluna usando uma data / hora baseada em utc, existe alguma maneira de incluir o fuso horário na instrução de atualização ou devo usar convert_tz. Por exemplo. update tableset modified= '2016-07-07 08:10 +00: 00'
bumperbox
2
O @bumperbox mysql sempre assume que a data que você está dando à coluna timestamp está no mesmo fuso horário do servidor mysql, portanto, você precisa converter a data do fuso horário +12: 00 para o fuso horário do servidor mysql para a atualização. É por isso que eu uso o UTC no servidor mysql e converto qualquer data para o UTC antes de armazená-lo.
Timo Huovinen
5
Uma das melhores e mais informativas respostas que já encontrei nos últimos anos usando o SO. Obrigado.
Mitya
AVISO!!! Qualquer uso ou conversão da hora local em um fuso horário do horário de verão estará errado por uma hora durante uma hora a cada ano no final do horário de verão. Isso afeta UNIX_TIMESTAMP(NOW());todos os usos de CONVERT_TZ()onde um dos parâmetros é `@@ session.time_zone. Para converter de maneira confiável datas e horários UTC em registros de data e hora UNIX, basicamente é necessário definir a sessão time_zone primeiro.
Doin
1
@ Limlim totalmente certo, eu esqueci de corrigir isso há algum tempo.
Timo Huovinen
3

Este é um exemplo de trabalho:

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

PHP e MySQL têm suas próprias configurações de fuso horário padrão. Você deve sincronizar o tempo entre o banco de dados e o aplicativo da Web, caso contrário, poderá executar alguns problemas.

Leia este tutorial: Como sincronizar seus fusos horários PHP e MySQL


fonte
São basicamente duas linhas de código: date_default_timezone_set("America/Los_Angeles");e mysql_query("SET time_zone='" . date('P', time()) . "'");funcionaram de maneira muito elegante!
Novmen
3
@Noumenon Cuidado com isso! Estive coçando a cabeça hoje de manhã, porque era exatamente isso que eu estava fazendo, e alguns dos meus tempos estão fora daqui a uma hora. O que eu suspeito é que o uso de um fuso horário nomeado é mais preciso quando o horário de verão está envolvido. Se você usa America / New_York, o MySQL conhece o horário de verão e armazena as datas adequadamente. Se você defini-lo como -04: 00 como você tem aqui, não levará em consideração o cálculo do horário de verão.
Nathanb
1
Fazer seleção se mysql sabe sobre DST corretamente, regras de DST se regularmente actualizado e as tabelas do MySQL relacionados também precisam de atualização (veja acima na parte inferior da minha resposta)
Timo Huovinen
1

Os prós e os contras são praticamente idênticos. Depende se você deseja ou não isso.

Cuidado, se o fuso horário do MySQL diferir do horário do seu sistema (por exemplo, PHP), comparar o horário ou a impressão com o usuário envolverá alguns ajustes.

alandarev
fonte