Your problem is that your timestamp value, "2017-11-05T08:30:00", is completely lacking a time zone designator, either "2017-11-05T08:30:00Z" for UTC or "2017-11-05T08:30:00-07:00" for your local time zone. I.e. your statement that your timestamp is in UTC format is false.
When this happens, Json.NET parses the value into a DateTime
with DateTime.Kind == DateTimeKind.Unspecified
(which makes sense, since the time zone is, in fact, unspecified in the JSON). Later on, some other code interpreted that to mean local time but since daylight savings time did in fact expire around the time you asked this question in the location (Washington, DC, United States) listed in your profile, things became confused, since DateTime
doesn't remember its time zone offset, only whether or not it is in the machine's local time zone. (For more on this limitation see this documentation page as well as What's wrong with DateTime anyway? by Jon Skeet which specifically mentions that certain bugs with the .Net date and time types can occur only during daylight savings changes.) If daylight savings expired between when the timestamp was deserialized and when it was subsequently processed, you would get exactly the bug you are seeing.
That being said, you asked, And how can I fix that on deserialization level, so every instance could be properly converted to valid UTC DateTime instance? This can be done by setting JsonSerializerSettings.DateTimeZoneHandling == DateTimeZoneHandling.Utc
during deserialization:
var settings = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
};
var root = JsonConvert.DeserializeObject<RootObject>(json, settings);
The possible values of DateTimeZoneHandling
are explained here:
Local: Treat as local time. If the DateTime object represents a Coordinated Universal Time (UTC), it is converted to the local time.
Utc: Treat as a UTC. If the DateTime object represents a local time, it is converted to a UTC.
Unspecified: Treat as a local time if a DateTime is being converted to a string. If a string is being converted to DateTime,
convert to a local time if a time zone is specified.
RoundtripKind: Time zone information should be preserved when converting.
By using DateTimeZoneHandling.Utc
the timestamp will be assumed to be in UTC right from the beginning, so odd behaviors due to daylight savings changes should not occur.
You might also consider fixing the server code so that it properly appends a Z to its timestamp value to indicate the value is, in fact, in UTC.
Sample fiddle showing all four settings in action.