Chrono Mock
IdiomaPortuguês (Brasil)

Testando o problema do ano 2038 no Windows

Em 19 de janeiro de 2038, às 03:14:07 UTC, acaba um contador de 32 bits com sinal de segundos desde 1970. O relógio do Windows segue adiante - os programas, formatos de arquivo e bibliotecas que guardaram a própria cópia de 32 bits, não. Veja como encontrá-los em um único aplicativo, sem colocar a data da máquina inteira em 2038.

Onde o bug ainda pode estar

O relógio do Windows é um contador de 64 bits de passos de 100 nanossegundos desde 1601, então não conhece 2038. O tipo time_t da biblioteca de execução do C é de 64 bits por padrão desde o Visual Studio 2005. Sobra o código que escolheu um tempo de 32 bits ou perdeu a metade alta pelo caminho:

  • Compilações de 32 bits feitas com _USE_32BIT_TIME_T, ou que chamam _time32 e __time32_t diretamente. A documentação da Microsoft avisa que esse aplicativo pode falhar depois de 18 de janeiro de 2038.
  • Um tempo de 64 bits guardado em um int ou long. No Windows os dois têm 32 bits, mesmo em um programa de 64 bits.
  • Bibliotecas, formatos de arquivo, protocolos de rede e colunas de banco de dados que guardam segundos desde 1970 em 32 bits.
  • Código que soma anos. Uma licença de vinte anos, um cronograma de financiamento de trinta ou um certificado válido até 2040 cruzam o limite muito antes de 2038 chegar.

Cruzando o limite em um único aplicativo

O limite é definido em UTC e --at é lido no fuso da sessão, então fixe o fuso em UTC quando você mesmo escrever o instante. O cenário pronto já traz um instante UTC absoluto e não precisa de fuso.

# O cenário: a sessão começa no próprio limite, 03:14:07 UTC
chrono run "C:\apps\test.exe" --preset year-2038 --report session.txt

# Começar dezessete segundos antes e deixar o aplicativo cruzá-lo ao vivo
chrono run "C:\apps\test.exe" --at 2038-01-19T03:13:50 --zone +00:00 --report session.txt

# Começar antes e rodar a velocidade x60 - o limite chega em cerca de 14 segundos
chrono run "C:\apps\test.exe" --at 2038-01-19T03:00:00 --zone +00:00 --mode x60

# Ou rodar com a data de hoje e saltar por cima dele com o aplicativo aberto
chrono run "C:\apps\test.exe" --jump-after 5:2038-01-19T03:14:05 --zone +00:00

Um segundo depois do limite, um contador de 32 bits com sinal passa a -2.147.483.648, que se lê como 13 de dezembro de 1901, 20:45:52 UTC. Uma data de 1901, um número negativo, um item que de repente expirou ou foi para o início da ordenação, uma exceção lançada pelo código de datas - é assim que o estouro aparece visto de fora.

Colocar o relógio real em 2038 é a outra forma de chegar aqui, e a que mais quebra coisas. O que acontece quando você altera a data do sistema →

Conferindo que chegou ao programa

Um teste que passa porque o programa nunca viu 2038 é pior que nenhum teste. O relatório no fim de cada sessão diz qual relógio o programa leu e quantas vezes:

Chrono Mock - session report
  target:   .\build\test.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:08
            real elapsed 1.3s, fake elapsed 1.3s
  covered channels (substituted, with call counts, 10 calls in all):
            - pid 21240: GetSystemTimeAsFileTime (5 calls)
            - pid 21240: GetLocalTime (5 calls)

A linha session confirma que o relógio do aplicativo passou do limite. Um veredito works com contagens de chamadas significa que o programa leu o relógio e recebeu a data de 2038; works partially nomeia os canais que ele leu por outro caminho. O que significam os vereditos →

O que um teste aprovado não prova

  • Só o relógio do próprio programa se moveu. Uma data que ele carrega de um arquivo ou de um banco de dados é a que está gravada lá, então uma data de 2038 dentro dos dados é outro teste. A calculadora de datas calcula essas datas para você: chrono calc --base 2038-01-19T03:14:07 --zone +00:00 --shift +1s
  • Um servidor com o qual o programa conversa continua na data real. Um limite de 32 bits desse lado está fora do alcance, e a sessão avisa quando vê o programa abrir uma conexão de rede.

Experimente