Introdução
Um Unix timestamp é um dos conceitos mais fundamentais na computação. Ele representa um momento específico no tempo como um número inteiro simples — o número de segundos decorridos desde o epoch Unix (1 de janeiro de 1970, 00:00:00 UTC). Este formato simples e universal sustenta tudo, desde registros de banco de dados e respostas de API até arquivos de log e tarefas agendadas.
Ao contrário de strings de data legíveis por humanos como "9 de abril de 2024", um Unix timestamp não tem fuso horário, localidade nem ambiguidade. O número 1712620800 se refere exatamente ao mesmo momento em qualquer lugar do mundo. Essa propriedade o torna a língua franca de datas e horas na engenharia de software.
História do Unix Epoch
O sistema operacional Unix foi desenvolvido nos Bell Labs no final dos anos 1960 e início dos anos 1970 por Ken Thompson, Dennis Ritchie e colegas. O rastreamento de tempo foi integrado ao kernel desde o início, mas a escolha exata da data de epoch evoluiu durante o desenvolvimento inicial.
Algumas das primeiras versões do Unix usavam 1º de janeiro de 1971 como epoch. Outras experimentaram com 1º de janeiro de 1969. Por fim, 1º de janeiro de 1970, 00:00:00 UTC foi padronizado — uma data "redonda" próxima o suficiente da criação do sistema para que os timestamps de eventos reais fossem pequenos inteiros positivos.
A escolha de 1970-01-01 é, em essência, arbitrária. O que importa não é a data em si, mas a consistência da convenção. Como todos os sistemas concordam com o mesmo epoch, os timestamps podem ser trocados entre programas, linguagens e plataformas sem nenhuma sobrecarga de conversão.
Esta convenção foi posteriormente formalizada no POSIX (Portable Operating System Interface), que define um Unix timestamp como o número de segundos não-bissextos desde 1970-01-01T00:00:00Z.
Segundos vs. Milissegundos
Nem todos os "timestamps" são iguais. A distinção mais importante é a unidade:
- Unix timestamp (POSIX): segundos desde o epoch — ex.:
1712620800 - JavaScript
Date: milissegundos desde o epoch — ex.:1712620800000 - Java
System.currentTimeMillis(): milissegundos desde o epoch - Go
time.Now().Unix(): segundos;time.Now().UnixNano()fornece nanossegundos - Python
time.time(): segundos em ponto flutuante
A diferença de fator 1000 entre segundos e milissegundos é uma fonte comum de bugs. Passar um timestamp em milissegundos para código que espera segundos produz uma data no futuro distante (ano ~56.000); o inverso produz uma data em janeiro de 1970.
A conversão é direta:
const unix_ms = unix_s * 1000;
const unix_s = Math.floor(unix_ms / 1000);
Uma regra prática: se o seu timestamp é um número de 10 dígitos, provavelmente está em segundos. Se tem 13 dígitos, provavelmente está em milissegundos.
Formato ISO 8601 e Variantes
ISO 8601 é o padrão internacional para representar datas e horas. Ele define uma família de formatos de string sem ambiguidade:
2024-04-09 # Somente data
2024-04-09T12:00:00 # Data/hora local (sem info de fuso horário)
2024-04-09T12:00:00Z # UTC (Z = hora Zulu = UTC+0)
2024-04-09T20:00:00+08:00 # Com offset UTC (ex.: Asia/Shanghai)
2024-04-09T12:00:00.123Z # Com milissegundos
2024-04-09T12:00:00.123456789Z # Com nanossegundos
O T separa as partes de data e hora. O sufixo Z (de "Zulu", o alfabeto fonético da OTAN para UTC) significa que a hora está em UTC. Um offset como +08:00 significa que a hora local está 8 horas à frente do UTC.
ISO 8601 é o formato recomendado para APIs REST, arquivos de log e qualquer sistema que troca dados temporais entre sistemas. É legível por humanos, ordenável como string e sem ambiguidades.
Fusos Horários e UTC
UTC (Tempo Universal Coordenado) é o principal padrão de tempo do mundo. Não é um fuso horário em si, mas a linha de base a partir da qual todos os fusos horários são definidos. UTC+0 é o mesmo que o Greenwich Mean Time (GMT) no inverno.
Os fusos horários são expressos como offsets do UTC: UTC+5:30 (Índia), UTC-8 (Horário Padrão do Pacífico dos EUA), UTC+9 (Japão). No entanto, offsets brutos não são suficientes para descrever completamente um fuso horário, porque os offsets mudam com o Horário de Verão (DST).
O Banco de Dados de Fusos Horários IANA (também chamado de banco de dados tz ou zoneinfo) é a lista oficial de todos os fusos horários do mundo. Ele usa identificadores como America/New_York, Europe/Lisbon, Asia/Tokyo e America/Sao_Paulo. Esses identificadores encapsulam não apenas o offset UTC atual, mas o registro histórico completo das regras de DST e mudanças políticas.
Todas as principais linguagens de programação e sistemas operacionais incluem o banco de dados IANA:
- JavaScript (Node.js):
Intl.DateTimeFormatcom identificadores IANA - Python: módulo
zoneinfo(Python 3.9+) oupytz - Java:
java.time.ZoneId(ex.:ZoneId.of("America/Sao_Paulo")) - Go:
time.LoadLocation("America/Sao_Paulo")
Nunca use offsets UTC brutos como +05:30 para representar fusos horários na lógica da aplicação. Use identificadores IANA, porque os offsets mudam sazonalmente.
Complicações do Horário de Verão (DST)
O Horário de Verão (DST) é a prática de adiantar os relógios em uma hora durante os meses de verão para estender a luz do dia à tarde. É observado na maior parte da América do Norte e Europa, e em partes da América do Sul, Oriente Médio e Oceania. Muitos países, incluindo Japão, China e Índia, não observam o DST.
O DST introduz duas anomalias clássicas:
Adiantamento de primavera (Spring forward): Os relógios saltam das 2:00 diretamente para as 3:00. A janela de 60 minutos entre 2:00 e 3:00 nunca existe no horário local. Se você agendar uma tarefa para as 2:30, ela será executada às 3:30 ou completamente ignorada, dependendo do agendador.
Retrocesso de outono (Fall back): Os relógios voltam das 2:00 para 1:00. A janela de 60 minutos entre 1:00 e 2:00 ocorre duas vezes. Se você registrar uma entrada de log às "1:45 hora local", ela é ambígua — pode ser da primeira ou segunda ocorrência dessa hora.
Os Unix timestamps são completamente imunes ao DST porque são sempre relativos ao UTC. O número 1712620800 sempre se refere ao mesmo instante, independentemente de onde você esteja ou qual estação seja.
A regra de ouro: sempre armazene e transmita timestamps em UTC. Converta para a hora local apenas na camada de apresentação, imediatamente antes de exibir para um usuário humano.
O Problema do Ano 2038
O Problema do Ano 2038 (também chamado de Y2K38 ou Bug do Milênio Unix) é uma vulnerabilidade de software semelhante em natureza ao bug do ano 2000.
A causa raiz: muitos sistemas legados armazenam Unix timestamps como um inteiro de 32 bits com sinal. O valor máximo de um inteiro de 32 bits com sinal é 2.147.483.647. Isso corresponde a:
2038-01-19 03:14:07 UTC
Um segundo após esse momento, um inteiro de 32 bits com sinal transborda e retorna ao valor mais negativo: -2.147.483.648, que corresponde a 1901-12-13 20:45:52 UTC. Sistemas que transbordam subitamente acreditarão que a data é 1901.
Sistemas potencialmente afetados:
- Sistemas embarcados legados e dispositivos IoT compilados para arquiteturas de 32 bits
- Esquemas de banco de dados antigos que usam o tipo
TIMESTAMPdo MySQL (usava armazenamento de 32 bits em versões anteriores à 8.0) - Kernels Linux de 32 bits (resolvido no kernel para plataformas de 64 bits)
- Alguns sistemas de arquivos mais antigos que registram tempos de modificação como inteiros de 32 bits
A solução é direta: migrar todo o armazenamento de timestamps para inteiros de 64 bits com sinal. Um timestamp de 64 bits pode representar datas até aproximadamente o ano 292.277.026.596 — muito além de qualquer preocupação prática. A maioria dos sistemas modernos de 64 bits já lida com isso corretamente.
Trabalhando com Timestamps em Diferentes Linguagens
JavaScript
// Hora atual
const now = Date.now(); // milissegundos desde o epoch
const unix = Math.floor(now / 1000); // converter para segundos
// Analisar um Unix timestamp
const date = new Date(unix * 1000);
console.log(date.toISOString()); // "2024-04-09T12:00:00.000Z"
console.log(date.toLocaleString("pt-BR", { timeZone: "America/Sao_Paulo" }));
Python
import time
import datetime
# Unix timestamp atual (segundos)
unix = int(time.time())
# Converter para datetime UTC
dt = datetime.datetime.fromtimestamp(unix, tz=datetime.timezone.utc)
print(dt.isoformat()) # "2024-04-09T12:00:00+00:00"
# Usar zoneinfo para fuso horário IANA
from zoneinfo import ZoneInfo
dt_local = dt.astimezone(ZoneInfo("America/Sao_Paulo"))
print(dt_local.isoformat())
Go
package main
import (
"fmt"
"time"
)
func main() {
unix := time.Now().Unix() // int64 segundos
t := time.Unix(unix, 0).UTC()
fmt.Println(t.Format(time.RFC3339)) // "2024-04-09T12:00:00Z"
loc, _ := time.LoadLocation("America/Sao_Paulo")
fmt.Println(t.In(loc).Format(time.RFC3339))
}
Java
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
long unix = Instant.now().getEpochSecond(); // segundos
Instant instant = Instant.ofEpochSecond(unix);
System.out.println(instant.toString()); // "2024-04-09T12:00:00Z"
ZonedDateTime zdt = instant.atZone(ZoneId.of("America/Sao_Paulo"));
System.out.println(zdt.toString());
Rust
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let unix = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("O tempo voltou para trás")
.as_secs(); // u64 segundos
println!("{}", unix); // ex.: 1712620800
}
Tabela de Formatos de Timestamp Comuns
| Formato | Exemplo | Usado em |
|---|---|---|
| Unix (segundos) | 1712620800 |
Unix/Linux, APIs POSIX |
| Unix (milissegundos) | 1712620800000 |
JavaScript, Java |
| Unix (microssegundos) | 1712620800000000 |
PostgreSQL, algumas APIs |
| Unix (nanossegundos) | 1712620800000000000 |
Go, Rust |
| ISO 8601 UTC | 2024-04-09T12:00:00Z |
APIs REST, bancos de dados |
| ISO 8601 com offset | 2024-04-09T20:00:00+08:00 |
Aplicativos de calendário |
| RFC 2822 | Tue, 09 Apr 2024 12:00:00 +0000 |
Cabeçalhos de email |
| RFC 3339 | 2024-04-09T12:00:00Z |
Protocolos de Internet |
| Data HTTP | Tue, 09 Apr 2024 12:00:00 GMT |
Cabeçalhos HTTP |
Aritmética de Timestamps
Como um Unix timestamp é simplesmente um número, a aritmética sobre timestamps é trivial.
Calcular a duração entre dois timestamps:
start = 1712620800
end = 1712707200
duracao_segundos = end - start # 86400 segundos = exatamente 1 dia
Encontrar uma data futura ou passada:
const now = Math.floor(Date.now() / 1000);
const umaSemanaMais = now + 7 * 24 * 60 * 60; // +604800 segundos
const trintaDiasAtras = now - 30 * 24 * 60 * 60; // -2592000 segundos
Durações comuns em segundos:
| Duração | Segundos |
|---|---|
| 1 minuto | 60 |
| 1 hora | 3.600 |
| 1 dia | 86.400 |
| 1 semana | 604.800 |
| 30 dias | 2.592.000 |
| 1 ano (365 dias) | 31.536.000 |
Nota: para aritmética com consciência de calendário (ex.: "adicionar 1 mês"), use uma biblioteca de datas em vez de segundos brutos, porque os meses têm comprimentos diferentes e o DST pode tornar alguns dias de 23 ou 25 horas.
Casos de Uso
Registro de aplicações: Entradas de log com Unix timestamps podem ser ordenadas, filtradas e comparadas entre sistemas distribuídos rodando em fusos horários diferentes — tudo sem ambiguidade.
APIs REST: Retornar timestamps como inteiros Unix evita a interpretação de fuso horário no lado do servidor. O cliente lê o inteiro e o formata no fuso horário local do usuário.
Bancos de dados: Armazenar timestamps como inteiros (ou strings ISO 8601) é mais portável do que tipos de data específicos da plataforma. O TIMESTAMPTZ do PostgreSQL armazena internamente em UTC; o DATETIME do MySQL (preferido em vez de TIMESTAMP) evita o limite Y2038.
Tarefas agendadas e cron: Calcular "executar às 3:00 todos os dias" em UTC evita surpresas do DST. Muitos frameworks de agendamento (Kubernetes CronJobs, agendamentos do GitHub Actions) usam UTC por convenção.
Expiração de cache e TTL: Os cabeçalhos HTTP Cache-Control: max-age=3600 e Expires usam Unix timestamps absolutos ou segundos relativos; CDNs e navegadores dependem de aritmética precisa de timestamps para invalidar caches.
Event sourcing e trilhas de auditoria: Logs de eventos imutáveis requerem timestamps que ordena inequivocamente os eventos. Unix timestamps, especialmente em resolução de nanossegundos, oferecem essa garantia mesmo para sistemas de alto throughput.
Boas Práticas
Sempre armazene timestamps em UTC. Nunca armazene a hora local em um banco de dados. Converta para a hora local apenas na camada de apresentação.
Use inteiros de 64 bits. Evite
int32para timestamps em qualquer código novo. Mesmo que seu sistema atualmente lide apenas com datas no futuro próximo, 64 bits é o padrão seguro.Use ISO 8601 para serialização legível por humanos. Quando você precisa de uma representação em string em logs ou APIs, ISO 8601 é inequívoco e ordenável lexicograficamente.
Use identificadores de fuso horário IANA, não offsets.
"America/Sao_Paulo"é correto;"-03:00"é frágil porque o offset muda duas vezes por ano.Valide as unidades do timestamp. Antes de usar um timestamp externo, verifique se está em segundos, milissegundos ou outra unidade. Uma verificação rápida: um número de 10 dígitos provavelmente está em segundos; 13 dígitos provavelmente em milissegundos.
Nunca analise datas com expressões regulares em código de produção. Use a biblioteca padrão da sua linguagem ou uma biblioteca de terceiros bem testada.
Cuidado com "meia-noite". Em fusos horários que observam o DST, a meia-noite pode não existir em certas datas (transições de adiantamento de primavera). Use o meio-dia (12:00 UTC) como hora "representativa" segura para cálculos de data apenas.
Teste próximo às transições de DST. Se sua aplicação envolve agendamento ou cálculos de tempo, escreva testes que exercitem especificamente os limites de adiantamento de primavera e retrocesso de outono para os fusos horários relevantes.
Perguntas Frequentes (FAQ)
P: O que representa o Unix timestamp 0?
R: 1º de janeiro de 1970, 00:00:00 UTC — o epoch Unix. Timestamps negativos representam datas anteriores a 1970.
P: Posso usar timestamps para ordenação?
R: Sim. Como um Unix timestamp é um inteiro monotonicamente crescente, ordenar por timestamp é equivalente a ordenar cronologicamente.
P: O tempo Unix é afetado por segundos bissextos?
R: O POSIX define o tempo Unix como se cada dia tivesse exatamente 86.400 segundos, o que significa que os segundos bissextos não são contados. Um timestamp POSIX é tecnicamente "tempo Unix" ou "tempo POSIX", não o "verdadeiro" Tempo Atômico Internacional (TAI). Na prática, isso raramente importa para código de aplicação.
P: Qual é a data máxima representável com um Unix timestamp?
R: Com um inteiro de 64 bits com sinal, o máximo corresponde ao ano 292.277.026.596. Com um inteiro de 32 bits com sinal, é 2038-01-19 03:14:07 UTC.
P: Como obtenho o Unix timestamp atual no meu navegador?
R: Math.floor(Date.now() / 1000) no console do navegador retorna o Unix timestamp atual em segundos.
P: Por que meu timestamp mostra 1970-01-01 quando o converto?
R: Quase certamente você está passando milissegundos para uma função que espera segundos (ou vice-versa). Divida por 1000 se você ver uma data no futuro distante; multiplique por 1000 se você ver 1º de janeiro de 1970.