为啥 JavaScript 会根据结构相同的字符串猜测两个不同的时区?

Posted

技术标签:

【中文标题】为啥 JavaScript 会根据结构相同的字符串猜测两个不同的时区?【英文标题】:Why is JavaScript guessing two different timezones based on strings that are identical in structure?为什么 JavaScript 会根据结构相同的字符串猜测两个不同的时区? 【发布时间】:2016-09-23 09:49:47 【问题描述】:

我以这种格式为我的脚本提供两个日期:2016-05-25 12:30:02。但是,javascript 将每个日期设置为不同的 GMT 偏移量。我格式化了日期以匹配建议的答案here 并添加了提供的函数,但我得到了如下所示的相同结果,所以我恢复到下面显示的原始脚本。

var response = JSON.parse(jsonResponse);
var lastExportedOrderData = response.lastExportedOrder;

/* currentDateTime = '2016-05-25 12:30:02'; */
var currentDateTime = new Date(response.scriptExecutionTime); 

/* lastOrderExportedAt = '2016-01-12 16:53:56'; */
var lastOrderExportedAt = new Date(lastExportedOrderData.exported_at); 

currentDateTime 结果: 2016-05-25 12:30:02 -> 2016 年 5 月 25 日星期三 12:30:02 GMT-0400 (EDT)

lastOrderExportedAt 结果: 2016-01-12 16:53:56 > 2016 年 1 月 12 日星期二 16:53:56 GMT-0500 (美国东部标准时间)

只要日期在同一个时区,我并不关心时区。

更新

我的日期现在使用 ISO-8601 标准格式输入,但我的问题仍然存在。 2016-05-25T14:04:00-05:00 结果为 GMT-0400 其中2016-01-12T16:53:56-05:00 结果为 GMT -0500

【问题讨论】:

更新了帖子以阐明脚本的当前状态。我的意思是我尝试了 parseDate() 建议,但不再实施。我粘贴了旧版本的代码以进一步混淆事情。 Stack Overflow 上的问题并不意味着移动目标。一旦你有了答案,就不能修改问题以使答案的重要部分无效。我已经回滚了编辑。它并没有改变基本的东西,那就是它取决于 DST,但仍然...... 我的帖子已更新以反映我当前的输入,而不是覆盖我的原始内容。您提到“..并且由于(在您的示例中)两个日期都被解析为本地时间,因此可以可靠地比较它们”。如果我理解正确,您是否建议在比较日期时忽略 GMT 偏移量?我仍然想了解为什么我会看到两种不同的偏移量,因为我认为我正确地实施了您的建议。 从根本上说,Date 实例只是数字的简单包装。该数字是自 1970 年 1 月 1 日格林威治标准时间午夜以来的毫秒数。您看到的字符串只是对该数据的解释。所以是的,它们可以可靠地进行比较。 【参考方案1】:

它们位于同一时区(美国东部)。不同之处在于 5 月是夏令时,因此是东部夏令时间 (EDT),而 1 月不是,因此是东部标准时间。

请务必注意,它们的基础时间值(例如,从 dt.getTime() 开始)不受时区的影响,并且由于两个日期(在您的示例中)都被解析为 当地时间,它们可以可靠地进行比较。如果一个被解析为本地时间,另一个被解析为 UTC,你只会遇到比较它们的问题。


旁注:您依赖new Date 解析这些字符串,这意味着您依赖的未指定行为可能因 JavaScript 引擎而异。 only 字符串格式引擎需要支持的是a simplified version of ISO-8601,这些字符串不在其中。特别是,一个浏览器的 JavaScript 引擎完全有可能决定将它们解析为 UTC 格式并且另一个引擎的浏览器将决定(就像您的浏览器所做的那样)将它们解析为本地时间。

您还没有说明要在哪个时区解释字符串,但如果(例如)它们是 UTC,您可以通过替换之间的空格轻松地将它们更改为 ISO-8601 格式带有T 的日期和时间,并在末尾添加Z

str = str.replace(" ", "T") + "Z";

生成的字符串,例如 "2016-05-25T12:30:02Z",对任何现代 JavaScript 引擎(因此,不是 IE8)上的 new Date 有效。

或者,如果您知道应该在哪个时区偏移中解释它们(例如 GMT-05:00),您可以将空格替换为 T 并在末尾添加时区偏移:

