【问题标题】:Use JSON.NET to parse json date of format Date(epochTime-offset)使用 JSON.NET 解析格式 Date(epochTime-offset) 的 json 日期
【发布时间】:2015-10-19 21:50:16
【问题描述】:

我在 C# 中使用 Json.net 7.0.1 来使用休息 API。问题在于 API 在其 JSON 响应中使用的日期格式。它看起来像这样:

/Date(1445301615000-0700)/

这意味着表示 2015-10-19 17:40:15 的 UTC 时间

如果您将 1445301615000 插入 epoch time converter,您会看到它是 2015-10-20 00:40:15 。所以比 UTC 早 7 小时。然后它们包括 -0700 大概是为了将其抵消回 UTC。因此,为了给我一个 UTC 时间,他们向我发送了 UTC+7-0700。他们为什么这样做我不知道,但我无法改变。

我的问题是,如何最好地让 Json.NET 解析该日期字符串并得出 2015-10-19 17:40:15 UTC 的 DateTime。我可以编写一个自定义的 JsonConverter 来劫持值并手动操作它,但我想知道是否有更原生的方法。

我尝试将JsonSerializerSettings DateTimeZoneHandling 属性更改为所有不同的值。将其设置为 Utc 只是忽略时区偏移,产生2015-10-20 00:40:15。将其设置为 Local、Unspecified 或 RoundtripKind all yield 2015-10-19 20:40:15,我认为这是因为我的本地时区是 UTC-4,所以它试图将该调整应用于 2015-10-20 00:40 的主要日期值.

我还考虑使用DateFormatString 属性来表示预期的日期字符串格式。但是我找不到合适的format string characters 来表示这种纪元时间偏移格式。

这是一个简化的例子:

Person person;
string json = @"{ 'Name': 'John', 
                  'LastSeen':'/Date(1445301615000-0700)/' }";   // 1445301615000 = 2015-10-20 00:40:15

person = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine(person.LastSeen);     // 10/19/2015 8:40:15 PM    Kind = Local

person = JsonConvert.DeserializeObject<Person>(json, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat });
Console.WriteLine(person.LastSeen);     // 10/19/2015 8:40:15 PM    Kind = Local

person = JsonConvert.DeserializeObject<Person>(json, new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Utc });
Console.WriteLine(person.LastSeen);     // 10/20/2015 00:40:15 PM    Kind = Utc

// In all three, the -0700 portion is being ignored. I'd like person.LastSeen to be 10/19/2015 17:40:15.

再次,我可以知道 API 将给我 UTC+7 并自己进行调整以获得真正的 UTC。但我想知道 Json.NET 是否有本地方式来处理这种类型的日期字符串。

【问题讨论】:

  • 我确实有,还有很多相关的文档。我发现很多关于解析像 Date(1445301615000) 这样的普通“Microsoft 格式”日期字符串的内容,但对于添加了 -0700 偏移量的这种特殊格式却一无所知。

标签: c# .net datetime timezone json.net


【解决方案1】:

/日期(1445301615000-0700)/

这意味着表示 2015-10-19 17:40:15 的 UTC 时间

对不起,这不正确。 UTC 时间为2015-10-20 00:45:15。您的值对应于 本地 时间,在那个时刻具有 -07:00 偏移的时区。

在这个screwy format 中,时间戳部分仍然完全基于UTC。偏移量是额外的信息。它不会更改时间戳。您可以提供不同的偏移量,或者完全省略它,它仍然是同一时刻。

关于时间点,以下所有内容都是等效的。

/Date(1445301615000-0700)/
/Date(1445301615000)/
2015-10-20T00:40:15Z
2015-10-19T17:40:15-07:00

请注意,在 ISO 格式中,偏移量确实改变了值,但在 MS 格式中它不会。

最好不要使用这种格式,因为 ISO8601 是 JSON 的明智选择。但是,如果您坚持使用它,那么最好不要将其反序列化为 DateTime。相反,请使用DateTimeOffset

考虑:

string s = "\"/Date(1445301615000-0700)/\"";
DateTime dt = JsonConvert.DeserializeObject<DateTime>(s);
Console.WriteLine(dt.Kind); // Local

这样不好。基本上,如果有 any 偏移,它会认为这是您的本地时区,可能是,但可能不是。

string s = "\"/Date(1445301615000)/\"";
DateTime dt = JsonConvert.DeserializeObject<DateTime>(s);
Console.WriteLine(dt.Kind); // Utc

这没关系,但您忘记了当地时间。

string s = "\"/Date(1445301615000-0700)/\"";
DateTimeOffset dto = JsonConvert.DeserializeObject<DateTimeOffset>(s);
Console.WriteLine(dto); // 10/19/2015 5:40:15 PM -07:00

