sqlpostgresqlepochdate-time

EXTRACT(EPOCH FROM ...) no SQL: duracoes em segundos e tempo Unix

Como EXTRACT(EPOCH FROM ...) transforma um intervalo em segundos totais e um timestamp em tempo Unix, por que esses dois casos diferem e como voltar com to_timestamp.

4 min de leituraReferencesql · postgresql · epoch · date-time · interval · unix-time

EXTRACT(EPOCH FROM ...) responde a uma das perguntas mais comuns da analise: "quantos segundos se passaram?". Sobre um interval, ela devolve a duracao completa em segundos; sobre um timestamp, devolve o tempo Unix, os segundos desde 1970-01-01 00:00:00 UTC. Voce recorre a ela para medir quanto durou um processo, para expressar um SLA em segundos ou minutos, ou para transformar uma data em um numero simples para armazenamento, ordenacao e troca com filas, logs e APIs externas.

O charme do EPOCH e que o tempo vira um numero double precision comum que voce pode dividir, tirar a media, somar e comparar como qualquer outra metrica. Este artigo percorre quatro casos praticos e termina com uma pegadinha de timestamptz e fusos horarios.

Dois modos: intervalo versus timestamp

O comportamento do EPOCH depende do tipo do argumento, e esse e o ponto central: a mesma expressao devolve ou uma duracao, ou um momento absoluto.

  • Sobre um interval, o resultado e a duracao total desse intervalo em segundos. interval '1 day' da 86400, os segundos que um dia tem.
  • Sobre um timestamp, o resultado e o tempo Unix, os segundos desde a epoca de 1970. E um numero enorme, nao uma duracao.
SELECT
  EXTRACT(EPOCH FROM INTERVAL '1 day')                 AS interval_seconds, -- 86400
  EXTRACT(EPOCH FROM TIMESTAMP '2026-06-17 12:00:00')  AS unix_seconds;     -- 1781697600

O tipo do resultado e double precision, entao os segundos fracionarios (milissegundos) sao preservados. Nao confunda os dois modos: 86400 e o comprimento de um dia, enquanto 1781697600 e o momento especifico 2026-06-17 12:00 UTC na linha do tempo Unix. Misturar a duracao de um intervalo com o tempo Unix de um timestamp na mesma expressao produz um numero sem sentido, entao antes de cada EPOCH pergunte-se: estou medindo uma duracao ou fixando um ponto no tempo?

Duracao entre dois timestamps

O padrao mais comum e subtrair um timestamp de outro para medir o tempo decorrido. Subtrair dois valores timestamp produz um interval, e o EPOCH o transforma em segundos.

SELECT
  id,
  EXTRACT(EPOCH FROM (shipped_at - created_at)) AS seconds_to_ship
FROM orders
WHERE status = 'shipped';

Para obter minutos ou horas, divida o resultado por 60 ou 3600:

SELECT
  id,
  EXTRACT(EPOCH FROM (shipped_at - created_at)) / 3600.0 AS hours_to_ship
FROM orders
WHERE status = 'shipped';

Divida por 3600.0 com o ponto decimal, nao por 3600. Embora o EPOCH ja retorne double precision e a divisao seja fracionaria, o habito do .0 te salva em expressoes onde o numerador acaba sendo um inteiro e a divisao inteira descartaria o resto.

Agregados: tempo medio de conclusao

O EPOCH se encaixa muito bem nas funcoes de agregacao. Vamos calcular o tempo medio de envio em horas por pais, juntando orders com users:

SELECT
  u.country,
  AVG(EXTRACT(EPOCH FROM (o.shipped_at - o.created_at))) / 3600.0 AS avg_hours
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'shipped'
GROUP BY u.country
ORDER BY avg_hours DESC;

O EPOCH converte cada diferenca em segundos, o AVG tira a media desses numeros e so entao voce divide por 3600. Isso e mais confiavel do que tentar tirar a media dos proprios intervalos. O mesmo truque vale para medianas e percentis com percentile_cont(0.5): calcule as duracoes em segundos e deixe a conversao para horas ou minutos para o fim.

De volta: de segundos para um timestamp

Se uma coluna armazena o tempo Unix como numero, o to_timestamp o transforma em uma data legivel. E o inverso de EXTRACT(EPOCH FROM timestamp). Passe a ele o mesmo 1781697600 que produzimos acima e voce deve voltar ao momento original:

SELECT
  to_timestamp(1781697600) AS as_timestamp; -- 2026-06-17 12:00:00+00

A volta se fecha: EXTRACT(EPOCH FROM ...) transformou 2026-06-17 12:00:00 em 1781697600, e o to_timestamp devolve esse mesmo momento em UTC. Tambem e util para aritmetica: some um numero fixo de segundos e volte para um timestamp.

SELECT
  to_timestamp(
    EXTRACT(EPOCH FROM created_at) + 3600
  ) AS one_hour_later
FROM users
LIMIT 5;

O to_timestamp recebe o tempo Unix e devolve um timestamptz em UTC, entao como o momento aparece depois depende do fuso horario da sessao.

Pegadinha: EPOCH sobre timestamptz e fusos horarios

A grande confusao e sobre os tipos. EXTRACT(EPOCH FROM ...) em um timestamptz sempre conta a partir da epoca em UTC, o que nao e ambiguo. Em um timestamp "ingenuo" (sem fuso), o PostgreSQL tambem trata o valor como UTC, o que pode nao corresponder a sua expectativa se os dados foram armazenados em um fuso local: a mesma linha da um tempo Unix diferente conforme o tipo que voce escolher.

  • Para timestamptz, o resultado e estavel e independente do TimeZone da sessao.
  • O MySQL nao tem um EXTRACT(EPOCH ...) direto: use UNIX_TIMESTAMP(ts) para o tempo Unix e TIMESTAMPDIFF(SECOND, a, b) para a duracao.
  • O ClickHouse usa toUnixTimestamp(ts) para o tempo Unix e dateDiff('second', a, b) para uma diferenca.
-- MySQL: duracao em segundos
SELECT TIMESTAMPDIFF(SECOND, created_at, shipped_at) AS seconds_to_ship
FROM orders
WHERE status = 'shipped';

Os problemas com EPOCH quase sempre vivem nos dados de borda, nao na funcao: timestamp sem fuso contra timestamptz com fuso, intervalos negativos quando shipped_at precede created_at, e perda de precisao ao depois arredondar os segundos para horas. Antes de portar uma consulta entre PostgreSQL, MySQL e ClickHouse, rode-a em uma tabela pequena com NULL, duracao zero e uma data de borda: no "caminho feliz" os motores coincidem e divergem justamente nesses valores. Uma conclusao pratica: se um numero do EPOCH afeta dinheiro, um SLA ou um identificador externo, nao deixe seu significado implicito. E cuide do desempenho: EXTRACT(EPOCH FROM ts) em um WHERE envolve a coluna em uma funcao e muitas vezes bloqueia um indice comum sobre ts, entao para filtros por intervalo de datas compare a coluna com os limites e reserve o EPOCH para os calculos do SELECT.

Tenha em mente a divisao: um intervalo da uma duracao, um timestamp da um momento. O mesmo EPOCH, mas o significado do numero e o oposto exato.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador