PHP e mySQL: Bug do ano 2038: O que é? Como resolver?

116

Eu estava pensando em usar TIMESTAMP para armazenar a data + hora, mas li que há uma limitação do ano 2038 nele. Em vez de fazer minha pergunta em massa, preferi dividi-la em pequenas partes para que seja fácil para usuários novatos entenderem também. Então, minha (s) pergunta (s):

  1. Qual é exatamente o problema do ano 2038?
  2. Por que isso ocorre e o que acontece quando ocorre?
  3. Como resolvemos isso?
  4. Existem alternativas possíveis para usá-lo, que não representam um problema semelhante?
  5. O que podemos fazer com os aplicativos existentes que utilizam o TIMESTAMP, para evitar o chamado problema, quando ele realmente ocorre?

Desde já, obrigado.

Devner
fonte
1
Ainda faltam 28 anos. Você ainda usa alguma tecnologia relacionada a computadores de 1982? Improvável. Portanto, não se preocupe, porque em 2038 isso provavelmente não será mais um problema.
Gordon
24
Gordon, eu faço um aplicativo que faz previsões. Economizo quanto dinheiro tenho a cada mês e posso estimar quando serei milionário. No meu caso, 28 anos não é muito, e tenho certeza de que não sou o único com esse problema agora (resolvi usando números de 64 bits para representar o carimbo de data / hora).
Emil Vikström
2
@Emil Usando inteiros de 64 bits agora , você encontrou uma solução fácil para um problema concreto. Não aplicável a (ou necessário para) todos, mas trabalhando para seu caso de uso. O que quero dizer é que se o OP não tem um problema concreto, como previsão, então este pode ser um tópico interessante, mas nada com que ele deva se preocupar, porque resolver isso em um nível geral não é um problema de PHP (cuidado com a tag). Apenas meu 2c.
Gordon
1
Em 2038, analisar a string "AAAA-MM-DD HH: MM: SS: mmmm ..." será a operação mais barata que você pode sonhar. Em 2038, o de 32 bits estará obsoleto. Duvido que o carimbo de data / hora Unix como o conhecemos exista então, e se existir, nossos sistemas de 256 bits lidarão com datas que vão muito além da idade em que sistemas de 4096 bits são dados em refeições felizes.
Super Cat
8
Gordon, são 9 anos depois. TIMESTAMPS ainda são usados. Ainda usamos tecnologia de 28 anos atrás. É chamada de rede mundial de computadores.
sfscs

Respostas:

149

Eu marquei este como um wiki da comunidade, então fique à vontade para editar quando quiser.

Qual é exatamente o problema do ano 2038?

"O problema do ano 2038 (também conhecido como Bug Unix Millennium, Y2K38 por analogia ao problema Y2K) pode fazer com que alguns softwares de computador falhem antes ou no ano de 2038. O problema afeta todos os softwares e sistemas que armazenam a hora do sistema como um 32 assinado -bit inteiro, e interpretar esse número como o número de segundos desde 00:00:00 UTC em 1º de janeiro de 1970. "


Por que isso ocorre e o que acontece quando ocorre?

Vezes além de 03:14:07 UTC na terça-feira, 19 de janeiro de 2038 serão ' agrupados ' e armazenados internamente como um número negativo, que esses sistemas interpretarão como um tempo em 13 de dezembro de 1901 em vez de 2038. Isso se deve a o fato de que o número de segundos desde a época do UNIX (1 de janeiro de 1970 00:00:00 GMT) terá excedido o valor máximo de um computador para um inteiro assinado de 32 bits.


Como resolvemos isso?

  • Use tipos de dados longos (64 bits são suficientes)
  • Para MySQL (ou MariaDB), se você não precisa das informações de tempo, considere usar o DATEtipo de coluna. Se precisar de maior precisão, use em DATETIMEvez de TIMESTAMP. Esteja ciente de que as DATETIMEcolunas não armazenam informações sobre o fuso horário, portanto, seu aplicativo terá que saber qual fuso horário foi usado.
  • Outras soluções possíveis descritas na Wikipedia
  • Espere que os desenvolvedores do MySQL consertem esse bug relatado há mais de uma década.

Existem alternativas possíveis para usá-lo, que não representam um problema semelhante?

Sempre que possível, tente usar tipos grandes para armazenar datas em bancos de dados: 64 bits é suficiente - um tipo longo e longo em GNU C e POSIX / SuS, ou sprintf('%u'...)em PHP ou na extensão BCmath.


