Deserializacja ciągu JSON do .NET DateTime w lokalnej strefie czasowej powoduje dodanie dwóch godzin

Nov 27 2020

Kiedy deserializuję ten ciąg json „2020-10-05T07: 29: 00 + 00: 00” na moim lokalnym komputerze do obiektu DateTime, kończy się jako 2020-10-05 09:29, gdzie powinno być 07:29. Data jest określona jako lokalna (+00: 00), więc nie rozumiem, skąd pochodzą dodatkowe dwie godziny. Próbowałem spojrzeć na to pytanie Deserializacja strefy czasowej datetime, ale konwersja daty za pomocą .ToLocalTime () nic nie robi.

Kontekstem jest duży ciąg json z lotniskami z zewnętrznego interfejsu API z wieloma datami, niektóre są czasem UTC, niektóre są czasami lokalnymi. Muszę znaleźć najprostszy sposób na deserializację ciągu, który zapewni prawidłowe czasy danych.

Należy pamiętać, że czas lokalny może znajdować się w dowolnym miejscu na świecie, więc nie powinien zależeć od serwera, na którym działa aplikacja.

Oto przykład kodu wykorzystujący NewtonSoft.Json do deserializacji:

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

To jest mój wynik w mojej strefie czasowej (UTC +2 DaylightSavingTime):

UTC: 05-10-2020 05:29:00 (Utc). LOC: 05-10-2020 09:29:00 (lokalnie) Konwersja nie powiodła się

Oto skrzypce dotnetowe https://dotnetfiddle.net/uHLdAh Daje to prawidłowe dane wyjściowe, ponieważ serwer prawdopodobnie działa w trybie GMT.

Odpowiedzi

1 iikkoo Nov 27 2020 at 14:56

Problem polega na tym, że „2020-10-05T07: 29: 00 + 00: 00” nie jest czasem lokalnym, jest interpretowany jako czas UTC z przesunięciem 0 godzin. A kiedy uruchomisz go na swoim komputerze, doda przesunięcie twojej bieżącej strefy czasowej, tj. +02: 00h.

Na przykład, jest teraz 08:36, a moja strefa czasowa to CET. Gdybym miał utworzyć obiekt DateTimeOffset w UTC, powiedziałbym, że 2020-11-27T06: 36: 00 + 02: 00.

Tak więc uruchomienie następującego kodu daje różne wyniki w zależności od strefy czasowej maszyny wykonującej:

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

Dane wyjściowe podczas uruchamiania na dotnetfiddle.net:

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

Dane wyjściowe podczas pracy na moim komputerze lokalnym:

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

Musisz określić, czy dane wejściowe są podane w czasie UTC z odpowiednim przesunięciem równym 0, czy też w czasie „lokalnym” z błędnym przesunięciem. Następnie należy utworzyć obiekt DateTimeOffset określający strefę czasową, którą powinien mieć nowy obiekt daty i godziny.

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

Co daje następujący wynik:

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

Masz teraz obiekt DateTimeOffset zachowujący się zgodnie z oczekiwaniami.

MRaymaker Nov 27 2020 at 19:41

Rozwiązanie problemu okazało się proste. Po prostu załóżmy, że wszystkie czasy to UTC. To rozwiązuje wszystkie problemy z konwersją.

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