使用JSON.NET解析json格式的日期Date(epochTime-offset)

Use JSON.NET to parse json date of format Date(epochTime-offset)

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

/Date(1445301615000-0700)/

表示 UTC 时间 2015-10-19 17:40:15

如果将 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 的日期时间。我可以写一个自定义的 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 来表示这种 epochtime-offset 格式。

这是一个简化的例子:

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 是否有处理此类日期字符串的本机方法。

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

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
}

/Date(1445301615000-0700)/

That is meant to represent a UTC time of 2015-10-19 17:40:15

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

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

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

/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 解释偏移量并应用默认行为,这通常不是 期望的行为。