Quais são alguns casos de uso potencialmente prejudiciais, embora ainda não estejamos em 2038?

Portanto, um MySQL DATETIME tem um intervalo de 1000-9999, mas TIMESTAMP só tem um intervalo de 1970-2038. Se o seu sistema armazena datas de nascimento, datas futuras de encaminhamento (por exemplo, hipotecas de 30 anos) ou algo semelhante, você já encontrará esse bug. Novamente, não use TIMESTAMP se isso for um problema.


O que podemos fazer com os aplicativos existentes que utilizam o TIMESTAMP, para evitar o chamado problema, quando ele realmente ocorre?

Poucos aplicativos PHP ainda estarão por aí em 2038, embora seja difícil prever, já que a web ainda não é uma plataforma legada.

Aqui está um processo para alterar uma coluna da tabela de banco de dados para converter TIMESTAMPpara DATETIME. Tudo começa com a criação de uma coluna temporária:

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

Recursos

Corey Ballou
fonte
2
Impressionante. Para mim, sua resposta é a mais completa. Muito obrigado.
Devner
7
No MySQL, se a versão futura alterar o tipo de dados de armazenamento subjacente de TIMESTAMP para 64 bits, não há necessidade de alterar seu código para DATETIME, certo? Só não acho que desencorajar ninguém de usar o TIMESTAMP seja a coisa certa a fazer, porque esse tipo de dados tem sua finalidade.
pixelfreak
1
O campo BIGINT assinado do MySQL parece funcionar bem para armazenar carimbos de data / hora e fiz alguns testes locais no MySQL 5.5 que confirmam que funciona. Obviamente, usar assinado seria melhor do que não assinado, pois você também pode representar datas no passado. Algum motivo para não usar BIGINT para carimbos de data / hora?
zuallauz
Uma coisa a ser observada, o MySQL só tem configuração automática para data / hora atual (CURRENT_TIMESTAMP) para campos de carimbo de data / hora. Esperançosamente, essa funcionalidade irá eventualmente ser portada para todos os tipos de data.
Ray de
5
É absolutamente absurdo que o MySQL (e MariaDB) não usem inteiros de 64 bits para armazenar carimbos de data / hora em sistemas de 64 bits. Usar DATETIME não é uma solução adequada porque não temos ideia do fuso horário. Isso foi relatado em 2005 , mas ainda nenhuma correção está disponível.
Mike
14

Ao usar carimbos de data / hora UNIX para armazenar datas, você está, na verdade, usando inteiros de 32 bits, que mantém a contagem do número de segundos desde 01/01/1970; ver hora Unix

Esse número de 32 bits estourará em 2038. Esse é o problema de 2038.


Para resolver esse problema, você não deve usar um timestamp UNIX de 32 bits para armazenar suas datas - o que significa que, ao usar o MySQL, você não deve usar TIMESTAMP, mas DATETIME(consulte 10.3.1. Os tipos DATETIME, DATE e TIMESTAMP ):

O DATETIMEtipo é usado quando você precisa de valores que contenham informações de data e hora. O intervalo suportado é de '1000-01-01 00:00:00'até '9999-12-31 23:59:59'.

O TIMESTAMPtipo de dados tem um intervalo de '1970-01-01 00:00:01'UTC a '2038-01-19 03:14:07'UTC.


A (provavelmente) melhor coisa que você pode fazer com seu aplicativo para evitar / corrigir esse problema é não usar TIMESTAMP, mas DATETIMEpara as colunas que devem conter datas que não estão entre 1970 e 2038.

No entanto, uma pequena observação: há uma grande probabilidade (estatisticamente falando) de que seu aplicativo terá sido reescrito algumas vezes antes de 2038 ^^ Então, talvez, se você não tiver que lidar com datas no futuro , você não terá que cuidar desse problema com a versão atual do seu aplicativo ...

Pascal MARTIN
fonte
1
+1 para a informação. A tentativa aqui é adaptar as melhores práticas desde o início para que não seja necessário se preocupar com o problema posteriormente. Portanto, embora, por enquanto, 2038 possa não parecer um problema, eu apenas quero seguir as práticas recomendadas e segui-las para cada aplicativo que eu criar, desde o início. Espero que isso faça sentido.
Devner
Sim, faz sentido, entendo seu ponto de vista. Mas "prática recomendada" também pode significar "o que atende a necessidade" - Se você sabe que um carimbo de data / hora será suficiente, não há necessidade de usar uma data e hora (eles precisam de mais memória para serem armazenados, por exemplo; e isso pode importar se você tem milhões de registros) ;; Tenho alguns aplicativos nos quais uso carimbo de data / hora para algumas colunas (colunas que contêm data atual, por exemplo) e data e hora para outras (colunas que contêm datas passadas / futuras, por exemplo)
Pascal MARTIN
Vejo que seu procedimento também faz sentido e funciona bem, especialmente no que diz respeito ao espaço de armazenamento. Se estiver tudo bem, posso perguntar qual estrutura e comprimento você geralmente usa para a coluna TIMESTAMP em seus aplicativos? Obrigado.
Devner
Bem, quando eu quero usar um TIMESTAMP, é para / porque meus dados de "data / hora" cabem entre 1970 e 2038 - e, então, eu uso o tipo de dados TIMESTAMP do MySQL.
Pascal MARTIN
Obrigado pela informação. Pela sua resposta, entendo que é suficiente apenas declarar a coluna como TIMESTAMP e não precisamos fornecer nenhum comprimento para ela (ao contrário de e int ou var, onde fornecemos o comprimento). Estou certo?
Devner
7

Uma rápida pesquisa no Google resolverá o problema : problema do ano 2038

  1. O problema do ano 2038 (também conhecido como Bug Unix Millennium, Y2K38 por analogia ao problema Y2K) pode fazer com que alguns softwares de computador falhem antes ou no ano de 2038
  2. O problema afeta todos os softwares e sistemas que armazenam a hora do sistema como um número inteiro de 32 bits com sinal e interpretam esse número como o número de segundos desde 00:00:00 UTC em 1º de janeiro de 1970. A última hora que pode ser representada desta forma é 03:14:07 UTC na terça-feira, 19 de janeiro de 2038. Os tempos além deste momento "envolverão" e serão armazenados internamente como um número negativo, que esses sistemas interpretarão como uma data em 1901 em vez de 2038
  3. Não há solução fácil para este problema para combinações existentes de CPU / SO, sistemas de arquivos existentes ou formatos de dados binários existentes
Rubens Farias
fonte
2

http://en.wikipedia.org/wiki/Year_2038_problem tem a maioria dos detalhes

Em suma:

1) + 2) O problema é que muitos sistemas armazenam informações de data como um int assinado de 32 bits igual ao número de segundos desde 01/01/1970. A última data que pode ser armazenada desta forma é 03:14:07 UTC na terça-feira, 19 de janeiro de 2038. Quando isso acontecer, o int será "agrupado" e armazenado como um número negativo que será interpretado como uma data em 1901. O que acontecerá exatamente varia de sistema para sistema, mas basta dizer que provavelmente não será bom para nenhum deles!

Para sistemas que armazenam apenas datas no passado, acho que você não precisa se preocupar por um tempo! O principal problema são os sistemas que funcionam com datas no futuro. Se o seu sistema precisa funcionar com datas de 28 anos no futuro, você deve começar a se preocupar agora!

3) Use um dos formatos de data alternativos disponíveis ou mude para um sistema de 64 bits e use ints de 64 bits. Ou para bancos de dados, use um formato de carimbo de hora alternativo (por exemplo, para MySQL, use DATETIME)

4) Veja 3!

5) Veja 4 !!! ;)

Addsy
fonte
Seus pontos 4 e 5 me lembram de 'Chamada por referência'. Isso colocou um sorriso no meu rosto. Obrigado e +1 pelo mesmo.
Devner
1

Bros, se você precisa usar PHP para exibir carimbos de data / hora, esta é a MELHOR solução PHP sem mudar do formato UNIX_TIMESTAMP.

Use uma função custom_date (). Dentro dele, use o DateTime. Aqui está a solução DateTime .

Contanto que você tenha UNSIGNED BIGINT (8) como seus carimbos de data / hora no banco de dados. Contanto que você tenha o PHP 5.2.0 ++

Dexter
fonte
-1

Como eu não queria atualizar nada, pedi ao meu backend (MSSQL) para fazer este trabalho ao invés do PHP!

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

Pode não ser uma maneira eficiente, mas funciona.

Venkatesh GS Rao
fonte