Trabalho Avançado com Datas e Horas em C#: das operações básicas às soluções de alto desempenho
\nTrabalhar com datas e horas é uma das tarefas mais comuns e, ao mesmo tempo, traiçoeiras no desenvolvimento. Um erro no fuso horário ou uma formatação incorreta pode levar a bugs graves em produção. Neste artigo, iremos além dos construtores básicos de DateTime e abordaremos técnicas avançadas: DateTimeOffset, trabalho com fusos horários, medições de alta precisão com Stopwatch, além de questões de desempenho e segurança de thread. O material é voltado para desenvolvedores que dominam C# e desejam escrever código confiável para .NET.
1. DateTime, DateTimeOffset e TimeSpan: escolhendo o tipo correto
\nMuitos desenvolvedores usam DateTime por padrão, mas no mundo moderno de sistemas distribuídos, essa muitas vezes não é a melhor escolha. Vamos analisar as principais diferenças.
DateTime vs DateTimeOffset
\nDateTime armazena um momento no tempo sem uma vinculação explícita a um fuso horário. Você pode especificar Kind (Local, Utc, Unspecified), mas isso é apenas um rótulo, não uma informação completa de deslocamento. Já DateTimeOffset sempre armazena o deslocamento do UTC, tornando-o inequívoco e preferível para armazenamento em bancos de dados e APIs.
// DateTime — pode ser enganoso\nDateTime localNow = DateTime.Now; // Kind = Local\nDateTime utcNow = DateTime.UtcNow; // Kind = Utc\n\n// DateTimeOffset — sempre com deslocamento explícito\nDateTimeOffset preciseNow = DateTimeOffset.Now; // +03:00 (exemplo)\nDateTimeOffset utcOffset = DateTimeOffset.UtcNow; // +00:00\n\n// Conversão entre eles\nDateTimeOffset fromDateTime = new DateTimeOffset(2024, 12, 25, 10, 0, 0, TimeSpan.FromHours(3));\nConsole.WriteLine(fromDateTime.Offset); // 03:00:00\n\nRecomendação: Para armazenar um momento no tempo (por exemplo, data de criação de um registro), use DateTimeOffset. Para exibição ao usuário no horário local, converta na camada de apresentação.
TimeSpan para intervalos
\nTimeSpan não armazena uma data, apenas um intervalo de tempo. É uma ferramenta indispensável para aritmética de datas e medição de duração de operações.
DateTimeOffset start = DateTimeOffset.UtcNow;\n// ... algum processo demorado ...\nDateTimeOffset end = DateTimeOffset.UtcNow;\n\nTimeSpan duration = end - start;\nConsole.WriteLine($"A operação levou {duration.TotalMilliseconds} ms");\n// Criando um intervalo\nTimeSpan workDay = new TimeSpan(8, 0, 0); // 8 horas\nDateTimeOffset deadline = DateTimeOffset.UtcNow.Add(workDay);\n\n\n2. Parsing e formatação: cultura, ISO 8601 e desempenho
\nParsing incorreto é uma fonte de bugs em aplicações que trabalham com diferentes configurações regionais. Vamos explorar métodos avançados.
\n\nParseExact e TryParseExact
\nUse ParseExact para strings com um formato conhecido e rigoroso. Isso aumenta a confiabilidade e a legibilidade do código.
string dateString = "2024-12-25T10:30:00+03:00";\n// ISO 8601 — formato universal\nDateTimeOffset parsed = DateTimeOffset.ParseExact(\n dateString,\n "yyyy-MM-dd'T'HH:mm:sszzz",\n CultureInfo.InvariantCulture\n);\nConsole.WriteLine(parsed); // 25.12.2024 10:30:00 +03:00\n\n// Parsing seguro com TryParseExact\nif (DateTimeOffset.TryParseExact(\n "2024/12/25",\n new[] { "yyyy/MM/dd", "dd.MM.yyyy" },\n CultureInfo.InvariantCulture,\n DateTimeStyles.None,\n out DateTimeOffset result))\n{\n Console.WriteLine($"Parsing bem-sucedido: {result}");\n}\n\nDica: Para armazenar datas em logs e APIs, sempre use o formato ISO 8601 (especificador "o"). Isso garante leitura inequívoca por qualquer sistema.
Formatação considerando a cultura
\nAo exibir para o usuário, leve em conta a localidade dele, mas para troca interna, use a cultura invariante.