9

I've been migrating from 2.2 to 3.0 and so far everything has been pretty smooth however when replacing Newtonsoft with the new System.Text.Json I run into the following problem.

When trying to deserialize a DateTime min value with a UTC offset it throws the following exception

Unhandled exception. System.Text.Json.JsonException: The JSON value could not be converted to System.DateTime. Path: $.DateCreated | LineNumber: 2 | BytePositionInLine: 43

Example:

using System;
using Newtonsoft.Json;      

class Program
{
    static void Main(string[] args)
    {
        var json = "{\"MyDateTime\": \"0001-01-01T00:00:00+01:00\"}";
                
        var newtonDeserialized = JsonConvert.DeserializeObject<Class>(json);

        //Bad things happen here.
        var systemDeserialized =  System.Text.Json.JsonSerializer.Deserialize<Class>(json);
        
        Console.WriteLine(newtonDeserialized.MyDateTime);
        Console.WriteLine(systemDeserialized.MyDateTime);
    }
}

class Class
{
    public DateTime MyDateTime {get; set;}
}

Online example can be found here Fiddle.

I guess I could write my own DateTime serializer but there must be an easier/cleaner way to accomplish what Newtonsoft did without any configuring.

This is happening on 3.0.100, 3.1.500, 5.0.100 and 6.0.100

Skami
  • 1,506
  • 1
  • 18
  • 29
  • That's odd. According to [this](https://learn.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support) I would have expected it to just work. – Fildor Sep 30 '19 at 08:02
  • Fun fact: `var json = "{\"MyDateTime\": \"0001-01-01T00:00:00-01:00\"}";` seems to work ... – Fildor Sep 30 '19 at 08:08
  • `var json = "{\"MyDateTime\": \"0001-01-01T05:00:00+01:00\"}";` Also works ... **it's out of range!** – Fildor Sep 30 '19 at 08:09
  • @Fildor I guess I could kindly ask my boss to move to the US ;) – Skami Sep 30 '19 at 08:09
  • @Fildor Yes, I figured it would be out of range but I expected it to return a DateTime.MinValue. – Skami Sep 30 '19 at 08:10
  • 1
    Do you really need **that** Date? I guess it is trying to apply the offset somewhere down the road and then it cannot create "0000-12-31T23:00:00" ... Tricky ... – Fildor Sep 30 '19 at 08:12
  • I guess making them nullable and correcting the data would also solve this issue. However, it still remains something weird to have happen in my opinion. – Skami Sep 30 '19 at 08:14
  • _"I expected it to return a DateTime.MinValue."_ I heard some people suggest to make DateTime and Offset two separate fields ... may work for your requirement if you can make that change. – Fildor Sep 30 '19 at 08:15
  • @Skami unless your boss was alive 2019 years ago, DateTimeOffset is the wrong type for the job. Where did this value come from and what is it supposed to represent? A `Min` value is *not* a missing, unknown or N/A value. If there's no valid value for that field don't emit it or emit null. – Panagiotis Kanavos Sep 30 '19 at 08:37
  • In any case, when dealing with timezones, `DateTime` is *most definitely* the wrong type. The correct type is `DateTimeOffset` and `DateTimeOffset.MinValue` returns `0001-01-01T00:00:00.0000000+00:00`. You may have to replace `DateTime` in your DTOs with `DateTimeOffset` – Panagiotis Kanavos Sep 30 '19 at 08:39
  • 2
    It's interesting that `0001-01-01T00:00:00+00:00` will deserialise correctly, but `0001-01-01T00:00:00+01:00` won't. Doesn't matter if it's `DateTime` or `DateTimeOffset`. – DavidG Sep 30 '19 at 08:53
  • @DavidG well trying to parse it to DateTimeOffset, at least you get a message that makes sense: _"The UTC representation of the date '0001-01-01T00:00:00+01:00' falls outside the year range 1-9999."_ – Fildor Sep 30 '19 at 09:16
  • 3
    I did a bit of poking around and it seems it will eventually get here: https://github.com/dotnet/corefx/blob/027eb8e19c3498cc2c656931c3b879f5ddb63b0d/src/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs#L409-L419 - note the comment saying the year must be 0001 to 9999. This might be a bug because ISO8601 doesn't have that restriction (I think). – Peter Hull Sep 30 '19 at 09:26
  • It's not the range of ISO 8601 that matters, but rather the supported range of `DateTimeOffset` and `DateTime`. – Matt Johnson-Pint Sep 30 '19 at 20:13
  • @Skami I think the easiest way does not exist yet and you have to create your own DateTimeConverter. The good example you can take a look here: https://stackoverflow.com/a/58103218/6699861 – Serhii Matvienko Jan 16 '20 at 13:45
  • 2
    Since `"0001-01-01T00:00:00+01:00"` is actually earlier than `DateTime.MinValue`, one might ask: why can Newtonsoft parse this successfully? Turns out that Json.NET has specific code to clamp the time zone so that the datetime value doesn't fall outside the minimum and maximum -- based on whether the time zone is east or west of UTC. To do this they had to write their own parser; see [`DateTimeUtils.TryParseDateTimeIso()`](https://github.com/JamesNK/Newtonsoft.Json/blob/52190a3a3de6ef9a556583cbcb2381073e7197bc/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs#L214). – dbc Dec 24 '21 at 21:42

0 Answers0