← Todos os artigos

Formatação de datas correta por fuso no JavaScript sem bibliotecas

O Intl.DateTimeFormat integrado do JavaScript trata a formatação de fusos IANA sem moment.js ou date-fns. Aprenda as opções timeZone explícitas, a exibição segura quanto ao horário de verão, formatToParts, os limites da conversão de relógio e quando o Temporal ou uma biblioteca de fusos ainda é útil.

Por que Date não tem propriedade de fuso

Um Date do JavaScript é sempre uma contagem de milissegundos UTC. Não há fuso armazenado dentro do objeto. Quando você chama .toString() ou .toLocaleString() sem opções, o JavaScript usa o fuso local do runtime a partir do sistema operacional. O mesmo código produz saídas diferentes em um servidor em Nova York e em um notebook em Tóquio, mesmo que o timestamp subjacente seja idêntico.

Formatar com Intl.DateTimeFormat

Intl.DateTimeFormat é a API integrada para formatação ciente de localidade e fuso. Ela aceita identificadores de fuso IANA, trata as transições de horário de verão automaticamente e está disponível em navegadores modernos e no Node.js. O segredo é passar a opção timeZone explicitamente em vez de confiar no padrão do runtime.

  • new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
  • date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
  • date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
  • new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)

Extrair partes individuais com formatToParts

Use formatToParts() para obter os componentes individuais da data como objetos {type, value} e montar strings de data personalizadas. Isso é melhor do que dividir uma string de data localizada porque pontuação, ordem e escritas diferem por localidade.

  • const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
  • parts.find(p => p.type === 'year').value → '2023'
  • parts.find(p => p.type === 'month').value → '11'
  • Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }

Converter hora de relógio em UTC (o sentido difícil)

Ir de uma hora de relógio em um fuso dado para UTC é mais difícil com a API Date clássica. Formatar um instante em America/New_York é fácil; construir o instante representado por 2026-03-08 02:30 em America/New_York não é, pois essa hora local pode ser pulada ou repetida durante as transições de horário de verão.

  • O Temporal chegou ao Estágio 4 em 2026, mas o suporte nativo dos navegadores ainda não é universal
  • Para uso em produção em todos os navegadores hoje, o polyfill do Temporal ou date-fns-tz toDate() ainda são escolhas práticas
  • Evite aritmética manual de offset UTC — as transições de horário de verão ocorrem em horas locais diferentes em anos diferentes
  • O zonedToEpochMs() deste site usa correção de offset de uma iteração — veja src/timeUtils.ts

Escolher o identificador de fuso correto

Use nomes de fuso IANA como America/New_York, Europe/London, Asia/Tokyo ou UTC. Evite offsets fixos como UTC-5 para a hora voltada ao usuário porque os offsets mudam com o horário de verão. America/New_York pode ser UTC-5 em janeiro e UTC-4 em julho; o nome IANA permite à plataforma aplicar a regra histórica correta para a data selecionada.

  • Bom: America/Los_Angeles — inclui regras de horário de verão históricas e futuras
  • Bom: Europe/Berlin — trata o horário de verão da Europa Central automaticamente
  • Bom: Asia/Shanghai — exibição estável UTC+8 sem horário de verão
  • Use UTC para logs, armazenamento de API, timestamps de banco e comparação entre regiões
  • Use o fuso IANA do usuário para a exibição final na interface do produto

FAQ sobre formatação de fusos

Two daylight-saving transitions a year break the assumption that "wall-clock time + timezone = a unique moment". Both are encoded in the IANA timezone database (still often called the Olson database) — the authoritative source of timezone rules used by every modern OS, browser, and language runtime.

  • DST gap: 02:00 to 02:59 local time does not exist on the spring-forward day
  • DST overlap: 01:00 to 01:59 local time occurs twice on the fall-back day
  • Ambiguous local time: in the overlap window you must pick "earlier" or "later" explicitly
  • Python uses a fold flag; JS Temporal uses a disambiguation option; java.time defaults to the earlier offset
  • Olson database / IANA tzdata / zoneinfo are three names for the same dataset, updated several times a year
  • Each IANA name (America/Los_Angeles, Europe/Berlin) maps to a list of (transition, offset, abbreviation) tuples
  • Stale tzdata on a device produces silently-wrong local times after any DST or zone change
  • Server best practice: do all date arithmetic in UTC, convert to local only at display

FAQ

Can JavaScript format a date in another timezone without a library?
Yes. Use Intl.DateTimeFormat or toLocaleString with a timeZone option, such as America/New_York or Asia/Tokyo.
Does Intl.DateTimeFormat handle daylight saving time?
Yes. When you use an IANA timezone identifier, the runtime applies the correct offset for that timezone and date.
Should I store timezone offsets or timezone names?
Store UTC for event instants. If you need the user's local context, store an IANA timezone name such as America/New_York, not only a numeric offset.
How do I get the user's timezone in JavaScript?
Use Intl.DateTimeFormat().resolvedOptions().timeZone, which returns an IANA name like America/New_York. Store that name, not a numeric offset, when you need to reconstruct the user's local time later.
What is the DST gap (spring-forward)?
The DST gap is the wall-clock hour that is skipped when daylight saving time begins. In most US zones, when the clock would tick from 02:00 to 02:59 it instead jumps to 03:00. Local times in that window do not exist on the transition day. Unix timestamps themselves never skip; only the wall-clock interpretation does.
What is the DST overlap (fall-back)?
The DST overlap is the wall-clock hour that occurs twice when daylight saving time ends. In most US zones, the clock ticks from 01:59 back to 01:00, so every minute from 01:00 to 01:59 happens twice. A wall-clock time in that window maps to two Unix timestamps; converters and schedulers must specify "earlier" or "later".