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));
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top