Introdução: por que o tempo é uma dor de cabeça para todo desenvolvedor
\n\nVocê já pegou um bug onde um usuário de Vladivostok vê a data de amanhã, embora no calendário seja hoje? Ou quando um servidor em AWS us-east-1 envia um horário que seu cliente em Kaliningrado interpreta como 3 horas da manhã? Bem-vindo ao mundo dos fusos horários em TypeScript.
\n\nO problema é que o JavaScript (e o TypeScript como sua extensão) foi originalmente projetado para navegadores. O objeto nativo Date é, essencialmente, um carimbo de data/hora mascarado em UTC. Ele não sabe trabalhar com fusos horários da maneira que precisamos. E se você está escrevendo um aplicativo sério em TypeScript — seja um painel de administração em React, um backend em Node.js ou um aplicativo móvel em React Native — não conseguirá evitar um trabalho adequado com timezones.
Neste artigo, mostrarei como domar o tempo. Vamos entender os conceitos básicos, aprender a usar bibliotecas modernas e evitar as armadilhas típicas. Vamos lá!
\n\nO que você precisa saber sobre fusos horários antes de escrever código
\n\nAntes de mergulhar no TypeScript, vamos esclarecer as coisas. Existem dois tipos de tempo no mundo: o absoluto (UTC) e o local (o que o usuário vê).
\n\n- \n
- UTC (Tempo Universal Coordenado) — é o padrão. Servidores, bancos de dados e APIs devem armazenar o tempo em UTC. Esta é uma regra de ferro. \n
- Banco de Dados de Fusos Horários IANA — é um dicionário de fusos horários como
Europe/Moscow,Asia/Vladivostok. São essas strings, e não abreviações como MSK ou PST, que devem ser usadas no código. \n - Offset (deslocamento) — é a diferença em relação ao UTC, por exemplo +03:00. Mas lembre-se: o deslocamento pode mudar devido ao horário de verão (DST). Portanto, armazenar o offset em vez da zona IANA é uma má ideia. \n
\n\n\nRegra de ouro: armazenamos em UTC, convertemos no cliente, usamos strings IANA para identificar zonas.
\n
Por que o Date nativo não é suficiente: dor e sofrimento
\n\nVamos encarar a verdade: new Date() em TypeScript é como um canivete suíço com a lâmina quebrada. Ele só sabe armazenar um carimbo de data/hora e exibi-lo no fuso local do navegador ou servidor. Tente fazer algo mais complexo:
// Exemplo ruim: tentando trabalhar com fusos usando meios nativos\nconst date = new Date('2024-03-10T12:00:00');\nconsole.log(date.toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' }));\n// Exibirá: "10/03/2024, 09:00:00" — mas é apenas uma string, não um objeto!\n\n// E se precisar obter um Date em outro fuso?\n// Ops... o JS nativo não sabe criar um Date vinculado a um fuso.\n\n\nO problema é que Date sempre armazena o tempo em UTC. Todos os métodos como getHours() apenas convertem o UTC para o horário local do navegador. Você não pode obter um objeto Date que "pense" que está em Nova York.
É por isso que usamos bibliotecas. No ecossistema TypeScript, existem dois principais candidatos: Luxon e date-fns-tz.
\n\nLuxon: artilharia pesada para trabalhar com datas
\n\nLuxon é uma biblioteca moderna do criador do Moment.js (que já está obsoleto). Ela foi escrita do zero, usa o nativo Intl.DateTimeFormat e é perfeitamente tipada para TypeScript.
Instalação do Luxon
\n\nnpm install luxon\nnpm install --save-dev @types/luxon\n\n\nCriando uma data em um fuso horário específico
\n\nimport { DateTime } from 'luxon';\n\n// Criando uma data no fuso America/Sao_Paulo\nconst saoPauloTime = DateTime.fromObject(\n { year: 2024, month: 3, day: 10, hour: 12 },\n { zone: 'America/Sao_Paulo' }\n);\nconsole.log(saoPauloTime.toISO());\n// Saída: "2024-03-10T12:00:00.000-03:00"\n\n// Convertendo para outro fuso\nconst nyTime = saoPauloTime.setZone('America/New_York');\nconsole.log(nyTime.toISO());\n// Saída: "2024-03-10T11:00:00.00