Dates in my database are stored as Utc. But when I retreieve them w/ the entity framework they come out as type unspecified.
When JSON.Net serializes them they are not in Utc format. Is there a way to tell JSON.Net to serialize DateTimes as Utc even if their type is not specified as Utc?
Set DateTimeZoneHandling
on JsonSerializerSettings
to Utc
. That will convert all dates to UTC before serializing them.
public void SerializeObjectDateTimeZoneHandling()
{
string json = JsonConvert.SerializeObject(
new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Unspecified),
new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
Assert.AreEqual(@"""2000-01-01T01:01:01Z""", json);
}
Documentation: DateTimeZoneHandling setting
The response above totally works, and so I used that to create an attribute to convert an API response from PST to UTC.
First I needed to create a JsonConverter
public class UTCDateTimeConverter : Newtonsoft.Json.JsonConverter {
private TimeZoneInfo pacificZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
public override bool CanConvert(Type objectType) {
return objectType == typeof(DateTime);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
if (reader.Value == null) return null;
var pacificTime = DateTime.Parse(reader.Value.ToString());
return TimeZoneInfo.ConvertTimeToUtc(pacificTime, pacificZone);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
writer.WriteValue(TimeZoneInfo.ConvertTimeFromUtc((DateTime) value, pacificZone));
}
}
Then I had to apply that to the properties that needed to be converted
public class Order{
[JsonConverter(typeof(UTCDateTimeConverter))]
public DateTime OrderDate {get;set;}
}
As @dez mentioned in a comment, you can "mark" the DateTime objects as UTC directly in .net code right after LOADING them from DB and BEFORE serializing them:
var item = GetItemFromDb(...);
// mark appropriate DateTime fields manually as needed
item.OrderDate = DateTime.SpecifyKind(item.OrderDate, DateTimeKind.Utc);
// now it will be serialized to "2018-10-17T16:21:23.507Z" with the Z at the end
// and javascript will parse it properly and convert to local timezone as needed
To me, it was simpler to create the UTC converter for DateTime properties (based on the implementation of the Newtonsoft.Json.Converters.IsoDateTimeConverter).
public class UtcJsonDateTimeConverter : DateTimeConverterBase
{
private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFZ";
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string text;
if (value is DateTime dateTime)
{
text = dateTime.ToString(DefaultDateTimeFormat, CultureInfo.InvariantCulture);
}
else
{
throw new JsonSerializationException(
$"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}.");
}
writer.WriteValue(text);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
bool nullable = objectType == typeof(DateTime?);
if (reader.TokenType == JsonToken.Null)
{
if (!nullable)
{
throw new JsonSerializationException($"Cannot convert null value to {objectType}.");
}
return null;
}
if (reader.TokenType == JsonToken.Date)
{
return reader.Value;
}
else if (reader.TokenType != JsonToken.String)
{
throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}.");
}
string date_text = reader.Value.ToString();
if (string.IsNullOrEmpty(date_text) && nullable)
{
return null;
}
return DateTime.Parse(date_text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
}
public class SomeEntity
{
[JsonProperty(PropertyName = "id", Order = 1)]
public int ID { get; set; }
[JsonProperty(PropertyName = "created", Order = 2)]
[JsonConverter(typeof(UtcJsonDateTimeConverter))]
public DateTime Created { get; set; }
}
I used the accepted answer however applied to the default settings:
JsonConvert.DefaultSettings = (() =>
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter());
settings.Formatting = Formatting.Indented;
settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
return settings;
});
©2020 All rights reserved.