Se você já armazenou uma data em um banco de dados, construiu uma API ou depurou um bug de agendamento que só aparecia em determinados países, provavelmente já se deparou com a relação entre Unix timestamp UTC e o horário local. A confusão é real e causa bugs sérios em produção. Este artigo explica exatamente o que é UTC, por que Unix timestamps são definidos em UTC por padrão, como os offsets de fuso horário funcionam e como converter timestamps corretamente em JavaScript, Python e PHP. Você também vai encontrar os erros mais comuns que desenvolvedores cometem e como evitá-los.
Índice
Pontos principais:
- Um Unix timestamp sempre conta segundos a partir de 1º de janeiro de 1970, 00:00:00 UTC - ele não carrega nenhum fuso horário.
- UTC é o ponto de referência universal; o horário local é simplesmente UTC mais ou menos um offset.
- Converter um timestamp para horário local corretamente exige conhecer o fuso horário de destino, não apenas um número de offset.
- O horário de verão (DST) altera o offset de um fuso horário, e é por isso que fixar offsets no código causa bugs.
O que é UTC e por que importa?
UTC significa Tempo Universal Coordenado (do inglês Coordinated Universal Time). É o padrão de tempo primário pelo qual o mundo regula relógios e horários. Ao contrário dos fusos horários, o UTC não tem offset - ele é o ponto zero. Não observa horário de verão e não se ajusta para nenhum país ou região.
Antes do UTC, havia o GMT (Greenwich Mean Time), e muitas pessoas ainda usam os dois termos como sinônimos. Tecnicamente, UTC e GMT diferem levemente, mas para a maioria das finalidades em software eles são tratados como equivalentes. O UTC foi formalmente definido pela recomendação ITU-R TF.460 e é mantido por relógios atômicos.
Para desenvolvedores, UTC importa porque fornece um único ponto de referência sem ambiguidade. Se o seu servidor em Frankfurt e o seu usuário em São Paulo registram um evento usando UTC, você sempre pode comparar esses dois timestamps corretamente. Se cada um registrar no horário local sem metadados, você tem um problema.
Por que Unix timestamps são sempre UTC
Um Unix timestamp (também chamado de epoch time ou POSIX time) é definido como o número de segundos decorridos desde 1º de janeiro de 1970, 00:00:00 UTC. Esse ponto de ancoragem - meia-noite de 1º de janeiro de 1970, em UTC - faz parte da própria definição. Não existe uma versão do Unix time que começa no horário local de Nova York ou de Tóquio.
Isso significa que a resposta para "o Unix timestamp é sempre UTC?" é sim, por definição. O número 1700000000 representa exatamente o mesmo instante no tempo para qualquer pessoa no planeta. O que muda entre os usuários é como esse momento é exibido no fuso horário local de cada um.
Para entender a fundo o conceito que sustenta isso, leia nosso artigo sobre Epoch Time: A Base dos Unix Timestamps.
Esse design ancorado em UTC é uma das propriedades mais úteis do Unix time. Como o número em si não carrega nenhuma informação de fuso horário, dois sistemas em países diferentes podem trocar um timestamp e ambos sabem exatamente a qual momento ele se refere, sem nenhuma negociação extra.
UTC vs. Horário Local e Fusos Horários
UTC é a referência. O horário local é o que você obtém ao aplicar uma regra de fuso horário sobre ele. Um fuso horário não é apenas um offset fixo - é uma região nomeada com um conjunto de regras que podem mudar ao longo do tempo (principalmente por causa do horário de verão).
Por exemplo, o "Eastern Time" nos Estados Unidos é UTC-5 no inverno e UTC-4 no verão. O nome do fuso horário é "America/New_York" no banco de dados de fusos horários da IANA, que é a fonte oficial das regras de fuso horário usada pela maioria das linguagens de programação e sistemas operacionais.
A conclusão prática: quando você armazena um Unix timestamp no seu banco de dados, está armazenando um valor UTC. Quando você o exibe para um usuário, você o converte para o fuso horário local dele. Nunca armazene horário local como um número bruto e assuma que é UTC. É aí que os bugs começam.
Como funcionam os offsets UTC
Um offset UTC descreve o quanto um fuso horário está distante do UTC. Ele é escrito como +HH:MM ou -HH:MM. Alguns exemplos:
- UTC+02:00 - Horário de Verão da Europa Central (CEST), usado na Alemanha e na França durante o verão.
- UTC-05:00 - Horário Padrão do Leste (EST), usado na Costa Leste dos EUA no inverno.
- UTC+05:30 - Horário Padrão da Índia (IST), que tem um offset de meia hora.
- UTC+00:00 - O próprio UTC, também usado pelo Reino Unido no inverno (GMT).
Para converter manualmente um epoch time UTC para horário local, você soma ou subtrai o offset em segundos. Para UTC+02:00, isso é 2 * 3600 = 7200 segundos. Então, se o seu Unix timestamp for 1700000000, o horário local em UTC+02:00 corresponderia a 1700000000 + 7200 antes da formatação.
No entanto, fazer isso manualmente é arriscado porque os offsets mudam com o horário de verão. Sempre use uma biblioteca de fuso horário adequada em vez de fixar um offset no código. Veja nosso guia sobre Como Converter Unix Timestamp para Data para uma análise mais aprofundada dos métodos de conversão.
Convertendo Unix timestamps para horário local
Aqui está um exemplo concreto usando o Unix timestamp 1700000000, que corresponde a 14 de novembro de 2023, 22:13:20 UTC. Vamos convertê-lo para "America/New_York" (UTC-5 em novembro) em três linguagens.
JavaScript
const ts = 1700000000;
// JavaScript Date recebe milissegundos
const date = new Date(ts * 1000);
// Exibir no horário local de Nova York
const options = {
timeZone: 'America/New_York',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
};
console.log(date.toLocaleString('en-US', options));
// Output: November 14, 2023 at 05:13:20 PM ESTNote que o objeto Date do JavaScript armazena o tempo internamente como milissegundos UTC. O método toLocaleString com a opção timeZone cuida do offset e das regras de horário de verão para você.
Python
from datetime import datetime, timezone
import zoneinfo # Python 3.9+
ts = 1700000000
# Cria um datetime ciente de UTC a partir do timestamp
utc_dt = datetime.fromtimestamp(ts, tz=timezone.utc)
# Converte para o horário local de Nova York
ny_tz = zoneinfo.ZoneInfo('America/New_York')
local_dt = utc_dt.astimezone(ny_tz)
print(local_dt.strftime('%Y-%m-%d %H:%M:%S %Z'))
# Output: 2023-11-14 17:13:20 ESTO ponto-chave aqui é usar datetime.fromtimestamp(ts, tz=timezone.utc). Se você omitir o argumento tz, o Python usa o fuso horário local do servidor para interpretar o timestamp - o que é uma fonte comum de bugs.
PHP
$ts = 1700000000;
// Cria um objeto DateTime a partir do Unix timestamp (sempre UTC)
$dt = new DateTime('@' . $ts);
// Define o fuso horário de destino
$dt->setTimezone(new DateTimeZone('America/New_York'));
echo $dt->format('Y-m-d H:i:s T');
// Output: 2023-11-14 17:13:20 ESTEm PHP, o prefixo @ ao construir um objeto DateTime indica ao PHP que o valor deve ser tratado como um Unix timestamp (UTC). Sem ele, o PHP pode interpretar a string usando a configuração de fuso horário padrão do servidor.
Erros comuns que desenvolvedores cometem
Até desenvolvedores experientes tropeçam no tratamento de fusos horários. Veja os problemas mais frequentes:
1. Assumir que o horário local do servidor é UTC
Se o seu servidor está configurado para "Europe/Berlin" e você chama time() no PHP ou datetime.now() no Python sem um argumento de fuso horário, você obtém o horário local - não UTC. Sempre seja explícito sobre UTC ao registrar timestamps.
2. Fixar offsets UTC no código em vez de usar nomes de fuso horário
Usar +02:00 como offset fixo para a Alemanha vai quebrar no inverno, quando a Alemanha muda para +01:00. Sempre use fusos horários nomeados como Europe/Berlin para que a biblioteca aplique automaticamente as regras de horário de verão corretas.
3. Armazenar timestamps como strings de horário local sem metadados de fuso horário
Uma string como 2023-11-14 17:13:20 em um banco de dados é ambígua. É UTC? EST? BRT? Se você armazenar timestamps como inteiros Unix ou como strings ISO 8601 com offset UTC (por exemplo, 2023-11-14T22:13:20Z), o significado é inequívoco. Veja nossa comparação entre Unix Timestamp vs ISO 8601 para orientação sobre qual escolher.
4. Ignorar o horário de verão no agendamento
Se você agenda uma tarefa para rodar às "09:00 todo dia" somando 86400 segundos ao último horário de execução, ela vai deslocar uma hora nos dias em que o horário de verão muda. Use uma biblioteca de cron ou agendador que entenda as regras de fuso horário.
5. Confundir milissegundos e segundos
O JavaScript usa milissegundos para seu objeto Date, mas a maioria dos Unix timestamps está em segundos. Passar um timestamp em segundos para new Date() sem multiplicar por 1000 resulta em uma data em janeiro de 1970. Isso é abordado em detalhes no nosso artigo sobre Segundos vs Milissegundos vs Microssegundos.
Conclusão
Unix timestamps e UTC são inseparáveis por design. O timestamp é sempre um valor UTC - uma contagem de segundos a partir de uma âncora UTC fixa. O horário local é apenas um formato de exibição, não um tipo diferente de timestamp. A abordagem mais segura é armazenar tudo como UTC, converter para horário local somente no momento da exibição e sempre usar fusos horários nomeados em vez de offsets fixos no código. Siga essas regras e a grande maioria dos bugs relacionados a fuso horário simplesmente deixa de acontecer. Para as melhores práticas de armazenamento desses valores, veja nosso guia sobre Unix Timestamps em Bancos de Dados.
Converta Qualquer Unix Timestamp para UTC ou Horário Local - Instantaneamente
Cole qualquer Unix timestamp e veja a conversão para UTC e para o seu fuso horário local com um clique. Gratuito, sem cadastro, e funciona com segundos e milissegundos.
Experimente Nossa Ferramenta Gratuita →
Sim. Por definição, um Unix epoch timestamp conta segundos a partir de 1º de janeiro de 1970, 00:00:00 UTC. O número em si não carrega nenhum fuso horário - ele é um valor UTC. O fuso horário só se torna relevante quando você converte o timestamp em uma data e hora legível por humanos para fins de exibição.
Use a biblioteca de fuso horário nativa da sua linguagem. Em JavaScript, use toLocaleString com a opção timeZone. Em Python, use datetime.fromtimestamp(ts, tz=timezone.utc).astimezone() com um fuso horário nomeado. Em PHP, crie um DateTime a partir do timestamp e chame setTimezone(). Sempre use fusos horários nomeados, não offsets brutos.
Isso geralmente acontece porque uma função está usando o fuso horário padrão do servidor em vez de UTC ao interpretar o timestamp. Em Python, omitir o argumento tz em datetime.fromtimestamp() usa o horário local do sistema. Em PHP, não usar o prefixo @ tem o mesmo efeito. Sempre seja explícito sobre UTC no momento da criação.
Um offset UTC é o número de horas (e às vezes minutos) que um fuso horário está à frente ou atrás do UTC. Por exemplo, UTC-05:00 está cinco horas atrás do UTC. Ao converter um Unix timestamp para horário local, o offset é aplicado ao valor UTC. Como os offsets mudam com o horário de verão, você deve usar um fuso horário nomeado em vez de um offset fixo no código.
Sim, e essa é uma das principais vantagens dos Unix timestamps. Como todo timestamp é um valor UTC, você pode comparar quaisquer dois timestamps diretamente como inteiros. Um número maior sempre significa um momento posterior no tempo, independentemente de onde os timestamps foram gerados. Nenhuma conversão de fuso horário é necessária para a comparação.