c# - 如何让 Newtonsoft 按原样反序列化日期并将其命名为 utc?

标签 c# datetime json.net utc

我在sql数据库中有一个日期时间:2020-07-21 13:55:22.990 这个日期时间是UTC,尽管(如果我错了请纠正我),没有办法知道在数据库。作为引用,我在 CST(UTC-6),这个问题是在夏令时期间写的(所以我现在距离 UTC -5)。

我正在向该数据库发送查询并使用以下方法返回 json(我不认为它有任何问题,但为了以防万一,我将其包含在内):

private static string GetJSONFromSQLQuery(string query, string connectionString)
{
    var dataTable = new DataTable();

    using (var sqlConnection = new SqlConnection(connectionString))
    using (var sqlCommand = new SqlCommand(query, sqlConnection))
    using (var sqlDataAdapter = new SqlDataAdapter(sqlCommand))
    {
        sqlConnection.Open();
        sqlDataAdapter.Fill(dataTable);
    }

    var rowsAsDictionaries = new List<Dictionary<string, object>>();
    foreach (DataRow row in dataTable.Rows)
    {
        var rowAsDictionary = new Dictionary<string, object>();

        // ReSharper disable once LoopCanBeConvertedToQuery
        foreach (DataColumn column in dataTable.Columns)
            rowAsDictionary.Add(column.ColumnName, row[column]);

        rowsAsDictionaries.Add(rowAsDictionary);
    }

    return new JavaScriptSerializer().Serialize(rowsAsDictionaries);
}

这是我返回的 json 的片段(在实际调用中,对象中会有更多属性,并且通常会有更多对象):


[
    {
        "Timestamp":"\\/Date(1595357722990)\\/",
    },
]

https://www.freeformatter.com/epoch-timestamp-to-date-converter.html在“将纪元时间戳转换为日期”下显示这是 2020 年 7 月 21 日下午 1:55:22

然后我用 newtonsoft 反序列化 json:

var deserializedJson = JsonConvert.DeserializeObject<List<MyObjectType>>(json)

MyObjectType 有一堆 { get;放; } 属性,其中之一是:

public DateTime Timestamp { get; set; }

根据上面的 json 片段和 newtonsoft 调用,时间戳将设置为 7/21, 6:55:22,Kind 属性将设置为 Utc。这不正确不是我所期望的。

经过一番搜索,我了解了 JsonSerializerSettings 中的 DateTimeZoneHandling 属性,并使用 4 个枚举选项中的每一个进行了以下调用:

var local = JsonConvert.DeserializeObject<List<MyObjectType>>(json, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Local,

});

var roundtrip = JsonConvert.DeserializeObject<List<MyObjectType>>(json, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,

});

var unspecified = JsonConvert.DeserializeObject<List<MyObjectType>>(json, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Unspecified,

});

var utc = JsonConvert.DeserializeObject<List<MyObjectType>>(json, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,

});

这为我提供了 MyObjectType 中的以下 DateTime 对象(注意这些是 DateTime 对象的片段):

deserializedJson:
    Time: 7/21 6:55:22,
    Kind: Utc

local:
    Time: 7/21 1:55:22,
    Kind: Local
    
roundtripKind:
    Time: 7/21 6:55:22,
    Kind: Utc
    
unspecified:
    Time: 7/21 6:55:22,
    Kind: Unspecified

utc:
    Time: 7/21 6:55:22,
    Kind: Utc

Kind 属性特别重要,因为我会打电话

TimeZoneInfo.ConvertTimeFromUtc(theDeserializedTimeStamp, TimeZoneInfo.Local);

我想要的输出是:

Time: 7/21 1:55:22,
Kind: Utc

因为这反射(reflect)了数据库中的内容,所以我可以调用 TimeZoneInfo.ConvertTimeFromUtc 将 UTC 时间转换为本地时区。

如何获得该输出?

编辑:

我找到了一个解决方案。我将 GetJSONFromSQLQuery 中的返回语句更改为:

return JsonConvert.SerializeObject(rowsAsDictionaries);

并且所有 DateTimeZoneHandling 选项将时间保留为下午 1:55,并且 Kind 属性的结果有所不同(但符合预期)。不知道为什么 JavaScriptSerializer 中的 json 不能很好地运行,但至少我现在摆脱了困境。

