JavaScript中的DST实现在发送到MVC控制器时导致问题
我的客户是保险行业的,他要求提供潜在被保险人的出生日期。它使用jQuery日期选择器输入web表单,使用Knockout连接到模型属性,并通过JSON中的ajax发送到MVC4控制器 一些投保人收到的保险文件中有错误的出生日期。在随后的调查中,我们发现,除了数据输入错误(这些错误非常微小)外,错误日期还集中在以下两个时期:JavaScript中的DST实现在发送到MVC控制器时导致问题,javascript,ajax,asp.net-mvc,json,datetime,Javascript,Ajax,Asp.net Mvc,Json,Datetime,我的客户是保险行业的,他要求提供潜在被保险人的出生日期。它使用jQuery日期选择器输入web表单,使用Knockout连接到模型属性,并通过JSON中的ajax发送到MVC4控制器 一些投保人收到的保险文件中有错误的出生日期。在随后的调查中,我们发现,除了数据输入错误(这些错误非常微小)外,错误日期还集中在以下两个时期: 3月最后三周至4月初 十月的最后一周到十一月的第一周 由于我们的客户在美国/蒙特利尔时区,我立即想到了DST的问题 通过阅读几篇文章和其他堆栈溢出问题,我了解到ECMAS
- 3月最后三周至4月初
- 十月的最后一周到十一月的第一周
DateTime dt = new DateTime(2006, 10, 31, 0, 0, 0);
Console.WriteLine("{0:MM/dd/yy H:mm:ss zzz}", dt);
// 10-31-06 0:00:00 -05:00
dt = new DateTime(2008, 10, 31, 0, 0, 0);
Console.WriteLine("{0:MM/dd/yy H:mm:ss zzz}", dt);
// 10-31-08 0:00:00 -04:00
此问题的一个原因是,如果被保险人在2007年10月的最后一周出生,UTC时间将错误地报告给我的MVC控制器,例如:
Javascript日期对象:
new DateTime(2006, 9, 31, 0, 0, 0);
10-30-06 23:00:00 GMT
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
.Net解析JSON数据并获取:
new DateTime(2006, 9, 31, 0, 0, 0);
10-30-06 23:00:00 GMT
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
在测试期间,我发现JavaScript中日期对象的内部表示与.Net DateTime的内部表示不兼容,并且我无法使用JavaScript表示滚动我自己的解析器:
Javascript:
new DateTime(2006, 9, 31, 0, 0, 0);
10-30-06 23:00:00 GMT
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
.Net:
new DateTime(2006, 9, 31, 0, 0, 0);
10-30-06 23:00:00 GMT
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
(这是错误的,因为它表示为当地时间2006年10月30日23:00。)
对于我的客户机的用例,因为我感兴趣的是日期部分,所以我可以将日期对象的时间部分设置为中午,这将使我免受JavaScript方面所有日期错误解释的影响。不幸的是,这是一个丑陋的补丁,它无法解决其他潜在的情况,例如,如果我的客户希望我们实现一个功能,要求我们提供过去发生的事件的准确日期和时间
另一种方法是编写一种算法来检测控制器中可能存在问题的DST周,并在有问题的周内获得日期时设置正确的时间。不幸的是,考虑到ECMAScript 6标准规定必须遵守DST更改历史(因此未来所有浏览器都应该正确处理这种情况),并且考虑到IE10已经正常工作,我担心将来会出现问题。从架构上讲,这种方法似乎也是错误的
另一个要考虑的方面是,如果我必须将模型从客户机传递到服务器,然后再返回到客户机,我将需要有一种方法来重新创建“坏”JavaScript日期对象,以确保不影响应用程序客户端的显示。 似乎没有一个解决方案是正确的和可扩展的。我不敢相信以前从来没有人处理过这个问题
我怎样才能永久性地解决这个问题,并以一种可以应用于所有其他JavaScript/MVC项目的方式解决这个问题 编辑#1-与问题没有直接关系的其他信息:new DateTime(2006, 9, 31, 0, 0, 0);
10-30-06 23:00:00 GMT
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
正如@matt johnson所指出的,很少有使用时区信息的情况。然而,在这种情况下,投保人的出生日期与我的客户所在的时区有关。让我们举一个17岁的孩子为例,他住在不列颠哥伦比亚省维多利亚州,生日是12月2日。如果他在12月1日22:00提交保险报价,即使他仍然17岁,保险报价也会被接受,因为他已经在我客户的时区18岁了。同样的规则也适用于保险定价。这是法律要求
我只是想说清楚,我正在寻找一个全面的解决方案,它可以应用于任何其他项目。具体的问题是:在2007年之前的几年中,Javascript只在特定的几周内报告时间,并有1小时的偏移量。无论我如何表示时间(本地时区或UTC),此偏移量始终存在,因为错误的是底层数据(而不是数据表示)
我使用了“将时间部分设置为中午”的变通方法来规避该错误,但潜在的问题仍然存在。如果我的客户要求我们开发一个web应用程序,要求我们获取过去某个特定事件的日期和时间,会发生什么情况?例如:“请说明事故发生的确切日期和时间:2005年10月31日19:32”。MVC控制器将时间设置为18:32。您忽略了.Net的部分
日期时间结构。具体来说,您没有考虑任何DateTime
的.Kind
属性是三个可能的DateTimeKind
值之一
如果使用UTC值,则需要设置DateTimeKind.UTC
。由于JavaScript的数值是基于UTC的,因此您应该使用此值进行转换:
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMilliseconds(1162267200000);
另外,您正在做一些有点奇怪的事情,在具有DateTimeKind.Unspecified
的DateTime
上使用zzz
格式化程序。在这种情况下,实际上没有任何时区与该值关联,但是.Net会假定您希望它的行为就像是DateTimeKind.Local
,因为zzz
格式化程序在其他情况下没有意义
您确实需要小心不要使用DateTimeKind.Local
值(例如DateTime.Now
)或任何未指定类型可能被误解为Local的值(例如)