The following statement prints "1/1/0001 4:00:00 PM -05:00"
Console.WriteLine(JsonConvert.DeserializeObject<DateTimeOffset>("\"0001-01-01T16:00:00\""));
This is because when json.net deserializes a DateTime string (which doesn't have an offset) to a DateTimeOffset object, it assigns the local offset, which in this case is -05:00.
What if I don't want to use the local offset? Is there any way I can specify an offset to use for this deserialization?
(The use case is the database server and the web server are in different time zones, and I need the incoming requests that have zone-unspecified time to have the database server's offset after deserialization.)
Update: I can't control the incoming time string's format. I have a data transfer object class which has a DateTimeOffset property and I need to store the incoming time data to this property.
The type you're deserializing into should match the data you are expecting. If you're not expecting an offset to be included, then don't deserialize into a DateTimeOffset
. Instead, deserialize to a DateTime
. It will have DateTimeKind.Unspecified
for its .Kind
property.
The knowledge you have about the web server's time zone is extraneous to the task of deserialization. So apply it separately, after-the-fact.
// deserialize the json
DateTime dt = JsonConvert.DeserializeObject<DateTime>("\"2014-01-01T00:00:00\"");
// find your target time zone
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
// apply the time zone to determine the offset, and create the DateTimeOffset
DateTimeOffset dto = new DateTimeOffset(dt, tz.GetUtcOffset(dt));
Update
Per comments, if you need to do this conversion in the manner you've requested, you'll need a custom json converter. This should do the trick:
public class CustomDateTimeConverter : IsoDateTimeConverter
{
private readonly string defaultTimeZoneId;
public CustomDateTimeConverter(string defaultTimeZoneId)
{
this.defaultTimeZoneId = defaultTimeZoneId;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType != typeof (DateTimeOffset) && objectType != typeof (DateTimeOffset?))
return base.ReadJson(reader, objectType, existingValue, serializer);
var dateText = reader.Value.ToString();
if (objectType == typeof(DateTimeOffset?) && string.IsNullOrEmpty(dateText))
return null;
if (dateText.IndexOfAny(new[] { 'Z', 'z', '+'}) == -1 && dateText.Count(c => c == '-') == 2)
{
var dt = DateTime.Parse(dateText);
var tz = TimeZoneInfo.FindSystemTimeZoneById(this.defaultTimeZoneId);
var offset = tz.GetUtcOffset(dt);
return new DateTimeOffset(dt, offset);
}
return DateTimeOffset.Parse(dateText);
}
}
Then you can wire it up during conversion:
var settings = new JsonSerializerSettings();
settings.DateParseHandling = DateParseHandling.None;
settings.Converters.Add(new CustomDateTimeConverter(defaultTimeZoneId: "Eastern Standard Time"));
DateTimeOffset dto = JsonConvert.DeserializeObject<DateTimeOffset>("\"2014-01-01T00:00:00\"", settings);
Be sure to used a valid time zone id. Do not use a fixed offset.
Also, this will not be the correct approach if you are trying to pass a time without a date. That is a completely different problem, and passing in 0001-01-01
for the date is not a great approach. I'll be happy to discuss with you in chat.
If you have the offset in the original string (-8:00):
Console.WriteLine(JsonConvert.DeserializeObject<DateTimeOffset>("\"0001-01-01T16:00:00-08:00\""));
If not, try this
var dateTime = JsonConvert.DeserializeObject<DateTime>("\"0001-01-01T16:00:00\"");
DateTimeOffset dto = new DateTimeOffset(dateTime, TimeSpan.FromHours(-8));