Minustar
Minustar

Reputation: 1225

Parse String as UTC DateTimeOffset When No TZ Specified

I am developing a simple web API client. On of my models has a ExpiresOn property that I have specified as a DateTimeOffset.

public class Model {
    public DateTimeOffset ExpiresOn { get; set; }
}

I deserialise the API reponse from a JSON string using JsonConvert.DeserializeObject<T>(). I have noticed that since the JSON I retrieve doesn't specify any time zone information, JSON.Net will assume it's in local time and return me a DateTimeOffset adjusted to UTC, as per my settings. I have tried different combinations but I cannot seem to 'force' JsonConvert to read the string as if it were UTC.

Preferably, there would be an option to set that setting globally using JsonConvert.DefaultSettings as I have similar parsing to be done in several locations.

Also, I am coding for ASP.Net Core 2.1, and my host's tz is set to CEST (+02:00). Setting a different time zone on the host is not an options.

Addendum:

An example of string to parse would be "2018-07-09T11:22:33.45678". I've tried permutations of DateParseHandling and DateTimeZoneHandling but all return a date: 2018-07-09T11:22:33.45768+02:00.

Obviously the deserialiser assumes that the string is in local time. Hence, is there a way to have it treat it as AssumeUniversal would in DateTimeOffset.TryParse?

Upvotes: 2

Views: 1529

Answers (2)

Freggar
Freggar

Reputation: 1066

It seems like DateTimeZoneHandling doesn't work for DateTimeOffset.

As a workaround you would have to define a JsonConverter that circumvents this problem:

public class DateTimeOffsetConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTimeOffset);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) return null;
        DateTime dateTime = (DateTime)reader.Value;
        return new DateTimeOffset(dateTime);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value);
    }
}

Then use it like this:

public class Model
{
    [JsonConverter(typeof(DateTimeOffsetConverter))]
    public DateTimeOffset ExpiresOn { get; set; }
}

And then call it like this:

var str = "{\"ExpiresOn\":\"2018-07-09T11:22:33.45678\"}";
Model test = JsonConvert.DeserializeObject<Model>(str, new JsonSerializerSettings()
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    DateParseHandling = DateParseHandling.DateTime
});
Assert(test.ExpiresOn.Offset.Ticks == 0);

You can also define the JsonSerializerSettings in JsonConvert.DefaultSettings instead of every conversion call.

Upvotes: 1

Mihir Dave
Mihir Dave

Reputation: 4024

According to the documentation, you can pass DateTimeZoneHandling through JsonSerializerSettings

Here is the example for that though example here is for serializing but you can pass same settings for deserializing also have a look at this

Example:-

string jsonWithUtcTimeZone = JsonConvert.SerializeObject(flight, Formatting.Indented, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc
});

Deserialization Example:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
};

var json = JsonConvert.SerializeObject(a, settings);

var newA = JsonConvert.DeserializeObject<A>(json, settings);

Upvotes: 0

Related Questions