O tempo é relativo. Especialmente no código
Imagine: você está escrevendo um aplicativo para reserva de passagens aéreas. Um usuário de Nova York escolhe um voo às 14:00, horário local, e o servidor em Londres salva isso como DateTime.Now. O que pode dar errado? Absolutamente tudo. No melhor dos casos — um bilhete três horas antes, no pior — o usuário perde o voo e deixa uma avaliação furiosa.
Trabalhar com fusos horários em C# é aquele tópico que muitos desenvolvedores deixam para depois. Mas é exatamente isso que separa o código amador do profissional. Hoje vamos entender como fazer seu aplicativo se dar bem com os fusos horários, não enlouquecer com o horário de verão e não perder nenhum usuário.
DateTime vs DateTimeOffset: o primeiro confronto com a realidade
Muitos iniciantes em C# usam DateTime para tudo. E isso é normal, até você se deparar com usuários globais. O problema é que DateTime por si só não sabe em qual fuso horário está. É apenas um "momento no tempo" sem contexto.
// Ruim: tempo sem contextoDateTime localTime = DateTime.Now; // 14:00 — mas onde?DateTime utcTime = DateTime.UtcNow; // já é melhor, mas ainda não é ideal
// Bom: tempo com offsetDateTimeOffset withOffset = DateTimeOffset.Now; // 14:00 +03:00Console.WriteLine(withOffset); // 06.04.2025 14:00:00 +03:00DateTimeOffset é seu primeiro passo para o gerenciamento consciente do tempo. Ele armazena não apenas a data e hora, mas também o offset em relação ao UTC. Isso salva quando você precisa transmitir dados entre sistemas ou simplesmente mostrar ao usuário a hora local correta.
Quando DateTime ainda é necessário?
Sinceramente? Quase nunca para armazenar momentos no tempo. Se você está escrevendo código para um aplicativo desktop local — pode usar. Mas assim que aparecer um banco de dados, API ou vários usuários — migre para DateTimeOffset ou DateTime com a indicação explícita de DateTimeKind.Utc.
TimeZoneInfo: seu atlas pessoal de fusos horários
No .NET existe a poderosa classe TimeZoneInfo. Ela permite trabalhar com qualquer fuso horário conhecido pelo sistema operacional. E existem centenas deles — desde os padrões UTC+0 até zonas exóticas como "Lord Howe Island" com offset de 30 minutos.
// Obtém a lista de todas as zonas disponíveisReadOnlyCollection<TimeZoneInfo> allZones = TimeZoneInfo.GetSystemTimeZones();foreach (var zone in allZones.Take(5)){ Console.WriteLine($"{zone.Id}: {zone.DisplayName}");}// Saída (exemplo):// UTC: (UTC) Coordinated Universal Time// Russian Standard Time: (UTC+03:00) Moscow, St. Petersburg// Eastern Standard Time: (UTC-05:00) Eastern Time (US & Canada)Conversão de horário entre fusos
Suponha que um usuário de Moscou queira saber a que horas começará um webinar em Londres. Veja como fazer isso corretamente:
// Horário original — de MoscouDateTime moscowTime = new DateTime(2025, 6, 15, 18, 0, 0);TimeZoneInfo moscowZone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
// ConversãoDateTime londonTime = TimeZoneInfo.ConvertTime(moscowTime, moscowZone, londonZone);Console.WriteLine($"Em Moscou: {moscowTime}, em Londres: {londonTime}");// Em Moscou: 15.06.2025 18:00:00, em Londres: 15.06.2025 16:00:00Observe: indicamos explicitamente a zona de origem e a de destino. Isso elimina ambiguidades. O TimeZoneInfo.ConvertTime leva em conta automaticamente a diferença de horário, incluindo horário de verão/inverno.
Horário de verão (DST): o maior inimigo do desenvolvedor
Duas vezes por ano o mundo enlouquece. Em março os relógios são adiantados, em outubro — atrasados. E se seu aplicativo não estiver preparado para o DST, os usuários verão uma hora "extra" ou uma hora perdida. O C# lida com isso automaticamente