2016-05-13 18 views
4

我正在構建Web API,並且在使用DateTimes的JSON序列化時遇到問題。在做了一些測試之後,我只能得出結論,Newtonsoft.Json.JsonConvert和/或Newtonsoft IsoDateTimeConverter的行爲不是我所期望的。爲什麼在使用DateTimeStyles.AssumeUniversal時,JsonConvert會使用DateTimeKind.Unspecified更改DateTimes的時間?

考慮一下:

// Arrange 
var noonUtc = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Utc); 
var noon = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Unspecified); 

var settings = new JsonSerializerSettings(); 

settings.Converters.Add(new IsoDateTimeConverter 
{  
    Culture = CultureInfo.InvariantCulture,  
    DateTimeStyles = DateTimeStyles.AdjustToUniversal 
}); 

// Act 
var utcJson = JsonConvert.SerializeObject(noonUtc, settings); // "\"2016-05-12T12:00:00Z\"" 
var json = JsonConvert.SerializeObject(noon, settings);  // "\"2016-05-12T10:00:00Z\"" 

... // Assertions 

好了,所以爲DateTimeDateTimeKind.Unspecified的時間已經從12點調整到10點。我現在在斯德哥爾摩,現在比UTC的時間提前了兩個小時,非常合適。

然而,讓我們改變串行設置使用DateTimeStyles.AssumeUniversal,像這樣:

settings.Converters.Add(new IsoDateTimeConverter 
{  
    Culture = CultureInfo.InvariantCulture,  
    DateTimeStyles = DateTimeStyles.AssumeUniversal 
}); 

這將導致確切相同字符串,從而也兩個小時調整DateTimeDateTimeKind.Unspecified!不應該假定日期時間已經是UTC時間,並且保持原樣?我在這裏錯過了什麼?

回答

2

我不認爲你錯過了任何東西;這看起來可能是IsoDateTimeConverter中的一個錯誤。下面是從source相關代碼:

if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal 
    || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) 
{ 
    dateTime = dateTime.ToUniversalTime(); 
} 

正如你所看到的,它只查看_dateTimeStyles是否調用ToUniversalTime()之前設置爲AdjustToUniversalAssumeUniversal;它從不檢查日期的Kind屬性。

而對於DateTime.ToUniversalTime()文檔這樣說:

與.NET Framework 2.0版開始,由ToUniversalTime方法的返回值是由當前DateTime對象的Kind屬性決定。下表介紹了可能的結果。

Kind  | Results 
----------- | ---------------------------------------------------------- 
Utc   | No conversion is performed. 
Local  | The current DateTime object is converted to UTC. 
Unspecified | The current DateTime object is assumed to be a local time, 
      | and the conversion is performed as if Kind were Local. 

所以,是的,它看起來像器絕對不能調用在這種情況下ToUniversalTime。你可能想要report an issue

現在,您可以通過以正確行爲實施替換轉換器(從原始派生)來解決此問題。這可能更接近你想要的:

public class CorrectedIsoDateTimeConverter : IsoDateTimeConverter 
{ 
    private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     if (value is DateTime) 
     { 
      DateTime dateTime = (DateTime)value; 

      if (dateTime.Kind == DateTimeKind.Unspecified) 
      { 
       if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeUniversal)) 
       { 
        dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); 
       } 
       else if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeLocal)) 
       { 
        dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Local); 
       } 
      } 

      if (DateTimeStyles.HasFlag(DateTimeStyles.AdjustToUniversal)) 
      { 
       dateTime = dateTime.ToUniversalTime(); 
      } 

      string format = string.IsNullOrEmpty(DateTimeFormat) ? DefaultDateTimeFormat : DateTimeFormat; 
      writer.WriteValue(dateTime.ToString(format, Culture)); 
     } 
     else 
     { 
      base.WriteJson(writer, value, serializer); 
     } 
    } 
} 
相關問題