La deserialización de la cadena JSON a .NET DateTime en la zona horaria local agrega dos horas

Nov 27 2020

Cuando deserializo esta cadena json "2020-10-05T07: 29: 00 + 00: 00" en mi PC local a un objeto DateTime, termina como 2020-10-05 09:29, donde debería ser 07:29. La fecha se especifica como local (+00: 00), así que no entiendo de dónde vienen las dos horas adicionales. Intenté mirar esta pregunta Deserialización de la zona horaria de fecha y hora, pero convertir la fecha usando .ToLocalTime () no hace nada.

El contexto es una cadena json grande con aeropuertos de una API externa con muchas fechas y horas, algunas son UTC, otras son horas locales. Necesito encontrar la forma más sencilla de deserializar la cadena que producirá las fechas y horas correctas.

Tenga en cuenta que la hora local puede ser en cualquier parte del mundo, por lo que no debería depender del servidor que ejecuta la aplicación.

Aquí hay un ejemplo de código usando NewtonSoft.Json para deserializar:

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 es mi salida en mi zona horaria (UTC +2 DaylightSavingTime):

UTC: 10/05/2020 05:29:00 (Utc). LOC: 05-10-2020 09:29:00 (Local) Error de conversión

Aquí hay un violín de dotnet https://dotnetfiddle.net/uHLdAh Esto produce la salida correcta ya que el servidor probablemente ejecuta GMT.

Respuestas

1 iikkoo Nov 27 2020 at 14:56

El problema es que "2020-10-05T07: 29: 00 + 00: 00" no es la hora local, se interpreta como una hora UTC con 0 horas de compensación. Y, cuando lo ejecuta en su máquina, agrega el desplazamiento de su zona horaria actual, es decir, +02: 00h.

Por ejemplo, ahora son las 08:36 y mi zona horaria es CET. Si tuviera que crear un objeto DateTimeOffset en UTC, diría 2020-11-27T06: 36: 00 + 02: 00.

Por lo tanto, ejecutar el siguiente código produce resultados diferentes según la zona horaria de la máquina de ejecución:

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

Salida cuando se ejecuta en dotnetfiddle.net:

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

Salida cuando se ejecuta en mi máquina local:

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

Debe determinar si su entrada se da en hora UTC con una compensación adecuada de 0, o si está en la hora "local", con una compensación defectuosa. Entonces debería tener que crear un objeto DateTimeOffset especificando la zona horaria que debería tener el nuevo objeto de fecha y hora.

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);

Lo que produce el siguiente resultado:

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

Ahora tiene un objeto DateTimeOffset que se comporta como se esperaba.

MRaymaker Nov 27 2020 at 19:41

La solución al problema resultó ser sencilla. Simplemente asuma que todas las horas son UTC. Esto soluciona todos los problemas de la conversión.

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