Json.NET al deserializar el valor de DateTimeOffset falla para DateTimeOffset.MinValue sin zona horaria

Json.NET al deserializar el valor de DateTimeOffset falla para DateTimeOffset.MinValue sin zona horaria

El problema parece reproducible solo cuando la zona horaria de la máquina TimeZoneInfo.Local tiene un desplazamiento positivo de UTC, p. (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna . No pude reproducirlo en zonas horarias con un desplazamiento no positivo como UTC-05:00 o UTC mismo.

En concreto, en JsonReader.ReadDateTimeOffsetString() se hace una llamada a DateTimeOffset.TryParse usando DateTimeStyles.RoundtripKind :

if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
{
    SetToken(JsonToken.Date, dt, false);
    return dt;
}

Aparentemente, esto provoca un error de subdesbordamiento en zonas horarias con un desplazamiento UTC positivo. Si en el depurador analizo usando DateTimeStyles.AssumeUniversal en cambio, se evita el problema.

Es posible que desee informar un problema sobre esto a Newtonsoft. El hecho de que la deserialización de un DateTimeOffset específico La cadena falla solo cuando la zona horaria de la computadora tiene ciertos valores que parecen incorrectos.

La solución alternativa es usar IsoDateTimeConverter para deserializar su DateTimeOffset propiedades con IsoDateTimeConverter.DateTimeStyles establecido en DateTimeStyles.AssumeUniversal . Además es necesario deshabilitar el DateTime automático reconocimiento integrado en JsonReader configurando JsonReader.DateParseHandling = DateParseHandling.None , que debe hacerse antes el lector comienza a analizar el valor de su DateTimeOffset propiedades.

Primero, defina lo siguiente JsonConverter :

public class FixedIsoDateTimeOffsetConverter : IsoDateTimeConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?);
    }

    public FixedIsoDateTimeOffsetConverter() : base() 
    {
        this.DateTimeStyles = DateTimeStyles.AssumeUniversal;
    }
}

Ahora, si puedes modificar el JsonSerializerSettings para su controlador, use la siguiente configuración:

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters = { new FixedIsoDateTimeOffsetConverter() },
};

Si no puede modificar fácilmente el JsonSerializerSettings de su controlador tendrás que agarrar DateParseHandlingConverter de esta respuesta a Cómo evitar que una propiedad de un solo objeto se convierta en DateTime cuando es una cadena y aplicarlo así como FixedIsoDateTimeOffsetConverter a su modelo de la siguiente manera:

[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
public class RootObject
{
    [JsonProperty("revisedDate", NullValueHandling = NullValueHandling.Ignore)]
    [JsonConverter(typeof(FixedIsoDateTimeOffsetConverter))]
    public DateTimeOffset? RevisedDate { get; set; }
}

DateParseHandlingConverter debe aplicarse al modelo en sí en lugar del RevisedDate propiedad porque el JsonReader ya habrá reconocido 0001-01-01T00:00:00 como un DateTime antes de la llamada a FixedIsoDateTimeOffsetConverter.ReadJson() está hecho.

Actualizar

En los comentarios, @RenéSchindhelm escribe:Creé un problema para informar a Newtonsoft . Es La deserialización del valor de DateTimeOffset falla dependiendo de la zona horaria del sistema #1731 .


Esto es lo que estoy usando para solucionar el problema en .NET Core 3.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
                options.SerializerSettings.DateParseHandling = DateParseHandling.None;
                options.SerializerSettings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal });
            });
...

Cambiar DateTimeOffset a DateTime resuelve el problema.