O bug costuma começar com uma frase que parece inofensiva
A maioria dos bugs de data e hora não começa com astronomia exótica. Começa com uma frase de produto que soa óbvia: “enviar o lembrete amanhã de manhã”, “expirar este teste após 30 dias”, “mostrar as encomendas de hoje”, “executar este processo às 2:30”, “guardar o timestamp”. Cada frase esconde uma regra. Amanhã de manhã onde? Trinta dias de calendário ou 30 vezes 24 horas? Hoje na cidade do cliente, no armazém ou no servidor da base de dados? O que acontece quando 2:30 não existe?
O software é bom a contar. Calendários não são só contagem. São uma pilha de convenções humanas: fusos horários, mudanças de horário de verão, dias úteis, formatos locais, anos bissextos, semanas ISO, tipos de base de dados e horas legais de corte. Os bugs aparecem quando o código trata uma camada como se fosse outra.
Este guia é um manual de campo para os erros que continuam a partir sistemas em produção. Foi escrito para programadores, product managers, analistas e qualquer pessoa que já olhou para um dashboard e se perguntou por que motivo os números de ontem mudaram depois do pequeno-almoço.
1. Datetimes sem fuso: o timestamp sem passaporte
Um datetime sem fuso é uma data e hora de relógio sem fuso horário. 2026-03-08 02:15 parece preciso, mas não diz onde aconteceu. Em Nova Iorque, na mudança para horário de verão de 2026, a hora entre 02:00 e 02:59 não ocorre. Noutra cidade pode ser perfeitamente válida. Em UTC significa outra coisa.
A falha comum é guardar hora local como se fosse um instante. Uma reunião criada para “9:00 Europe/Madrid” deve continuar a ser às 9:00 em Madrid quando Espanha muda a hora. Um evento de pagamento que já aconteceu deve ser guardado como um instante. São modelos diferentes.
| Caso de uso | Guardar | Porquê |
|---|---|---|
| Log de auditoria, clique, pagamento, evento API | Instante, normalmente UTC | O evento já aconteceu num momento. |
| Reunião futura numa cidade | Data local, hora local, fuso IANA | A hora local deve sobreviver a alterações de DST. |
| Aniversário, feriado, dia de faturação | Apenas data | Pode não haver hora do dia útil. |
| Horários de abertura | Hora local mais local ou regra | A regra repete-se em tempo civil local. |
Se precisa de comparar momentos exatos, converta para instantes. Se precisa de manter uma marcação humana onde foi pretendida, guarde também o local. Um offset como UTC+01:00 não basta para planeamento futuro, porque o offset pode mudar.
2. Confusão com UTC: bom armazenamento, má experiência
“Guardar tudo em UTC” é uma regra útil para eventos que já aconteceram. Não é um desenho de produto completo. Os utilizadores não pensam em UTC quando marcam uma consulta, publicam um webinar ou escolhem uma data de check-in num hotel. Pensam em tempo civil local.
O bug aparece quando um sistema guarda UTC corretamente, mas esquece o fuso pretendido pelo utilizador. Um convite criado em Los Angeles para as 9:00 pode aparecer internamente como 17:00 UTC. Tudo bem. Mas se a aplicação reconstruir depois o evento com o fuso do servidor, ou guardar apenas um offset fixo, a reunião pode mover-se após uma mudança de horário.
Bons sistemas separam três perguntas: que instante é este, que hora local queria o utilizador e que regras de fuso horário explicam essa hora local? O nosso conversor de fusos horários é útil precisamente porque data, local e offset importam em conjunto.
3. Lacunas e dobras de DST: horas que desaparecem ou acontecem duas vezes
O horário de verão cria duas armadilhas clássicas. Na primavera, uma hora local pode ser saltada. No outono, uma hora local pode acontecer duas vezes. Se o seu agendador aceitar “2026-03-08 02:30 America/New_York”, o que deve fazer? Rejeitar? Mover para as 03:00? Interpretar noutro fuso? Não há resposta universal. O produto precisa de uma regra.
Horas ambíguas no outono são ainda mais subtis. “2026-11-01 01:30 America/New_York” pode significar a primeira 01:30 ou a segunda 01:30. Ambas as etiquetas locais são reais. Logs, sistemas de transporte, sistemas hospitalares e agendadores de tarefas precisam de saber qual foi pretendida.
Uma boa suite de testes inclui datas reais de transição para fusos importantes para os seus utilizadores. Não teste apenas 15 de janeiro. Teste um dia de avanço na primavera, um dia de recuo no outono e um país cujas regras de DST sejam diferentes das suas.
4. Datas puras que viram meia-noite por acidente
Uma data nem sempre é um datetime com meia-noite anexada. Uma reserva de hotel para 3 de junho, um aniversário, uma data limite fiscal e um feriado são conceitos de data. Quando o código os transforma em 2026-06-03T00:00:00Z, inventou uma hora e um fuso.
É assim que aniversários passam para o dia anterior para utilizadores a oeste de UTC. É também assim que “o relatório de hoje” pode mostrar dados de ontem para uma equipa e de amanhã para outra. A correção é simples: use tipos de apenas data para conceitos de apenas data, e crie um datetime só quando a regra de negócio precisar mesmo de uma hora.
Para prazos, faça uma pergunta mais precisa: o item expira no início da data, no fim da data, no fecho local do negócio ou num instante exato? “Expira em 3 de junho” é texto de produto, não uma regra completa.
5. Somar dias como segundos
Somar 86.400 segundos não é o mesmo que somar um dia de calendário em hora local. A maioria dos dias tem 24 horas, mas dias de transição DST podem ter 23 ou 25 horas. Se um lembrete diário deve aparecer às 8:00 hora local, some um dia de calendário no fuso do utilizador. Se uma cache deve expirar exatamente após 24 horas, some uma duração.
| Frase | Normalmente significa | Modelo mais seguro |
|---|---|---|
| Mesma hora amanhã | Próximo dia de calendário local à mesma hora de relógio | Data/hora no fuso do utilizador |
| Expira em 24 horas | Duração exata decorrida | Instante mais duração |
| Vence em 3 dias úteis | Regra local de dias de trabalho | Calendário de trabalho |
| Todas as segundas às 9 | Agenda local recorrente | Hora local mais fuso IANA |
É por isso que uma calculadora de dias úteis não é apenas uma calculadora de diferença de datas sem fins de semana. Feriados, fins de semana locais e encerramentos observados podem mudar o resultado.
6. Unidades de timestamp Unix: segundos, milissegundos, microssegundos, nanossegundos
Unix time é elegante: contar tempo desde 1970-01-01T00:00:00Z. O problema é que ecossistemas de software discordam sobre a unidade. Muitas APIs usam segundos. JavaScript usa milissegundos. Algumas bases de dados e sistemas de observabilidade usam microssegundos ou nanossegundos. 1717425600 e 1717425600000 representam o mesmo momento em unidades diferentes; use a errada e a data salta séculos.
A verificação mais rápida é a contagem de dígitos. Segundos Unix atuais têm 10 dígitos. Milissegundos têm 13. Microssegundos, 16. Nanossegundos, 19. Um bom importador deve identificar a unidade explicitamente ou rejeitar entradas ambíguas em vez de adivinhar silenciosamente. O nosso conversor de timestamp Unix mostra a mesma ideia como ferramenta.
7. Ordenar datas como texto no formato errado
Datas em estilo ISO ordenam bem como texto: 2026-06-03 vem antes de 2026-12-01. Muitos formatos locais não. Uma lista de strings MM/DD/YYYY ou DD/MM/YYYY pode parecer correta numa amostra pequena e falhar quando mudam os meses ou os países.
Use ISO 8601 ou tipos reais de data para troca e armazenamento. Use formatos localizados apenas na fronteira de apresentação. Se um CSV é para humanos, explique o formato esperado no cabeçalho ou na documentação de exportação. Se é para máquinas, prefira um padrão inequívoco como RFC 3339 para timestamps.
8. Bugs de número de semana perto do Ano Novo
Números de semana não são universais. Semanas ISO começam à segunda-feira e fazem da Semana 1 a semana que contém a primeira quinta-feira. Alguns sistemas de estilo americano começam ao domingo e contam como Semana 1 a semana que contém 1 de janeiro. Perto do Ano Novo, essas regras podem diferir por uma semana inteira.
Se um relatório diz “receita da Semana 1”, uma exportação de payroll diz “2026-W01” e uma folha de cálculo usa a função padrão de semana, confirme a regra antes de reconciliar números. Para equipas internacionais, o rótulo mais seguro é o sistema de semana mais o intervalo de datas, por exemplo: “ISO 2026-W01, 29 dez 2025-4 jan 2026”.
A mesma lógica está na nossa ferramenta de número da semana, que mostra o intervalo de datas em vez de tratar o número como autoexplicativo.
9. Fugas de fuso horário do servidor
Um dos bugs de produção mais frustrantes é código que passa todos os testes localmente e falha após o deploy porque o servidor corre noutro fuso horário. O portátil do programador, o runner de CI, a imagem Docker, a base de dados, o worker da fila e o warehouse analítico podem ter defaults diferentes.
Defina fusos horários explícitos em testes. Execute pelo menos um job de testes com um fuso não local. Evite depender dos defaults do processo para parse ou formatação. Se uma função recebe uma string de data, faça com que o formato esperado e o fuso façam parte do contrato.
10. “Fim do dia” sem lugar
Fim do dia parece claro até os clientes viverem em mais de um lugar. Uma promoção que termina “sexta-feira à meia-noite” pode significar meia-noite local do cliente, meia-noite da sede da empresa, meia-noite UTC ou o cut-off do processador de pagamentos. Cada regra cria uma experiência diferente.
Bom texto de produto nomeia o lugar ou usa uma contagem decrescente ligada ao prazo real. Bom backend guarda o prazo como um instante preciso depois de a regra de negócio ser aplicada. Boa documentação de suporte diz que regra foi usada.
Checklist prática antes de lançar código de data/hora
- Este valor é um instante, uma data/hora local, uma data pura, uma duração ou uma regra de recorrência?
- Se é local, que fuso IANA controla a regra?
- Se é uma data pura, evitámos inventar meia-noite UTC?
- As lacunas e dobras de DST têm comportamento explícito?
- As unidades de timestamp estão nomeadas, validadas e testadas?
- Os relatórios definem o sistema de número de semana e o intervalo de datas?
- Os testes correm fora do fuso horário do programador?
- O texto visível diz o suficiente para uma pessoa agir corretamente?
A regra mais profunda
Bugs de data e hora raramente são causados por falta de código inteligente. São causados por um substantivo em falta. “Amanhã” precisa de lugar. “Trinta dias” precisa de dizer calendário ou duração. “Semana 1” precisa de sistema. “Timestamp” precisa de unidade. “Fim do dia” precisa de jurisdição ou fuso horário.
Quando o substantivo é explícito, o código normalmente fica mais simples. A coluna da base de dados recebe o tipo correto. O contrato da API surpreende menos. A interface consegue explicar o resultado. E a próxima pessoa que lê o código não precisa de reconstruir o que “agora mais um dia” devia significar.