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

标签 datetime json.net

我正在构建一个 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

好的,DateTimeDateTimeKind.Unspecified 的时间已从 12 点调整为 10 点。我在斯德哥尔摩,目前比 UTC 早两个小时,所以很公平。

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

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

这会产生完全相同的字符串,因此也会将 DateTimeDateTimeKind.Unspecified 调整两个小时!难道不应该假设日期时间已经是 UTC 时间并保持原样吗?我在这里缺少什么?

最佳答案

我认为你没有遗漏任何东西;这看起来可能是 IsoDateTimeConverter 中的一个错误。这是来自 source 的相关代码:

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

如您所见,在调用 ToUniversalTime()< 之前,它仅查看 _dateTimeStyles 是否设置为 AdjustToUniversalAssumeUniversal/;它从不检查日期的 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.

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);
        }
    }
}

关于datetime - 使用 DateTimeStyles.AssumeUniversal 时,为什么 JsonConvert 会使用 DateTimeKind.Unspecified 更改 DateTimes 的时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37203207/

相关文章:

regex - 如何查找两个日期之间的小时数 dd/mm/yyyy hh :mm

c# - 禁用 Newtonsoft 更改 JSON 大小写

c# - 使用Newtonsoft如何避免 ":"后的tailing space空间

Python:将不明确的时间转换为时间对象的有效方法?

c - OCIDate 在进入 Oracle 的过程中被破坏

PHP DateTime 默认时区

mysql - 如何将日期和日期时间与 Doctrine 进行比较

vb.net - 无法访问 Newtonsoft.Json.Linq.JProperty 上的子值

entity-framework - 序列化时如何忽略导航属性

C# 从序列化的 json 数组访问值