JsonConvert.SerializeObject 的 json 如下所示:

[
   {
      "Timestamp":"2020-07-21T13:55:22.99"
   }
]

最佳答案

一些事情:

const dt = new Date(1595357722990);
console.log(dt.toISOString());

  • 您确实应该避免 JavaScriptSerializerThe docs说:

    Important

    For .NET Framework 4.7.2 and later versions, the APIs in the System.Text.Json namespace should be used for serialization and deserialization. For earlier versions of .NET Framework, use Newtonsoft.Json.

  • 格式“\\/Date(1595357722990)\\/”通常被称为“ASP.NET JSON Date”,因为它首先发布到世界上ASP.Net 的早期版本,大约在 JSON 开始变得比 XML 更流行的时候。当时这是一种糟糕的格式,现在也是如此。请参阅On the nightmare that is JSON Dates...来自 Scott Hanselman,早在 2012 年。

  • 您应该使用 ISO 8601扩展日期+时间格式(另请参阅 RFC 3339 ),这是大多数现代 JSON 实现的默认格式,包括 Newtosoft JSON.Net 和 System.Text.Json

  • 你说:

    Timestamp would be set to 7/21, 6:55:22 and the Kind property would be set to Utc. This is incorrect.

    不,这实际上是正确的 - 假设您指的是 PM。这就是 1595357722990 代表的值。可能您创建该值不正确(请参阅下文)。

  • 在大多数情况下,包括您的情况,您不需要更改 JSON.Net 的默认 DateTimeZoneHandling 选项。

  • 您说您想要的输出是具有 DateTimeKind.Utc - 但您的意思似乎是转换为本地时间之后?这没有道理。如果您要转换为本地时间,则结果类型将为 DateTimeKind.Local

  • 在这种情况下,您只需使用 TimeZoneInfo.ConvertTime 就可以了。由于输入类型已设置为 DateTimeKind.Utc,因此 ...FromUtc 部分不是必需的。 (只有当输入类型为 DateTimeKind.Unspecified 时,这才真正重要。)

  • 查看您的编辑,您似乎实际上将 2020-07-21T13:55:22.99 存储在数据库中。如果该字段旨在解释为 UTC,那么您确实应该在生成的 JSON 中看到 13 点。但是,它还应该包含一个尾随的 Z 以指示数据是 UTC。

    您的 JSON 实际上应该如下所示:

    [
        {
            "Timestamp":"2020-07-21T13:55:22.990Z"
        }
    ]
    

    默认情况下,它可能会有一些额外的小数位,这很好。

  • 您没有得到该结果的原因以及 JavaScriptSerializer 创建的原始值是错误的,是因为您没有设置 DateTimeKind.Utc 从数据库读取的值。您需要使用 DateTime.SpecifyKind 更改代码才能明确执行此操作。例如,您可以将内部循环修改为:

    foreach (DataColumn column in dataTable.Columns)
    {
        object o = row[column];
        if (o is DateTime)
        {
            o = DateTime.SpecifyKind((DateTime)o, DateTimeKind.Utc);
        }
    
        rowAsDictionary.Add(column.ColumnName, o);
    }
    
  • 一般来说,您的代码中存在很多次优的情况。实际上,您应该使用可枚举的或结构化类的列表,而不是对象字典,并且您应该使用 DataReader 而不是 DataTable。另外,在大多数框架中,例如在 ASP.net 中,您应该直接返回该列表并让框架进行 JSON 转换。换句话说,您可能根本不应该在此方法中转换为 JSON。

关于c# - 如何让 Newtonsoft 按原样反序列化日期并将其命名为 utc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63020230/

相关文章:

c# - parking 费计算

python - 在python中生成给定范围内的所有日期

c# - 当只读属性与对象引用一起使用时 Json.NET 失败?

c# - Json.NET 初始化抛出 System.TypeLoadException

c# - 当 header 设置为 HTTP/1.0 404 Not Found 时,如何在 C# 中获取页面的 HTML

c# - .Net - 随机排序的自定义分页

C# - 单元测试,模拟?

java - 将 getTime() 的值转换为毫秒时出错

java - 为什么要获得具有相同时区的不同 localDate

json - F#:如何将 Json.NET [JsonConstructor] 属性应用于主构造函数?