使用DateTimeStyles.AssumeUniversal时,为什么JsonConvert使用DateTimeKind.Unspecified更改DateTimes的时间?

我正在构建一个Web API,并且遇到了DateTimes的
JSON序列化问题.在做了一些测试后,我只能得出结论:Newtonsoft.Json.JsonConvert和/或Newtonsoft IsoDateTimeConverter的行为不符合我的预期.

考虑一下:

// 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

好的,所以DateTime与DateTimeKind.Unspecified的时间已从12点调整到10点.我在斯德哥尔摩,目前比UTC早两个小时,非常公平.

但是,让我们更改序列化程序设置以使用DateTimeStyles.AssumeUniversal,如下所示:

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

这导致完全相同的字符串,因此也调整DateTime与DateTimeKind.Unspecified两小时!它是否应该假设日期时间已经是UTC时间并且保留时间原样?我在这里错过了什么?

最佳答案 我不认为你错过任何东西;这看起来可能是IsoDateTimeConverter中的一个错误.以下是
source的相关代码:

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

如您所见,它仅在调用ToUniversalTime()之前查看_dateTimeStyles是否设置为AdjustToUniversal或AssumeUniversal;它从不检查日期的Kind属性.

DateTime.ToUniversalTime()的文档说明了这一点:

Starting with the .NET Framework version 2.0, the value returned by the ToUniversalTime method is determined by the Kind property of the current DateTime object. The following table describes the possible results.

06001

所以是的,看起来转换器在这种情况下绝对不应该调用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);
        }
    }
}
点赞