7 bugs de timestamp Unix que todo desenvolvedor já lançou
Estes são os erros de timestamp que causam incidentes em produção: divergências silenciosas de unidade, suposições de fuso, o ×1000 esquecido, o armazenamento como string, o parsing ambíguo e os erros nos limites do horário de verão. Cada bug inclui o sintoma, a causa raiz e uma correção prática.
Bug 1: Faltou × 1000 no JavaScript
new Date(1700000000) produz uma data próxima de janeiro de 1970, não de novembro de 2023. O construtor Date do JavaScript espera milissegundos, mas a maioria das APIs de servidor e bancos retorna segundos Unix. Esse bug costuma aparecer primeiro em painéis de administração porque o payload do backend é JSON válido e a interface simplesmente exibe o ano errado.
- Wrong: new Date(response.created_at) — if created_at is Unix seconds
- Right: new Date(response.created_at * 1000)
- Detection: if your date shows a year near 1970, the timestamp is in seconds not milliseconds
- Safe wrapper: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)
Bug 2: Unidades misturadas no limite de um sistema
O JavaScript retorna milissegundos. Python, Go e a maioria das APIs do servidor retornam segundos. Quando o JavaScript envia um timestamp a um backend Python sem converter, o backend interpreta um valor em milissegundos como segundos, colocando o evento no ano 55792. O sentido inverso é igualmente ruim: a interface recebe segundos, passa-os direto a new Date() e exibe janeiro de 1970.
- JavaScript sends: Date.now() → 1700000000000 (ms)
- Python receives: datetime.fromtimestamp(1700000000000) → year 55792
- Fix: divide by 1000 before sending to a non-JavaScript system
- Better fix: use ISO 8601 strings at API boundaries — they are unambiguous
Bug 3: Confiar no fuso local do servidor
Código que usa datetime.fromtimestamp() do Python sem especificar UTC produz resultados diferentes conforme o fuso configurado do servidor. Funciona em desenvolvimento e quebra em produção ao implantar em outra região. A mesma categoria de bug aparece no Node quando o código formata datas sem uma opção timeZone explícita e depois congela o resultado nos testes.
- Wrong: datetime.fromtimestamp(ts) — uses server's local timezone
- Right: datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
- Wrong in JS: date.toLocaleDateString() — uses the Node process timezone
- Right in JS: date.toISOString() or Intl.DateTimeFormat with an explicit timeZone option
Bug 4: Armazenar timestamps como strings
Armazenar um timestamp como string formatada em uma coluna VARCHAR parece inofensivo até você precisar ordenar por tempo, consultar um intervalo ou fazer aritmética. A comparação de strings de datas só funciona com campos ISO 8601 estritos e preenchidos com zeros. Strings legíveis como Nov 15, 2023 ou 1/2/24 são formatos de exibição, não de armazenamento.
- Wrong: INSERT INTO events (created) VALUES ('Nov 15, 2023') — not sortable
- Wrong: INSERT INTO events (created) VALUES ('2023-11-15') — loses time component
- Right: use a native DATETIME/TIMESTAMPTZ column or BIGINT for Unix milliseconds
- If a string is unavoidable: use ISO 8601 with full precision: '2023-11-15T06:13:20Z'
Bug 5: Parsear strings de data ambíguas
new Date('2024-01-01') e new Date('2024/01/01') parecem equivalentes mas se comportam de modo diferente. O formato ISO 8601 com hífens é parseado como meia-noite UTC; o formato com barras depende da implementação e a maioria dos navegadores o parseia como meia-noite local. Um seletor de data, um payload de API e uma linha de banco podem mostrar a mesma data de calendário representando instantes diferentes.
- new Date('2024-01-01') → January 1, 2024 00:00:00 UTC (correct, explicit)
- new Date('2024/01/01') → January 1, 2024 00:00:00 local time (varies by runtime)
- new Date('January 1, 2024') → local time, may be rejected by strict parsers
- Safe rule: always use ISO 8601 with explicit timezone: new Date('2024-01-01T00:00:00Z')
Bug 6: Somar 24 horas para obter amanhã
Somar 86.400.000 milissegundos parece uma forma limpa de obter amanhã, mas os dias de calendário locais nem sempre têm 24 horas. Durante as transições de horário de verão, um dia local pode ter 23 ou 25 horas. Se a lógica do produto significa o próximo dia de calendário local, use aritmética de calendário no fuso de destino, não aritmética de duração em milissegundos UTC.
- Errado para dias de calendário: next = new Date(date.getTime() + 86400000)
- Melhor em código Date local: copie o Date e então chame setDate(d.getDate() + 1)
- Para código de servidor: use uma biblioteca com fusos ou aritmética de datas do banco
- Para agendamentos recorrentes: armazene local_date, local_time e timezone_id, e calcule cada ocorrência
- Adicione testes para as datas de início e fim do horário de verão no fuso de destino
Bug 7: Filtros de fim de dia inclusivos
Uma consulta de analytics comum usa created_at >= start e created_at <= endOfDay. Parece razoável, mas cria bugs de precisão quando o banco armazena milissegundos, microssegundos ou nanossegundos. O padrão mais seguro é um intervalo semiaberto: maior ou igual ao início, e estritamente menor que o início do período seguinte.
- Risky: WHERE created_at <= '2026-05-19 23:59:59'
- Safer: WHERE created_at >= '2026-05-19' AND created_at < '2026-05-20'
- Half-open ranges work for seconds, milliseconds, microseconds, and nanoseconds
- The same pattern works for months: created_at >= monthStart AND created_at < nextMonthStart
Checklist de prevenção em produção
A maioria dos incidentes com timestamps é evitável com nomes, testes e limites explícitos. O objetivo não é tornar cada linha de código de data engenhosa; é tornar a unidade, o fuso e a semântica de intervalo entediantemente óbvios.
- Nomeie os campos numéricos com sua unidade: createdAtMs, expiresAtSeconds, imported_at_ms
- Use UTC para armazenar e fusos IANA explícitos para exibir
- Prefira strings ISO 8601 com Z ou offsets nos limites de API inspecionados por humanos
- Use intervalos semiabertos para filtros de data
- Teste em torno de 1 de janeiro, dias bissextos, transições de horário de verão e o limite do ano 2038
FAQ sobre bugs de timestamp Unix
- Qual é o bug de timestamp Unix mais comum?
- Passar segundos onde se esperam milissegundos, ou o contrário. No JavaScript, new Date(unixSeconds) cai em 1970 porque o construtor espera milissegundos; multiplique primeiro o valor de 10 dígitos por 1000.
- Como prevenir os bugs de segundos-vs-milissegundos?
- Nomeie os campos com sua unidade (createdAtMs, expiresAtSeconds), converta nos limites do sistema e prefira strings ISO 8601 nos contratos de API para que o valor se autodescreva.
- Por que somar 86.400.000 ms quebra «amanhã»?
- Os dias de calendário locais têm 23 ou 25 horas durante as transições de horário de verão, então somar 24 horas fixas pode cair na data errada. Use aritmética de calendário no fuso de destino.