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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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
Method 2
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;}
}
Method 3
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
Method 4
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; }
}
Method 5
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;
});
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0