A desserialização da string JSON para .NET DateTime no fuso horário local adiciona duas horas
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
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.
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
});