La désérialisation de la chaîne JSON en .NET DateTime dans le fuseau horaire local ajoute deux heures
Lorsque je désérialise cette chaîne json "2020-10-05T07: 29: 00 + 00: 00" sur mon ordinateur local en un objet DateTime, elle se termine par 2020-10-05 09:29, où elle devrait être 07:29. La date est spécifiée comme locale (+00: 00) donc je ne comprends pas d'où viennent les deux heures supplémentaires. J'ai essayé de regarder cette question La désérialisation du fuseau horaire Datetime, mais la conversion de la date à l'aide de .ToLocalTime () ne fait rien.
Le contexte est une grande chaîne json avec des aéroports à partir d'une API externe avec de nombreuses heures de données, certaines sont en UTC, d'autres en heures locales. J'ai besoin de trouver le moyen le plus simple de désérialiser la chaîne qui produira les dates-heures correctes.
Notez que l'heure locale peut être n'importe où dans le monde, elle ne doit donc pas dépendre du serveur qui exécute l'application.
Voici un exemple de code utilisant NewtonSoft.Json pour désérialiser:
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; }
}
Voici ma sortie dans mon fuseau horaire (UTC +2 DaylightSavingTime):
UTC: 05-10-2020 05:29:00 (Utc). LOC: 05-10-2020 09:29:00 (Local) La conversion a échoué
Voici un violon dotnet https://dotnetfiddle.net/uHLdAh Cela produit la sortie correcte puisque le serveur exécute probablement GMT.
Réponses
Le problème est que "2020-10-05T07: 29: 00 + 00: 00" n'est pas l'heure locale, il est interprété comme une heure UTC avec un décalage de 0 heure. Et, lorsque vous l'exécutez sur votre machine, il ajoute le décalage de votre fuseau horaire actuel, c'est-à-dire +02: 00h.
Par exemple, il est maintenant 08h36 et mon fuseau horaire est CET. Si je devais créer un objet DateTimeOffset en UTC, cela dirait 2020-11-27T06: 36: 00 + 02: 00.
Ainsi, l'exécution du code suivant donne des résultats différents en fonction du fuseau horaire de la machine en cours d'exécution:
// Current time
dto = DateTimeOffset.Now;
Console.WriteLine(dto.LocalDateTime);
// UTC time
dto = DateTimeOffset.UtcNow;
Console.WriteLine(dto.LocalDateTime);
Sortie lors de l'exécution sur dotnetfiddle.net:
3/11/2007 10:30:00 AM
3/11/2007 9:30:00 AM
Sortie lors de l'exécution sur ma machine locale:
2020-11-27 08:54:09
2020-11-27 08:54:09
Vous devez déterminer si votre entrée est donnée en heure UTC avec un décalage correct de 0, ou si elle est en heure "locale", avec un décalage défectueux. Ensuite, vous devez créer un objet DateTimeOffset spécifiant le fuseau horaire que le nouvel objet datetime doit avoir.
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);
Ce qui donne la sortie suivante:
2020-10-05 07:29:00 +02:00
Local
2020-10-05 05:29:00
Utc
Vous avez maintenant un objet DateTimeOffset se comportant comme prévu.
La solution au problème s'est avérée simple. Supposons simplement que toutes les heures sont UTC. Cela résout tous les problèmes de conversion.
var foo = JsonConvert.DeserializeObject<CustomTime>(json, new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});