A desserialização da string JSON para .NET DateTime no fuso horário local adiciona duas horas

Nov 27 2020

Quando eu desserializo esta string json "2020-10-05T07: 29: 00 + 00: 00" no meu pc local para um objeto DateTime, ela termina como 2020-10-05 09:29, onde deveria ser 07:29. A data é especificada como local (+00: 00), então não entendo de onde vêm as duas horas extras. Eu tentei olhar para esta questão a desserialização de fuso horário de data e hora, mas converter a data usando .ToLocalTime () não adiantou.

O contexto é uma grande string json com aeroportos de uma API externa com muitos datetimes, alguns são UTC, outros são horários locais. Preciso encontrar a maneira mais simples de desserializar a string que produzirá os datetimes corretos.

Observe que a hora local pode ser em qualquer lugar do mundo, portanto, não deve depender do servidor que executa o aplicativo.

Aqui está um exemplo de código usando NewtonSoft.Json para desserializar:

static void Main(string[] args)
    {
        var json =
            "{\"UTC\": \"2020-10-05T05:29:00Z\",\"Local\": \"2020-10-05T07:29:00+00:00\" }";
        var expected = new DateTime(2020,10,5,7,29,0);
        var foo = JsonConvert.DeserializeObject<CustomTime>(json);
        Console.WriteLine($"UTC:{foo.UTC} ({foo.UTC.Kind}).\r\nLOC:{foo.Local} ({foo.Local.Kind})");
        System.Console.WriteLine(foo.Local.Equals(expected) ? "All good" : "Conversion failed");
    }

public sealed class CustomTime
{
    public DateTime UTC { get; set; }
    public DateTime Local { get; set; }
}

Esta é minha saída no meu fuso horário (UTC +2 DaylightSavingTime):

UTC: 05-10-2020 05:29:00 (Utc). LOC: 05-10-2020 09:29:00 (Local) A conversão falhou

Aqui está um violino dotnet https://dotnetfiddle.net/uHLdAh Isso produz a saída correta, pois o servidor provavelmente executa GMT.

Respostas

1 iikkoo Nov 27 2020 at 14:56

O problema é que "2020-10-05T07: 29: 00 + 00: 00" não é a hora local, ela é interpretada como uma hora UTC com 0 horas de diferença. E, quando você o executa em sua máquina, ele adiciona o deslocamento de seu fuso horário atual, ou seja, +02: 00h.

Por exemplo, agora é 08:36 e meu fuso horário é CET. Se eu criasse um objeto DateTimeOffset em UTC, ele diria 2020-11-27T06: 36: 00 + 02: 00.

Portanto, executar o código a seguir produz resultados diferentes, dependendo do fuso horário da máquina em execução:

 // Current time
 dto = DateTimeOffset.Now;
 Console.WriteLine(dto.LocalDateTime);
 // UTC time
 dto = DateTimeOffset.UtcNow;
 Console.WriteLine(dto.LocalDateTime);

Resultado quando executado em dotnetfiddle.net:

3/11/2007 10:30:00 AM
3/11/2007 9:30:00 AM

Saída ao executar em minha máquina local:

2020-11-27 08:54:09
2020-11-27 08:54:09

Você deve determinar se a sua entrada é fornecida no horário UTC com um deslocamento adequado de 0, ou se é no horário "local", com um deslocamento defeituoso. Em seguida, você deve criar um objeto DateTimeOffset especificando o fuso horário que o novo objeto datetime deve ter.

var dto = new DateTimeOffset(2020,10,5,7,29,0, new TimeSpan(2, 0, 0));
Console.WriteLine(dto);
Console.WriteLine(dto.LocalDateTime.Kind);
Console.WriteLine(dto.UtcDateTime);
Console.WriteLine(dto.UtcDateTime.Kind);

O que produz a seguinte saída:

2020-10-05 07:29:00 +02:00
Local
2020-10-05 05:29:00
Utc

Agora você tem um objeto DateTimeOffset se comportando conforme o esperado.

MRaymaker Nov 27 2020 at 19:41

A solução para o problema provou ser simples. Suponha que todos os horários são UTC. Isso corrige todos os problemas na conversão.

var foo = JsonConvert.DeserializeObject<CustomTime>(json, new JsonSerializerSettings
{
   DateTimeZoneHandling = DateTimeZoneHandling.Utc
});