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

时间:2015-10-19 21:50:17

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

我在C#中使用Json.net 7.0.1来使用rest 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都会产生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是否有一种本地方式来处理这种类型的日期字符串。

2 个答案:

答案 0 :(得分:6)

  

/日期(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

那不好。基本上,如果有任何偏移量,它认为它是你的本地时区,它可能是,但它可能不是。

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

答案 1 :(得分:3)

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