Mar*_*eau 15 javascript ajax asp.net-mvc datetime json
我的客户在保险领域,需要潜在被保险人的出生日期.它使用jQuery datepicker输入到Web表单中,使用Knockout连接到模型属性,并通过JSON中的ajax发送到MVC 4控制器.
一些保单持有人已收到错误的出生日期的保险文件.在随后的调查中,我们发现除了数据输入错误之外,错误的日期集中在以下两个时期:
由于我们的客户在美国/蒙特利尔时区,我立即想到了DST的问题.DST规则于2007年在此时区发生变化.
阅读了几篇文章和其他Stack Overflow问题,我了解到ECMAScript 5标准规定实施必须考虑当前的DST规则,而不必尊重DST的变更历史.ES5 15.9.1.8目前唯一不符合此规范的浏览器是IE10(稍后将详细介绍).
根据此规范,浏览器将报告日期如下(在Chrome中测试):
(new Date(2006, 9, 31, 0, 0, 0)).toISOString()
// 2006-10-31T04:00:00.000Z
(new Date(2008, 9, 31, 0, 0, 0)).toISOString()
// 2008-10-31T04:00:00.000Z
Run Code Online (Sandbox Code Playgroud)
.NET平台正确报告日期如下:
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
Run Code Online (Sandbox Code Playgroud)
导致此问题的一个原因是,如果被保险人在2007年10月的最后一周出生,则UTC时间将错误地报告给我的MVC控制器,例如:
Javascript日期对象:
new DateTime(2006, 9, 31, 0, 0, 0);
Run Code Online (Sandbox Code Playgroud)
.Net解析JSON数据并得到:
10-30-06 23:00:00 GMT
Run Code Online (Sandbox Code Playgroud)
在测试期间,我发现JavaScript中的Date对象的内部表示与.Net DateTime的内部表示不兼容,并且我无法使用JavaScript表示形式滚动自己的解析器:
Javascript:
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
Run Code Online (Sandbox Code Playgroud)
.Net:
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
Run Code Online (Sandbox Code Playgroud)
(这是错误的,因为它表示为2006年10月30日当地时间23:00.)
对于我的客户端的用例,因为它是我感兴趣的日期部分,我可能只是将Date对象的时间部分设置为正午,这将使我免受JavaScript对日期误解的所有情况的影响.不幸的是,这是一个丑陋的补丁,并没有解决其他潜在的情况,例如,如果我的客户希望我们实现一个功能,要求我们确定过去发生的事件的确切日期和时间.
另一种方法是编写一个算法来检测我的控制器中可能存在问题的DST周,如果我在相关周内得到一个日期,则设置正确的时间.不幸的是,鉴于ECMAScript 6标准规定必须遵守DST更改历史记录(因此所有未来的浏览器都应该正确处理这种情况),并且鉴于IE10已经正常工作,我害怕将来会出现问题.从架构上讲,这种方法似乎也是错误的.
要考虑的另一个方面是,如果我必须将模型从客户端传递到服务器然后再传回客户端,我将需要有一种方法来重新创建"坏"JavaScript Date对象,以确保不会影响显示在应用程序的客户端.
没有解决方案似乎是正确和可扩展的.我无法相信以前没有人曾经处理过这个问题.
如何永久地解决这个问题,以及可以应用于所有其他JavaScript/MVC项目的方式?
编辑#1 - 与问题没有直接关系的附加信息:
正如@ matt-johnson指出的那样,很少会出现应该使用时区信息的情况.然而,在这种情况下,被保险人的出生日期与我的客户所在的时区有关.让我们以维多利亚不列颠哥伦比亚省的一名17岁生日为例,他的生日是12月2日.如果他在12月1日22:00提交保险报价,即使他仍然是17岁,保险报价也会被接受,因为他已经在我客户的时区18.同样的规则适用于保险定价.这是法律要求.
为了清楚起见,我正在寻找一种全面的解决方案,可以应用于任何其他项目.具体问题是:Javascript,在2007年之前的几个特定周内,以1小时的偏移量报告时间.无论我如何表示时间(本地时区或UTC),这种偏移始终存在,因为它是底层数据(而不是数据表示)是错误的.
我使用"设置中午的时间部分"解决方法来规避错误,但潜在的问题仍然存在.如果我的客户要求我们开发一个网络应用程序,要求我们获取过去特定事件的日期和时间,会发生什么?例如:"请说明事故发生的确切日期和时间:2005年10月31日19:32".MVC控制器将时间设置为18:32.
您忽略了 .NetDateTime
结构的一部分。具体来说,您没有考虑到.Kind
any 的属性DateTime
是三个可能值之一DateTimeKind
。更多信息请参见 MSDN。
如果您使用 UTC 值,则需要设置DateTimeKind.Utc
。由于 JavaScript 的数值是基于 UTC 的,因此您应该使用它进行转换:
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMilliseconds(1162267200000);
Run Code Online (Sandbox Code Playgroud)
另外,您正在做一些有点奇怪的事情,即zzz
在DateTime
具有DateTimeKind.Unspecified
. 在这种情况下,实际上没有任何与该值关联的时区,但 .Net 会假设您希望它的行为就像是 时区一样DateTimeKind.Local
,因为zzz
否则格式化程序没有任何意义。
您确实需要小心,不要使用DateTimeKind.Local
值(例如DateTime.Now
)或任何未指定的类型可能被误解为本地的值(例如zzz
或.ToUniversalTime()
)。在这种情况下,“本地”将是您的服务器本地的,这在 Web 应用程序中很少相关。 在这里阅读更多内容。
关于 JavaScript 的 DST 实现,我对 ECMAScript 规范也做了类似的观察。 您可以在这里阅读更多相关内容。
说了这么多,您遇到了一个非常具体的问题,那就是 JavaScript 或 .Net 都没有提供表示没有时间的日期的类型。两者都采用将时间设置为午夜的方法。如果您不小心,可能会遇到没有午夜的当地日期的问题,例如巴西的 October 20th, 2013。如果您仅限于美国,那么您可能不会遇到这个特定问题,但从设计角度来看它仍然相关。
最好的方法是使用真正的“没有时间的日期”类型。LocalDate
在 .Net 中,您可以在Noda Time库中找到它。作为 ISO8601 字符串,它看起来像"2013-10-31"
,没有任何时间或时区。
如果您不想使用 Noda Time(尽管我强烈推荐它),您仍然可以使用DateTime
.Net 中的类型,只是要非常小心,不要将时间部分用于任何事情。应该有DateTimeKind.Unspecified
。
不幸的是,目前 JavaScript 中还没有一个原生类型来表示没有时间的日期。(JavaScriptDate
类可能应该被命名为DateTime
)。因此,您需要自己构建一个仅包含日期的字符串。您可以手动组装它Date
,例如:
var s = dt.getFullYear() + "-" +
(dt.getMonth() < 10 ? "0" : "") + dt.getMonth() + "-" +
(dt.getDate() < 10 ? "0" : "") + dt.getDate();
Run Code Online (Sandbox Code Playgroud)
但更简单的方法是使用moment.js库:
var s = moment(dt).format("YYYY-MM-DD");
Run Code Online (Sandbox Code Playgroud)
我只是提出我对“最佳方法”的看法。但是,如果您只是想知道如何防止当前正在执行的操作失败,那么您应该小心,不要在 JavaScript 响应中转换为 UTC。你正在这样做.toISOString()
。同样,您可以手动组合完整的日期和时间,也可以使用 moment 来格式化根据本地时间的响应。
如果您认真思考一下,生日实际上并没有与之相关的时间或时区。大多数人在计算自己的年龄时不会跟踪出生的小时/分钟/秒(或出生地点的时区)。您可能会为占星学这样做,但对于日常业务使用,您只需使用您评估年龄的当地时区的一天开始时间。所以日期确实是唯一重要的数据。
归档时间: |
|
查看次数: |
332 次 |
最近记录: |