Need to clear up some confusion on a problem I though we had resolved months/years ago.
(currently using 4.0.42)
In client code using our sub-classed JsonServiceClient, it appears that deserialized DateTime properties in our DTO’s are by default DateTimeKind.Local, even though have the following JsConfig:
AssumeUtc = true;
AlwaysUseUtc = true;
DateHandler = DateHandler.ISO8601;
The values of the DateTimes that are being Deserialized are in fact correct, the problem is that the Kind property is DateTimeKind.Local instead of DateTimeKind.Utc.
So I have to call ToUniversalTime(), to make the DateTime actually a UTC dateTime.
Is this a recent change?
Do I have to apply my own Serialization/Deserialization functions to yield DateTime’s that are by default DateTimeKind.Utc?
Like this?
JsConfig.SerializeFn = time =>
{
return new DateTime(time.Ticks, DateTimeKind.Utc).ToString(“o”);
};
JsConfig.DeSerializeFn = time =>
{
return DateTime.ParseExact(time, “o”, null).ToUniversalTime();
};
The default behavior is DateTime’s are serialized and sent as UTC but deserialized back into local time.
There’s no recent change that I can recall, but can you try with the latest v4.0.46 to check if it’s still an issue.
Is the JsConfig on the client specified at the start of the Application?
Yes JsConfig is applied (albeit within a JSConfig.BeginScope) to all instances of our JsonServiceClient when it is instantiated in ctor.
OK, might be first time I am seeing this. Although that surprises the heck out of me, given we hadn’t noticed this in the last 2 years of using the client.
But knowing that all datetimes are serialized to DateTimeKind.Local is a real good gem.
(Now I am quietly cautious of what the side effects might be of the proposed change below??
OK, so lets say we decide to de-serialise DateTime(s) so that they are all DateTimeKind.Utc.
Do I do that by adding this in my JsConfig scope:
JsConfig.SerializeFn = time =>
{
return new DateTime(time.Ticks, DateTimeKind.Utc).ToString(“o”);
};
JsConfig.DeSerializeFn = time =>
{
return DateTime.ParseExact(time, “o”, null).ToUniversalTime();
};
And should we have both serialisation and deserialization handlers here? Or can we omit the serialization one?
If I have this code defined in the ctor of our sub-classed JsonServiceClient class:
var scope = JsConfig.BeginScope();
scope.AssumeUtc = true;
scope.AlwaysUseUtc = true;
scope.DateHandler = DateHandler.ISO8601;
Then deserialized DateTime instances of any DTO’s are going to have Kind = DateTimeKind.Utc correct?
I wouldn’t create an open scope in a constructor, they should in a using{}, scoped around and limited to where it’s used like how we do it in StripeGateway.
OK great, so given we have a sub-classed version of JsonServiceClient.
If we wanted to ensure that all outbound requests used our JSScope do I wrap the scope around the only this single method:
Obviously that will depend on if you’re only using the API’s that use that method, there’s a number that don’t, e.g. Async API’s, SendOneWay, SendAll, PostFile, etc. Check the ServiceClientBase source code to find out which methods do use Send<T>.