C# Date (server) => Json date/time (browser) => C# date/time with time part (server) issue
Posted
技术标签:
【中文标题】C# Date (server) => Json date/time (browser) => C# date/time with time part (server) issue【英文标题】:C# Date (server) => Json date/time (browser) => C# date/time with time portion (server) issue 【发布时间】:2020-02-09 13:04:38 【问题描述】:上下文信息:PST 时区、带有 WebApi 2.x 的 Asp.net Mvc 旧版应用程序(使用 json.net 对 json 进行序列化/反序列化)、Chrome/FF 浏览器。该应用程序配置有:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Unspecified;
一位测试人员报告了与此日期相关的问题:1977-04-15。这是流程:
C# 1977-04-15 => 由于使用未指定,序列化为 json 为“1977-04-15T00:00:00”。 在浏览器中(在 PST 时区),它被解析为:var dt = new Date("1977-04-15T00:00:00")
dt
Output: Date Fri Apr 15 1977 00:00:00 GMT-0800 (Pacific Standard Time)
序列化回 C# WebApi 服务:
JSON.stringify(dt);
Output: "\"1977-04-15T08:00:00.000Z\""
回到 C# 世界,这变成了(我使用了 linqpad):
Console.WriteLine(DateTime.Parse("1977-04-15T08:00:00.000Z"));
Output: 1977-04-15 01:00:00
似乎 C# 在该日期使用了不同于浏览器的 PST 时区偏移量。我在 FF & Chrome 中对其进行了测试。这正常吗?
解决此问题的最佳方法是什么?
一种方法是将 DateTimeZoneHandling 配置为 Newtonsoft.Json.DateTimeZoneHandling.Local,但我不愿意在现有应用程序上这样做。
如果我使用本地,我有:
c#:
var date = new DateTime(1977, 04, 15); //, 0, 0, 0, DateTimeKind.Local);
JsonSerializerSettings settings = new JsonSerializerSettings
DateTimeZoneHandling = DateTimeZoneHandling.Local
;
Console.WriteLine(JsonConvert.SerializeObject(date, settings));
Ouput: "1977-04-15T00:00:00-07:00"
在 javascript 领域:
"1977-04-15T00:00:00-07:00"
dt
Output: Date Thu Apr 14 1977 23:00:00 GMT-0800 (Pacific Standard Time)
JSON.stringify(dt)
Output: "\"1977-04-15T07:00:00.000Z\""
回到 C# 领域:
Console.WriteLine(DateTime.Parse("1977-04-15T07:00:00.000Z"));
Output:1977-04-15 00:00:00
这似乎有效,日期匹配。
更新:
经过更多测试,我们在 C# (linqpad) 中进行了测试:
var date = new DateTime(1977, 04, 03, 0, 0, 0, DateTimeKind.Local);
date.ToLongDateString().Dump();
date.ToUniversalTime().Dump();
date = new DateTime(1977, 04, 04, 0, 0, 0, DateTimeKind.Local);
date.ToLongDateString().Dump();
date.ToUniversalTime().Dump();
Output:
Sunday, April 03, 1977
1977-04-03 08:00:00
Monday, April 04, 1977
1977-04-04 07:00:00
在 JavaScript (FF) 中我们有:
var dt = new Date(1977, 3, 24);
dt.toGMTString()
var dt = new Date(1977, 3, 25);
dt.toGMTString()
Output:
"Sun, 24 Apr 1977 08:00:00 GMT"
"Mon, 25 Apr 1977 07:00:00 GMT"
DST 时间表似乎存在差异。
我对 Java 世界很感兴趣,所以我在 scala 中运行了这个:
scala> ZonedDateTime.of(1977, 4, 24, 0, 0, 0, 0, ZoneId.of(ZoneId.SHORT_IDS.get("PST")))
res8: java.time.ZonedDateTime = 1977-04-24T00:00-08:00[America/Los_Angeles]
scala> ZonedDateTime.of(1977, 4, 25, 0, 0, 0, 0, ZoneId.of(ZoneId.SHORT_IDS.get("PST")))
res9: java.time.ZonedDateTime = 1977-04-25T00:00-07:00[America/Los_Angeles]
看起来 C# 是错误的。我已经在带有 .Net framework 4.8 (528049) 的 Windows 7 VM 上进行了测试。
更新 2:
这越来越有趣了。我使用 .net core sdk 2.1 在 MacO 上测试了 F#。
let mutable dt = System.DateTime(1977, 04, 24, 0, 0, 0, System.DateTimeKind.Local)
printfn "%s" (dt.ToLongDateString())
printfn "%A" (dt.ToUniversalTime())
let dt = System.DateTime(1977, 04, 25, 0, 0, 0, System.DateTimeKind.Local)
printfn "%s" (dt.ToLongDateString())
printfn "%A" (dt.ToUniversalTime())
Output:
Sunday, April 24, 1977
4/24/77 8:00:00 AM
Monday, April 25, 1977
4/25/77 7:00:00 AM
注意 DST 偏移量的变化。
【问题讨论】:
Z 表示“祖鲁时间”(即 UTC+0)。 PST 为 -7,因此如果转换为本地 (PST) 时间,我预计将以 Z 结尾的 ISO8601 日期时间减去 7 小时。 是的,但问题是浏览器使用 -8 小时,而 C# 使用 -7。除非我没有看到明显的东西。 服务器上的时区设置是否与客户端相同?在这个日期前后,夏令时是否有任何历史变化,一个人可能注意到而另一个人可能没有注意到? 是的,它们是相同的,即在 PST 区域中。我不知道有任何变化,但我没有看过。我现在正在做。 浏览器在 1977 年 4 月 25 日向 DST 报告更改,即偏移量从 -8 更改为 -7。var dt = new Date(1977, 3, 25); dt.toGMTString();
在 FF 中生成:“Mon, 25 Apr 1977 07:00:00 GMT”。
【参考方案1】:
<rant>
我发现使用DateTime
的日期令人沮丧</rant>
处理您的问题的一种方法是在某个时候提取.Date
(编辑:或new Date(orig.Year, orig.Month, orig.Day)
)部分以释放时间部分。
编辑:回复:cmets: 这是一个可以提供帮助的 hack。最终,管道的每个元素都需要正常工作。
我发现最好的方法是对 3 个部分使用字符串 ("yyyy-MM-dd") 或 int
s。
【讨论】:
我完全同意。我想这就是我要做的,但我不喜欢它,因为我必须回去寻找所有的景点。 这是相当危险的,除非您只使用单一时区。 是的,我所有的用户都在同一个时区,否则我不会这样做。感谢您指出了这一点。我要做很多测试。 你必须小心有夏令时的时区,因为理论上时间倒退一天是可能的(我不是 DLST 专家,也许这是不可能的)。以上是关于C# Date (server) => Json date/time (browser) => C# date/time with time part (server) issue的主要内容,如果未能解决你的问题,请参考以下文章
将序列化的 C# DateTime 转换为 JS Date 对象
C#后台调用前台js(RegisterStartupScript)