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,
EXTRACT(EPOCH FROM TIMESTAMP '2026-06-17 12:00:00') AS unix_seconds;
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;
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.
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.
EXTRACT(EPOCH FROM ...)responde a uma das perguntas mais comuns da analise: "quantos segundos se passaram?". Sobre uminterval, ela devolve a duracao completa em segundos; sobre um timestamp, devolve o tempo Unix, os segundos desde1970-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
EPOCHe que o tempo vira um numerodouble precisioncomum 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 detimestamptze fusos horarios.Dois modos: intervalo versus timestamp
O comportamento do
EPOCHdepende do tipo do argumento, e esse e o ponto central: a mesma expressao devolve ou uma duracao, ou um momento absoluto.interval, o resultado e a duracao total desse intervalo em segundos.interval '1 day'da86400, os segundos que um dia tem.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; -- 1781697600O tipo do resultado e
double precision, entao os segundos fracionarios (milissegundos) sao preservados. Nao confunda os dois modos:86400e o comprimento de um dia, enquanto1781697600e o momento especifico2026-06-17 12:00 UTCna 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 cadaEPOCHpergunte-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
timestampproduz uminterval, e oEPOCHo 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.0com o ponto decimal, nao por3600. Embora oEPOCHja retornedouble precisione a divisao seja fracionaria, o habito do.0te salva em expressoes onde o numerador acaba sendo um inteiro e a divisao inteira descartaria o resto.Agregados: tempo medio de conclusao
O
EPOCHse encaixa muito bem nas funcoes de agregacao. Vamos calcular o tempo medio de envio em horas por pais, juntandoorderscomusers: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
EPOCHconverte cada diferenca em segundos, oAVGtira 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 compercentile_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_timestampo transforma em uma data legivel. E o inverso deEXTRACT(EPOCH FROM timestamp). Passe a ele o mesmo1781697600que produzimos acima e voce deve voltar ao momento original:SELECT to_timestamp(1781697600) AS as_timestamp; -- 2026-06-17 12:00:00+00A volta se fecha:
EXTRACT(EPOCH FROM ...)transformou2026-06-17 12:00:00em1781697600, e oto_timestampdevolve 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_timestamprecebe o tempo Unix e devolve umtimestamptzem 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 umtimestamptzsempre conta a partir da epoca em UTC, o que nao e ambiguo. Em umtimestamp"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.timestamptz, o resultado e estavel e independente doTimeZoneda sessao.EXTRACT(EPOCH ...)direto: useUNIX_TIMESTAMP(ts)para o tempo Unix eTIMESTAMPDIFF(SECOND, a, b)para a duracao.toUnixTimestamp(ts)para o tempo Unix edateDiff('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
EPOCHquase sempre vivem nos dados de borda, nao na funcao:timestampsem fuso contratimestamptzcom fuso, intervalos negativos quandoshipped_atprecedecreated_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 comNULL, duracao zero e uma data de borda: no "caminho feliz" os motores coincidem e divergem justamente nesses valores. Uma conclusao pratica: se um numero doEPOCHafeta dinheiro, um SLA ou um identificador externo, nao deixe seu significado implicito. E cuide do desempenho:EXTRACT(EPOCH FROM ts)em umWHEREenvolve a coluna em uma funcao e muitas vezes bloqueia um indice comum sobrets, entao para filtros por intervalo de datas compare a coluna com os limites e reserve oEPOCHpara os calculos doSELECT.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.