Year 2038 Problem

O problema do ano 2038 (Y2K38) explicado

A 19 de janeiro de 2038 às 03:14:07 UTC, o timestamp Unix 2 147 483 647 — o valor máximo de um inteiro com sinal de 32 bits — vai transbordar e saltar para uma data de 1901. Eis o que isso significa, que sistemas são afetados, como funciona a correção com time_t de 64 bits e como testar o teu próprio código.

O overflow numa linha

Um inteiro com sinal de 32 bits chega no máximo a 2 147 483 647. Armazenado como segundos Unix, esse valor é 19/01/2038 03:14:07 UTC — e um segundo depois salta para -2 147 483 648, que se lê como 13 de dezembro de 1901. Qualquer sistema que ainda guarde o tempo num inteiro com sinal de 32 bits dará um salto de 137 anos para o passado nesse instante. A correção cabe numa palavra: 64 bits. A tabela abaixo mostra que tipos de armazenamento são afetados e quais já são seguros.

Tipo de armazenamentoTransborda em / data máx.Seguro após 2038?
time_t com sinal 32 bits / SQL INT2038-01-19 03:14:07 UTCNão
MySQL TIMESTAMP2038-01-19 03:14:07 UTCNão — usar DATETIME
32 bits sem sinal2106-02-07 06:28:15 UTCGanha 68 anos, não é solução
time_t com sinal 64 bits / SQL BIGINT~292 mil milhões de anosSim
Vírgula flutuante de 64 bits (JavaScript, Python)ano 275 760Sim
MySQL DATETIME (campos compactados)ano 9999Sim

O que é o problema do ano 2038?

Os timestamps Unix são tradicionalmente armazenados num inteiro com sinal de 32 bits que conta os segundos desde 1 de janeiro de 1970 00:00:00 UTC. O valor máximo de um inteiro com sinal de 32 bits é 2 147 483 647 — que corresponde a 19 de janeiro de 2038 às 03:14:07 UTC. Um segundo após esse momento, o inteiro transborda para -2 147 483 648. Interpretado como timestamp Unix, esse valor negativo corresponde a 13 de dezembro de 1901 — enviando os sistemas afetados quase 137 anos para o passado.

  • 2^31 − 1 = 2 147 483 647 (inteiro com sinal de 32 bits máximo)
  • 2 147 483 647 como data = 19 de janeiro de 2038 03:14:07 UTC
  • 2 147 483 647 + 1 transborda para −2 147 483 648 (o valor de 32 bits mais negativo)
  • −2 147 483 648 como data = 13 de dezembro de 1901 20:45:52 UTC
  • A hora local do overflow varia: 22:14:07 EST a 18 de janeiro, 14:14:07 JST a 19 de janeiro

Que sistemas estão em risco?

O problema afeta os sistemas que armazenam timestamps como inteiros com sinal de 32 bits:

  • Sistemas embebidos e microcontroladores com time_t de 32 bits
  • Código C e C++ antigo compilado para alvos de 32 bits que usa time_t ou int para timestamps
  • Núcleos Linux antigos (anteriores a 5.6) em hardware de 32 bits ainda em produção
  • Colunas MySQL TIMESTAMP — limitadas a 2038-01-19; usar DATETIME em vez disso
  • Alguns protocolos de rede e formatos de ficheiro que codificam timestamps como valores de 32 bits
  • Firmware em sistemas de controlo industrial e dispositivos IoT com vidas úteis de décadas

Que sistemas são seguros?

A maior parte do código e da infraestrutura modernos já não é afetada:

  • Qualquer SO de 64 bits — Linux (glibc 2.34+ também corrige o hardware de 32 bits), macOS, Windows
  • Python — os timestamps usam vírgula flutuante de 64 bits, seguros muito para lá do ano 292 milhões
  • JavaScript — Date usa vírgula flutuante IEEE 754 de 64 bits, seguro até ao ano 275 760
  • Go e Rust — usam int64 para o tempo internamente, seguros durante milhares de milhões de anos
  • Java — java.time.Instant usa long (64 bits), seguro até ao ano ~292 milhões
  • PostgreSQL TIMESTAMP — armazena datas para lá de 2038 corretamente
  • MySQL DATETIME — intervalo 1000-01-01 a 9999-12-31, não afetado

MySQL TIMESTAMP e o teto de 2038

O MySQL tem dois tipos principais de coluna data-hora que se comportam de forma muito diferente perto de 2038:

  • TIMESTAMP é armazenado internamente como um inteiro Unix com sinal de 32 bits — transborda em 2038-01-19 03:14:07 UTC
  • DATETIME é armazenado como campos A/M/D h/m/s compactados e suporta até ao ano 9999 — usa-o para qualquer data que possa ser posterior a 2038
  • TIMESTAMP também converte implicitamente de/para o fuso de sessão do servidor; DATETIME armazena o valor tal como está
  • Para colunas epoch em bruto, armazena BIGINT em vez de INT para que o valor em si não possa transbordar
  • Audita os esquemas existentes: SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS WHERE DATA_TYPE = "timestamp"

Linux embebido e time_t de 32 bits

O maior risco Y2038 que resta está em sistemas embebidos de longa vida: routers, controladores industriais, centralinas automóveis, contadores inteligentes e dispositivos IoT que foram enviados com núcleos de 32 bits e podem continuar em serviço depois de 2038. Tanto a biblioteca C como qualquer protocolo binário próprio têm de estar limpos em 64 bits.

  • glibc obteve time_t de 64 bits para ABIs de 32 bits através da macro de compilação -D_TIME_BITS=64 (glibc 2.34+)
  • musl libc usa time_t de 64 bits por omissão desde a versão 1.2.0 (2020)
  • Debian, Ubuntu e OpenWrt migraram todos os seus ports de 32 bits para time_t de 64 bits
  • Os timestamps do sistema de ficheiros são um assunto à parte: ext2/ext3 usam tempos de 32 bits; ext4 suporta 64 bits em inodes ≥ 256 bytes
  • Para hardware ainda enviado em 2026+, confirma que tanto a biblioteca C como qualquer formato de fio são de 64 bits

Como corrigir e testar para Y2038

A correção é sempre a mesma — alargar o tempo para 64 bits — mas tem de ser aplicada em cada camada que armazena ou transmite um timestamp: a biblioteca C, as colunas da base de dados e qualquer protocolo binário ou formato de ficheiro. A forma mais rápida de encontrar um problema é mover um relógio para lá do limite num ambiente de teste e vigiar as datas que aterram em 1901.

  • Compila o código de 32 bits com -D_TIME_BITS=64 (e -D_FILE_OFFSET_BITS=64) no glibc 2.34+
  • Usa BIGINT, não INT, para qualquer coluna que armazene segundos ou milissegundos Unix em bruto
  • Prefere DATETIME ou TIMESTAMPTZ a MySQL TIMESTAMP para datas que possam exceder 2038
  • Em testes, coloca o relógio do sistema ou do contentor em 2038-01-19T03:14:08Z e confirma que as datas ficam em 2038
  • Verifica os formatos de fio e os cabeçalhos de ficheiro — um SO de 64 bits não ajuda se o campo do protocolo for de 32 bits

Reinícios relacionados: Y2K, GPS, NTP e 2106

O ano 2038 é um de uma família de reinícios cíclicos de largura fixa. Cada um vem de empacotar um relógio em poucos bits, e conhecer as datas ajuda a detetar o seguinte antes de ser implementado:

ReinícioDataCausa
Y2K2000-01-01Armazenamento do ano com dois dígitos
Reinício de semana GPS2019-04-06Contador de semana GPS de 10 bits
Reinício de era NTP2036-02-07Segundos sem sinal de 32 bits desde 1900
Ano 2038 (Y2K38)2038-01-19Segundos com sinal de 32 bits desde 1970
Ano 21062106-02-07Segundos sem sinal de 32 bits desde 1970

Referências relacionadas

FAQ

O que é o problema do ano 2038?
O problema do ano 2038 (Y2K38) é um bug em que sistemas que armazenam timestamps Unix em inteiros com sinal de 32 bits vão transbordar a 19 de janeiro de 2038 às 03:14:07 UTC. O valor máximo de um inteiro com sinal de 32 bits é 2 147 483 647. Um segundo depois, o inteiro salta para -2 147 483 648, que corresponde a 13 de dezembro de 1901.
Qual é o timestamp Unix de 19 de janeiro de 2038?
O timestamp Unix de 19 de janeiro de 2038 às 03:14:07 UTC é 2 147 483 647 — o valor máximo de um inteiro com sinal de 32 bits (2^31 - 1). É o momento exato em que ocorre o overflow do ano 2038 nos sistemas de 32 bits.
Como corrijo o problema do ano 2038?
Armazena o tempo num inteiro com sinal de 64 bits em vez de 32 bits. Usa time_t de 64 bits (compila o código de 32 bits com -D_TIME_BITS=64 no glibc 2.34+), BIGINT em vez de INT para colunas epoch, e DATETIME em vez de MySQL TIMESTAMP. Um valor de tempo com sinal de 64 bits não transborda durante cerca de 292 mil milhões de anos.
Y2K38 é o mesmo que Y2K?
Não. Y2K (problema do ano 2000) devia-se a programas que armazenavam os anos com dois dígitos. Y2K38 deve-se a armazenar timestamps Unix como inteiros com sinal de 32 bits que transbordam em 2038. Causas diferentes, sistemas afetados diferentes.
O que é o problema do ano 2106?
O ano 2106 é o mesmo overflow para timestamps sem sinal de 32 bits, que se esgotam a 7 de fevereiro de 2106 às 06:28:15 UTC. Passar de com sinal para sem sinal de 32 bits ganha mais 68 anos, mas não é uma solução real; o tempo de 64 bits é.
A internet vai abaixo em 2038?
Provavelmente não de forma significativa. A maior parte da infraestrutura da internet já migrou para sistemas de 64 bits. O risco concentra-se em sistemas embebidos, dispositivos IoT antigos e software que não é atualizado há décadas. Os sistemas operativos, linguagens e bases de dados modernos já são seguros.