I have a date time in the database and I retrieve it from the database using Entity Framework, I then pass out the data via JSON API through the DataContractJsonSerializer.
The time in the date time field appears to have been adjusted according to the local timezone of the server whilst being processed in DataContractJsonSerializer. The epoch expressed time is 1 hour ahead of the time expected. The DateTime Kind is UTC, but previously it was Unspecified and I had the same issue.
In my application, I wish to convert between timezones explicitly and on the client side, not the server, as this makes more sense. I'm surprised at this implicit functionality as my datetime values should be simple values just like an integer.
thanks
DataContractJsonSerializer
will output the timezone portion (+zzzz) if your DateTime.Kind is equal to Local OR Unspecified. This behaviour differs from the XmlSerializer which only outputs the timezone portion if Kind equals Unspecified.
If curious check out the source for JsonWriterDelegator
which contains the following method:
internal override void WriteDateTime(DateTime value)
{
// ToUniversalTime() truncates dates to DateTime.MaxValue or DateTime.MinValue instead of throwing
// This will break round-tripping of these dates (see bug 9690 in CSD Developer Framework)
if (value.Kind != DateTimeKind.Utc)
{
long tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks;
if ((tickCount > DateTime.MaxValue.Ticks) || (tickCount < DateTime.MinValue.Ticks))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.JsonDateTimeOutOfRange), new ArgumentOutOfRangeException("value")));
}
}
writer.WriteString(JsonGlobals.DateTimeStartGuardReader);
writer.WriteValue((value.ToUniversalTime().Ticks - JsonGlobals.unixEpochTicks) / 10000);
switch (value.Kind)
{
case DateTimeKind.Unspecified:
case DateTimeKind.Local:
// +"zzzz";
TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime());
if (ts.Ticks < 0)
{
writer.WriteString("-");
}
else
{
writer.WriteString("+");
}
int hours = Math.Abs(ts.Hours);
writer.WriteString((hours < 10) ? "0" + hours : hours.ToString(CultureInfo.InvariantCulture));
int minutes = Math.Abs(ts.Minutes);
writer.WriteString((minutes < 10) ? "0" + minutes : minutes.ToString(CultureInfo.InvariantCulture));
break;
case DateTimeKind.Utc:
break;
}
writer.WriteString(JsonGlobals.DateTimeEndGuardReader);
}
I've run the following test on my machine
var jsonSerializer = new DataContractJsonSerializer(typeof(DateTime));
var date = DateTime.UtcNow;
Console.WriteLine("original date = " + date.ToString("s"));
using (var stream = new MemoryStream())
{
jsonSerializer.WriteObject(stream, date);
stream.Position = 0;
var deserializedDate = (DateTime)jsonSerializer.ReadObject(stream);
Console.WriteLine("deserialized date = " + deserializedDate.ToString("s"));
}
which produces the expected output:
original date = 2011-04-19T10:24:39
deserialized date = 2011-04-19T10:24:39
Thus at some point your Date must be Unspecified or Local.
After pulling it out of the DB convert the kind from Unspecified to Utc by calling
entity.Date = DateTime.SpecifyKind(entity.Date, DateTimeKind.Utc);
and don't forget to assign the return value of SpecifyKind
back into your object like I have
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With