2014-11-27 4 views
6

В обычном .net, Если у нас есть время, которое имеет DateTimeKind.Unspecified Если мы преобразуем ToLocal - предполагается, что дата ввода - это UTC при преобразовании. Если мы конвертируем ToUniversal - предполагается, что дата ввода является локальной при преобразованииjson.net Сериализация/десериализация datetime 'unspecified'

Однако, если наша строка date в JSON.Net не указана, она, похоже, не имеет такой логики? Посмотрите на мои тестовые примеры ниже - я что-то делаю неправильно? Или это по дизайну? или ошибка в JSON.Net? Спасибо!

// TODO: This Fails with output 
    //  date string: "2014-06-02T21:00:00.0000000" 
    //  date serialized: 2014-06-02T21:00:00.0000000Z 
    //  Expected date and time to be <2014-06-03 04:00:00>, but found <2014-06-02 21:00:00>. 
    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_utc_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
     DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); 
     dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

     dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
     dateSerialized.Should().Be(dateRaw.ToUniversalTime()); 
     dateSerialized.Should().Be(dateRawAsUtc); 
    } 

    // TODO: This Fails with output 
    //  date string: "2014-06-02T21:00:00.0000000" 
    //  date serialized: 2014-06-02T21:00:00.0000000-07:00 
    //  Expected date and time to be <2014-06-02 14:00:00>, but found <2014-06-02 21:00:00>. 
    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_local_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
     DateTime dateRawAsLocal = new DateTime(2014, 6, 2, 14, 0, 0, 0, DateTimeKind.Local); 
     dateRawAsLocal.Should().Be(dateRaw.ToLocalTime()); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); 

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

     dateSerialized.Kind.Should().Be(DateTimeKind.Local); 
     dateSerialized.Should().Be(dateRaw.ToLocalTime()); 
     dateSerialized.Should().Be(dateRawAsLocal); 
    } 

    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_unspecified_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; // unspecified, does not have the 'Z' 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 
     dateSerialized.Kind.Should().Be(DateTimeKind.Unspecified); 
     dateSerialized.Should().Be(dateRaw); 
    } 
+0

Попробуйте использовать 'DateTimeOffset' вместо 'DateTime'. Он должен десериализоваться правильно. – daramasala

ответ

2

Я не 100% уверен, что вы ищете здесь, но я думаю, что это не безопасно предположить, что JSON.Net будет удовлетворить все ваши потребности без небольшой помощи. Как Mr. Newton says:

Даты в JSON сложны.

Первая вещь, чтобы определить, действительно ли вы хотите, чтобы поддержать принятие неуказанных дат или собираетесь ли вы предположить, что все входящие даты являются универсальными, даже если они отсутствуют косая Z.

Если вы предположим, что все входящие даты являются универсальными, вы можете просто увидеть, если они имеют косую Z и, если нет, то добавьте его (точно не код продукции, но вы получите идею):

if (!dateString.EndsWith("Z\"", StringComparison.InvariantCultureIgnoreCase)) 
{ 
    dateString = dateString.Substring(0, dateString.LastIndexOf("\"", StringComparison.InvariantCultureIgnoreCase)) + "Z\""; 
} 

Это изменение в предположении, делает требуют, чтобы даты, которые вы тестируете для модификации, были Utc.

Если вы не хотите, чтобы предположить, что входящие даты являются универсальными, но вместо того, чтобы рассматривать их как неопределенные, вы должны изменить способ, которым вы преобразующий поступающую JSON путем замены:

JsonSerializerSettings settings = new JsonSerializerSettings(); 
settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; 
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

с:

var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); 
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); 

Это приведет к неопределенной дате, которая точно соответствует дате. Вот где ваша рука помощи входит в игру:

if (dateSerialized.Kind == DateTimeKind.Unspecified) 
{ 
    dateSerialized = dateSerialized.ToUniversalTime(); 
} 

Это означает, что полный, переработанный первый тест будет выглядеть следующим образом, и он будет проходить:

string dateString = "\"2014-06-02T21:00:00.0000000\""; 
    DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
    DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); 
    dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); 

    var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); 
    DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); 
    if (dateSerialized.Kind == DateTimeKind.Unspecified) 
    { 
     dateSerialized = dateSerialized.ToUniversalTime(); 
    } 

    Console.WriteLine("date string: " + dateString); 
    Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

    dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
    dateSerialized.Should().Be(dateRaw.ToUniversalTime()); 
    dateSerialized.Should().Be(dateRawAsUtc); 
+0

UGH! ... но определенно спасибо за сообщение/info ... звучит так: «по дизайну» ... Теперь, что касается комментария о поддержке неуказанного, это часто случается - например, разработчик просто myDate = новый DateTime (2014, 10, 23); Я хотел бы иметь предсказуемое поведение в отношении того, что это значит или происходит, и если .Net делает это в одну сторону, а JSON.Net делает это по-другому, это ... уродливый ... и, кстати, я использовал ServiceStack раньше, и они делают это так же, как .Net. – Raymond

Смежные вопросы