O que é o Intl.RelativeTimeFormat

O Intl.RelativeTimeFormat e uma API nativa do JavaScript que formata durações relativas no estilo 'ha 2 horas', 'em 3 dias' ou 'ha 1 mes', com suporte completo a internacionalização. Ela faz parte da especificação ECMAScript Internationalization API (ECMA-402) e esta disponível em todos os navegadores modernos desde 2020.

Antes dessa API existir, a solução padrão era instalar uma biblioteca como date-fns, moment.js ou dayjs somente para formatar esse tipo de string. Isso adicionava kilobytes desnecessários ao bundle e dependência de manutenção externa para algo que o próprio ambiente já suporta.

A API faz parte da família Intl, que cobre formatação de números, moedas, datas absolutas e muito mais. Se você já usou Intl.NumberFormat ou Intl.DateTimeFormat, o padrão e idêntico.

Como funciona por dentro

O Intl.RelativeTimeFormat recebe dois argumentos no construtor: o locale (como 'pt-BR') e um objeto de opcoes. Ele expõe o método format(value, unit), onde value e um número (positivo para futuro, negativo para passado) e unit e a unidade de tempo.

Internamente, a API usa os dados de localização do próprio motor JavaScript (V8, SpiderMonkey, JavaScriptCore), que por sua vez seguem o padrão CLDR (Common Locale Data Repository) do Unicode. Isso significa que a formatação esta sempre alinhada com as convenções reais do idioma, sem você precisar manter strings de tradução.

A grande diferença em relação a bibliotecas externas: a API não calcula a diferença entre duas datas. Ela só formata. Você passa o número e a unidade, ela devolve a string. O calculo fica por sua conta, o que é intencional - deixa a API simples e composavel.

Principais recursos e opcoes

Veja o que você pode configurar no construtor:

  • numeric: 'auto' - usa palavras naturais quando possível ('ontem', 'amanha') em vez de '1 dia atrás'. Recomendado para UX.
  • numeric: 'always' - sempre usa número ('ha 1 dia'). Útil para consistência visual.
  • style: 'long' - forma completa: 'ha 2 horas'.
  • style: 'short' - abreviado: 'ha 2 h.'.
  • style: 'narrow' - mínimo: 'ha 2 h' (varia por idioma).

As unidades disponíveis são: year, quarter, month, week, day, hour, minute, second. Cada uma aceita singular e plural automaticamente.

💡
Dica

Use numeric: 'auto' para interfaces em português. Em vez de 'ha 1 dia', o usuário le 'ontem' - muito mais natural.

Como começar: exemplos passo a passo

Sem instalar nada. Abra o console do navegador ou o Node.js e teste:

const rtf = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });  rtf.format(-2, 'hour');   // 'ha 2 horas'  rtf.format(1, 'day');     // 'amanha'  rtf.format(-1, 'day');    // 'ontem'  rtf.format(-3, 'month');  // 'ha 3 meses'  rtf.format(5, 'minute');  // 'em 5 minutos'

Para calcular a diferença entre duas datas e passar o valor correto, use um helper simples:

function timeAgo(date) {  const seconds = Math.round((date - Date.now()) / 1000);  const rtf = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });    if (Math.abs(seconds) < 60) return rtf.format(seconds, 'second');  if (Math.abs(seconds) < 3600) return rtf.format(Math.round(seconds / 60), 'minute');  if (Math.abs(seconds) < 86400) return rtf.format(Math.round(seconds / 3600), 'hour');  if (Math.abs(seconds) < 2592000) return rtf.format(Math.round(seconds / 86400), 'day');  if (Math.abs(seconds) < 31536000) return rtf.format(Math.round(seconds / 2592000), 'month');  return rtf.format(Math.round(seconds / 31536000), 'year');}  timeAgo(new Date('2026-08-05'));  // 'ha 2 dias'

Exemplo prático: feed de posts com timestamps

Imagine um componente React que exibe quando cada post foi publicado. Antes, você importava date-fns. Agora:

// Sem nenhum import externo function PostCard({ title, publishedAt }) {  const rtf = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });  const diff = Math.round((new Date(publishedAt) - Date.now()) / 3600000);  const label = Math.abs(diff) < 24    ? rtf.format(Math.round(diff), 'hour')    : rtf.format(Math.round(diff / 24), 'day');    return 

{title}

; }

O componente acima não tem nenhuma dependência externa. O bundle final e menor, sem impacto no tempo de carregamento. Para um blog ou feed com centenas de cards, isso se multiplica.

⚠️
Atenção

Instanciar new Intl.RelativeTimeFormat() dentro de um loop ou render frequente pode ter custo de performance. Crie a instância fora da função e reutilize.

Comparação com alternativas

Quando usar cada opcao:

  • Intl.RelativeTimeFormat (nativo): ideal quando você só precisa formatar durações relativas, sem manipulação complexa de datas. Zero dependência, zero bundle extra.
  • date-fns (formatDistanceToNow): continua valendo quando você já usa date-fns para outras operações no projeto (parse, add, subtract). Não faz sentido instalar só para relativo.
  • dayjs (plugin relativeTime): mesma lógica do date-fns. Se já esta no projeto, use. Do contrario, prefira o nativo.
  • moment.js: legado. Pesado, imutável no package. Não adicionar em projetos novos.

O critério e simples: se o projeto já tem a biblioteca por outro motivo, use ela para consistência. Se você ia instalar só para o relativo, use o nativo.

Pontos positivos e limitações

Positivos: zero dependência, zero bundle, suporte nativo a dezenas de idiomas via CLDR, comportamento consistente entre plataformas. Funciona no Node.js (v12+) e em todos os navegadores modernos.

Limitações: a API não calcula a diferença entre datas - você precisa passar o valor calculado. Para casos complexos (fusos horários, calendarioshebraicos, etc.), uma biblioteca completa ainda e mais ergonómica. Também não ha opcao de personalizar o texto da string diretamente.

🔴
Cuidado

No Internet Explorer 11, a API Intl.RelativeTimeFormat não existe. Se você ainda precisa suportar IE11 (raro em 2026, mas acontece em sistemas legados), use um polyfill ou mantenha a biblioteca.

Casos de uso reais

Feed de noticias ou blog: timestamps do tipo 'ha 3 horas' ou 'ontem' em cada card de post. Caso clássico onde o nativo substitui date-fns completamente.

Chat ou notificações: exibir quando foi a última mensagem ('ha 2 min', 'ha 1 semana'). A API lida com todas as unidades necessárias.

Dashboard de dados: mostrar quando foi a última atualização de um gráfico ou métrica ('atualizado ha 5 minutos'). Sem biblioteca, sem overhead.

Apps multilingue: o mesmo código funciona para pt-BR, en-US, es-ES, já-JP e mais de 100 outros locales. Só mude o locale no construtor ou derive do navigator.language.

Dicas e boas práticas

💡
Dica

Reutilize a instância do Intl.RelativeTimeFormat fora dos loops. Criar a instância e mais caro que chamar .format() - faca uma vez e chame múltiplas vezes.

🚀
Pro tip

Use navigator.language no browser (ou o header Accept-Language no servidor) como locale dinâmico. Assim o timestamp aparece no idioma do usuário sem configuração extra.

💡
Dica

Combine com Intl.DateTimeFormat para um tooltip: o texto do card mostra 'ha 2 dias' (relativo), e ao passar o mouse aparece a data completa formatada ('07 de agosto de 2026 as 14h30'). Tudo nativo, zero biblioteca.

Vale a pena?

Se você esta instalando date-fns, dayjs ou qualquer outra biblioteca exclusivamente para formatar timestamps relativos, sim - vale muito a pena migrar para o Intl.RelativeTimeFormat. Você remove uma dependência, reduz o bundle e ganha suporte nativo a internacionalização.

Se a biblioteca já esta no projeto por outros motivos, não ha urgência - mantenha a consistência. A API nativa não substitui tudo que date-fns faz, só a parte de formatação relativa.

O próximo passo e abrir o projeto atual, buscar por formatDistanceToNow, fromNow() ou similares, e avaliar se da para trocar pelo nativo. Na maioria dos casos, da.