Timestamps, fusos horários e a hora que acontece duas vezes
Um timestamp responde a uma de duas perguntas, e a maioria dos erros com datas vem de as trocar. Ou nomeia um instante, um ponto na linha do tempo com que todos os observadores concordam, ou nomeia uma leitura de relógio de parede, que não significa nada enquanto não se souber de que parede se trata. Um pagamento aconteceu num instante; uma consulta no dentista é uma leitura de relógio de parede. Guarde um como se fosse o outro e mais tarde parte-se, por exatamente uma hora, numa manhã de domingo.
O tempo Unix conta, não dá as horas
O tempo Unix é o número de segundos decorridos desde 1970-01-01 00:00:00 UTC. Esse UTC diz onde a contagem começou; o valor em si não transporta fuso horário nenhum. O mesmo instante é o mesmo número em Amesterdão, em Auckland e em Lima.
O problema é a unidade, por isso conte os dígitos.
| Dígitos | Exemplo | Unidade |
|---|---|---|
| 10 | 1756080000 | segundos |
| 13 | 1756080000000 | milissegundos |
Qualquer data dos dias de hoje tem 10 dígitos em segundos e 13 em milissegundos, e assim será até 2286. As duas leituras erradas são escandalosas: milissegundos lidos como segundos vão parar ao ano 57626, e segundos lidos como milissegundos a janeiro de 1970. Um conversor de timestamps que mostre a data interpretada torna isso óbvio.
O outro limite é o recipiente. Uma contagem de segundos em 32 bits com sinal acaba em 2147483647, que corresponde às 03:14:07 UTC de 19 de janeiro de 2038, e depois dá a volta para negativo e lê-se como dezembro de 1901. Qualquer sistema que guarde um prazo de validade de 30 anos já calcula para lá disso. Trinta e dois bits sem sinal chegam a 2106, mas perdem todas as datas anteriores a 1970; 64 bits não têm teto prático.
ISO 8601 e RFC 3339
O formato que vale a pena decorar é 2026-08-25T14:30:00Z: data, um T, hora e depois um designador de zona. O RFC 3339 é um perfil mais estrito do ISO 8601 para protocolos de internet, e exige esse designador. O próprio ISO 8601 também permite datas por semana, uma forma sem separadores como 20260825T143000Z e timestamps sem desvio, por isso uma API que promete "ISO 8601" quer normalmente dizer o subconjunto RFC 3339.
Z significa um desvio de zero. +02:00 significa que o relógio local está duas horas adiantado em relação ao UTC, por isso o UTC obtém-se subtraindo; inverter isto é um erro comum de duas horas.
| Cadeia | O que nomeia |
|---|---|
2026-08-25T14:30:00Z | Um instante, sem ambiguidade |
2026-08-25T16:30:00+02:00 | O mesmo instante, num relógio duas horas adiantado |
2026-08-25T14:30:00 | Uma leitura de relógio de parede, sem instante |
2026-08-25 | Uma data, com 23, 24 ou 25 horas |
Uma cadeia que não tem Z nem desvio é ambígua, e os analisadores não concordam: uns assumem UTC, outros o fuso do sistema. É por isso que um valor só com data, como uma data de nascimento, aparece muitas vezes um dia mais cedo a oeste de Greenwich.
Um desvio não é um fuso horário
+02:00 é um facto sobre um instante num lugar. Europe/Amsterdam é um conjunto de regras: +01:00 no inverno, +02:00 no verão, e um comportamento diferente em décadas anteriores. Guardar o desvio conserva a resposta para o instante em que foi medido e deita fora a capacidade de calcular qualquer outra.
Os desvios também não são únicos. Numa tarde de verão, +02:00 cobre Amesterdão, Lagos e Joanesburgo, por isso só a partir do desvio não há caminho de volta até às regras nem forma de saber o que esse relógio vai marcar em dezembro. As abreviaturas são ainda piores, já que CST dá nome a três fusos diferentes. Guarde o identificador da IANA na forma Area/Location.
A hora que não existe e a hora que acontece duas vezes
Em toda a União Europeia, os relógios avançam no último domingo de março e recuam no último domingo de outubro. Em Amesterdão, as 02:00 passam a 03:00 na primavera, por isso todas as horas locais entre as 02:00:00 e as 02:59:59 não ocorrem nesse dia. No outono, as 03:00 passam a 02:00, por isso as 02:30 acontecem duas vezes, uma a +02:00 e outra uma hora depois a +01:00.
- Alarmes recorrentes. Uma tarefa marcada para as 02:30 locais não tem hora válida no domingo da primavera. Os agendadores divergem: saltam-na, deslocam-na para as 03:00, ou deslocam-na para a 01:30. No domingo do outono a mesma tarefa pode disparar uma vez ou duas, por isso verifique as próximas horas de disparo com um analisador de cron.
- Ficheiros de registo. Uma hora de timestamps locais sem desvio não pode ser ordenada, e dois eventos separados por uma hora ficam com texto idêntico. Registe em UTC, ou ponha o desvio em cada linha.
- Durações. Das 20:00 de 24 de outubro de 2026 até às 20:00 do dia seguinte, hora local de Amesterdão, vão 25 horas. "Somar um dia" e "somar 24 horas" são operações diferentes, e uma diferença de datas em dias de calendário responde a uma pergunta diferente da de um conversor de durações que trabalha em horas.
As regras mudam, por isso o armazenamento tem de lhes sobreviver
A base de dados de fusos horários da IANA, a tzdata, contém estas regras e é publicada várias vezes por ano, porque as regras são legislação: os governos abolem a hora de verão, adotam-na, ou mudam as datas da troca, por vezes com poucas semanas de aviso. Uma imagem de contentor fixada, um navegador e o motor de execução de uma linguagem trazem cada um a sua própria cópia e podem estar em desacordo dentro da mesma máquina. Converter uma hora local futura num instante é uma previsão, que só fica assente quando o momento chega.
| Tipo de valor | Guardar | Razão |
|---|---|---|
| Algo que aconteceu | Instante em UTC | O passado não pode voltar a ser legislado |
| Marcação futura num relógio de parede | Data e hora locais mais o id de fuso da IANA | A intenção é a leitura do relógio |
| Instante futuro, fixo em todos os fusos | UTC | A intenção é o instante |
| Data sem hora | Data simples, sem fuso e sem meia-noite | Colar-lhe um fuso faz com que derive |
É por isto que "basta guardar em UTC" está errado para uma reunião no ano que vem. Converta hoje as 09:00 de 30 de março de 2027 em Europe/Amsterdam para UTC e a resposta é 07:00Z. Se a data da troca mudar entretanto, a reunião continua a ser às 09:00 locais, mas 07:00Z passa a aparecer como 08:00: o valor guardado congelou uma previsão e deitou fora a intenção. Mantenha a hora local mais o fuso como fonte da verdade; qualquer coluna UTC ao lado dela é uma cache.
Mostrar datas a pessoas
03/04 não pode ser desambiguado a partir da cadeia. É 3 de abril para a maior parte do mundo e 4 de março nos Estados Unidos; 03/04/05 acrescenta uma terceira incógnita. Escreva o mês por palavras na saída destinada a pessoas (4 Apr 2026) e use ISO para troca entre máquinas. Uma etiqueta de timestamp do Discord contorna isto numa conversa, apresentando a data no fuso e na localidade de quem a lê: guarde um valor inequívoco e formate na periferia.
Os números de semana têm regra própria. As semanas ISO começam à segunda-feira, e a semana 1 é a que contém a primeira quinta-feira de janeiro, ou seja, a que contém o dia 4 de janeiro. Assim, o dia 1 de janeiro pode cair na semana 52 ou 53 do ano ISO anterior, e 29 de dezembro pode cair na semana 1 do seguinte. Uma convenção norte-americana frequente numera antes a partir da semana começada ao domingo que contém 1 de janeiro, por isso a mesma data recebe dois números, e vale a pena conferir a semana ISO de uma calculadora de dia da semana contra uma folha de cálculo.
Durações, meses e anos bissextos
"Um mês depois" não é um número fixo de dias; são 28, 29, 30 ou 31. Somar um mês a 31 de janeiro não tem resposta correta, e as bibliotecas limitam em geral ao fim de fevereiro, o que torna a operação não reversível: some um mês, subtraia-o outra vez, e 31 de janeiro passou a 28 de janeiro. O arredondamento é a mesma armadilha em ponto pequeno: truncar uma hora para o quarto de hora mais próximo e arredondá-la para ele diferem em até um intervalo inteiro.
A regra do ano bissexto por inteiro: um ano é bissexto se for divisível por 4, exceto se for divisível por 100, caso em que não é, a não ser que seja também divisível por 400. Assim, 1900 não foi, 2000 foi e 2100 não será, o que dá um ano médio de 365,2425 dias. Código que testa apenas a divisibilidade por 4 está correto para todos os anos de que há memória viva, e é por isso que sobrevive até 2100. Um aniversário a 29 de fevereiro é o incómodo aparentado: no ano seguinte há 28 de fevereiro e 1 de março, e nenhuma regra escolhe entre eles, por isso escolha um deliberadamente.