这样好多了。如果您确实想要 UTC DateTime,那么:

string s = "\"/Date(1445301615000-0700)/\"";
DateTimeOffset dto = JsonConvert.DeserializeObject<DateTimeOffset>(s);
DateTime utc = dto.UtcDateTime;
Console.WriteLine(utc); // 10/20/2015 12:40:15 AM

所以关键的教训是,无论格式如何,如果数据中存在时区偏移信息,则反序列化为DateTimeOffset。虽然在某些情况下使用DateTime可能有效,但您要求 .NET 解释偏移量并应用默认行为,这通常不是期望的行为。 p>

【讨论】:

  • 是的,我 100% 同意 API 的制造商滥用日期格式。我已经把它作为他们的一个错误提出来了,但并不期待太多......现在,在他们的脑海中/Date(1445301615000-0700)/ = UTC 2015-10-19 17:40:15。疯狂,我知道,但因此我的问题。感谢您的出色解释,以及使用 DateTimeOffset 的建议。我认为这将是最干净的方式。谢谢!
  • 您可以在您这边进行补偿,但是当他们修复错误时,您将不得不放松。为了补偿,请执行dto.UtcDateTime + dto.Offset。 (确实是个 hack!)如果他们需要澄清,请在这篇文章中指出他们 - 或者给他们我的电子邮件。 :)
【解决方案2】:

-hhmm 表示已序列化本地时间,而不是 UTC 时间。 .NET 与许多其他平台一样,承认时区的概念。例如,在 .NET 中,DateTime 类有一个属性,指示您正在处理的日期/时间类型。您可以显式构建不同类型的日期/时间。调试器不指出这一点很糟糕,但是您可以通过以下代码看到它。

var dt1 = new DateTime(2015, 01, 01, 00, 00, 00); // defaults to DateTimeKind.Unspecified
var dt2 = new DateTime(2015, 01, 01, 00, 00, 00, DateTimeKind.Local);
var dt3 = new DateTime(2015, 01, 01, 00, 00, 00, DateTimeKind.Utc);
var dt4 = new DateTime(2015, 01, 01, 00, 00, 00, DateTimeKind.Unspecified);
Debug.WriteLine(dt1.Kind); // writes "Unspecified"
Debug.WriteLine(dt2.Kind); // writes "Local"
Debug.WriteLine(dt3.Kind); // writes "Utc"
Debug.WriteLine(dt4.Kind); // writes "Unspecified"

然后您可以通过以下方式查看 DateTimeKind 对 Json 的影响

// local time -- default datetime handling from JSON.NET
{
    var dateTime = DateTime.Now;
    var jsonObject = new JObject {["dateTime"] = dateTime};
    var jsonString = jsonObject.ToString();
    Debug.WriteLine(jsonString); // uses "2015-10-19T18:13:53.4698565-04:00" form
}
// UTC time -- default datetime handling from JSON.NET
{
    var dateTime = DateTime.Now.ToUniversalTime();
    var jsonObject = new JObject {["dateTime"] = dateTime };
    var jsonString = jsonObject.ToString();
    Debug.WriteLine(jsonString); // uses "2015-10-19T22:13:53.5166571Z" form
}
// local time -- Microsoft-like datetime handling from JSON.NET
{
    var dateTime = DateTime.Now;
    var jsonObject = new JObject {["dateTime"] = dateTime };
    var jsonString = JsonConvert.SerializeObject(jsonObject, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat });
    Debug.WriteLine(jsonString); // uses "/Date(1445292833516-0400)/" format
}
// local time -- Microsoft-like datetime handling from JSON.NET
{
    var dateTime = DateTime.Now.ToUniversalTime();
    var jsonObject = new JObject {["dateTime"] = dateTime };
    var jsonString = JsonConvert.SerializeObject(jsonObject, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat });
    Debug.WriteLine(jsonString); // uses "/Date(1445292833579)/" form
}

【讨论】:

  • 好信息,谢谢。当然,就我而言,我正在尝试反序列化而不是序列化。我相信设置DateFormatHanding = MicrosoftDataFormat 只会影响json 的编写方式,而不影响它的读取方式。我将在我的问题中添加一个更详细的示例。
  • 这是很好的信息,虽然我推荐DateTime.UtcNow 而不是DateTime.Now.ToUniversalTime()。但是,它并没有完全解决反序列化问题。仍然是 +1。
  • 明白。我只是提供了该值如何被放入其中的背景。假设OP可以自己解决剩下的问题。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-02
  • 2017-01-19
  • 2010-11-24
  • 2010-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多