str = str.replace(" ", "T") + "-05:00";

该字符串(例如,"2016-05-25T12:30:02-05:00")也适用于new Date

或者,获取它们的部分:

var parts = /^(\d4)-(\d2)-(\d2) (\d2):(\d2):(\d2)$/.exec(str);

...并自己建立日期/时间

// As local time
var dt = new Date(
    +parts[1],     // Year
    +parts[2] - 1, // Month (0 = January)
    +parts[3],     // Day
    +parts[4],     // Hour
    +parts[5],     // Minute
    +parts[6]      // Second
);

// As UTC
var dt = new Date(Date.UTC(
    +parts[1],     // Year
    +parts[2] - 1, // Month (0 = January)
    +parts[3],     // Day
    +parts[4],     // Hour
    +parts[5],     // Minute
    +parts[6]      // Second
));

上面的一元 + 从字符串强制转换为数字。 (不过,这并不是绝对必要的,new DateDate.UTC 都会为您执行此操作。)

【讨论】:

美国EDT and EST 是同一地区的不同时区。 ;-) @RobG:是的,很公平。 :-) ...从某个角度来看。日期/时间的东西很奇怪。【参考方案2】:

只是为了补充 TJ 的答案,在与 ES5 及更高版本一致的实现中,字符串 2016-05-25T14:04:00 将被解析为“本地”时间,因为它没有时区偏移。 (在符合 ES2015 的浏览器中。遗憾的是,在之前的规范中,他们弄错了,将其默认为 UTC,目前 Chrome 实现了 ES5 规范 [UTC],但 Firefox 实现了 ES2015 规范 [本地时间]。)但是不要' t 依赖于此,根据 TJ 的答案解析字符串。

创建本地时间值时,会考虑主机时区设置。似乎主机系统设置为 EST/EDT。

EDT 在 3 月的第二个星期日的 02:00 开始,因此 2016-01-12 US Eastern Standard Time (EST) 适用,其偏移量为 -0500。对于 2016 年 5 月 25 日,使用美国东部夏令时间 (EDT),其偏移量为 -0400。

请注意,为 2016-05-25T12:30:02-0400 创建的日期的实际时间值是 2016-05-25T16:30:02Z(即等效的 UTC 时间)。显示时,日期的 toString 方法被调用,该方法应用主机系统偏移该时间和日期以将显示的值调整为“本地”时间。

日期在内部使用 UTC,因此它们可以在任何时区显示,并且仍然代表同一时刻。

【讨论】:

如果你的意思是"2016-05-25T14:04:00"(不是-05:00),ES5 有一个错误。 :-) 如果实现正确地实现了 ES5,那将在 UTC 中解析,因为 ES5 规范 said “缺少时区偏移的值是“Z”。” 这与 ISO- 8601 并且它在 ES2015 规范中得到了纠正,导致我们现在处于奇怪的情况:在当前的 Chrome(v50)上,它被解析为 UTC(ES2015 规范)。在当前的 Firefox (v46.0.1) 上,它被解析为本地时间。 哎哟 @T.J.Crowder—哎呀,修复了第一位。解析的情况是一团糟,需要新的解析和格式化方法,将格式字符串作为参数。有很多小型图书馆可供复制。当前依赖 Intl 对象的策略只完成了一半的工作。但我没有屏住呼吸...... :-( 不能再同意了。可悲的是,我 don't see any proposals 和以前的经验表明 TC-39 很乐意将这项工作留给图书馆。 :-| (我冒昧地更新了答案中的“本地时间”位。顺便说一句,我的评论有点错误,它应该是 "...current Chrome (v50),它被解析为 UTC(ES5 规范).")

以上是关于为啥 JavaScript 会根据结构相同的字符串猜测两个不同的时区?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 中不匹配的相同字符串

为啥当前节点中的int字段与链表中下一个节点的字符串字段相同?

为啥将您的 Javascript 文件移动到您也拥有的不同主域?

为啥隐式符号到字符串的转换会导致 JavaScript 中的 TypeError?

为啥当我在 MD5 哈希中转换相同的 C++ 字符串时,每次都会获得不同的输出?

为啥我的 javascript 一直引用相同的 innerHTML?