【问题标题】:Create date from string从字符串创建日期
【发布时间】:2017-03-14 04:53:07
【问题描述】:

我有两个字符串 2017-03-15 (date) 和 12:26 (time)。我想从中创建一个本地化的日期对象,而不使用库。

记住现在这里是:Tue Mar 14 2017 12:26:33 GMT+0800 (AWST),如果我这样做:

new Date( date + 'T' + time )

我得到了错误的结果,因为日期被认为是 UTC:

Wed Mar 15 2017 20:26:00 GMT+0800 (AWST)

如果我使用空格:

new Date( date + ' ' + time )

结果正确:

Wed Mar 15 2017 12:26:00 GMT+0800 (AWST)

但是,这在 Safari 上不起作用(如果没有 T)。 Safari 实际上会抛出一个错误 (!)。

我意识到将字符串解析为日期取决于实现。因此,“正确”的唯一方法是:

var timeSplit = time.split(':');
var dateSplit = date.split('-');

new Date( dateSplit[0], dateSplit[1] - 1, dateSplit[2], timeSplit[ 0 ], timeSplit[ 1 ] )

但这只是。所以。丑陋的。 有没有更好的解决方案可以跨浏览器运行?

【问题讨论】:

标签: javascript date


【解决方案1】:

我得到了错误的结果,因为日期被认为是 UTC:

这实际上是 ES5 规范中的一个错误(它说没有时区指示符意味着 UTC,这与它的子集 ISO-8601 standard 不一致)。 ES2015 更正它说没有时区指示符表示本地时间(与 ISO-8601 一致),但是当用于仅日期字符串(如"2018-01-17")时,这会导致与现有代码的兼容性问题。所以 ES2016 不得不再次更新它,从那以后它一直很稳定。如果没有时区指示符,则:

  • date-only 字符串(如 "2019-05-20")以 UTC 解析
  • 一个日期/时间字符串(如"2019-05-20T10:00")被解析为当地时间

由于这种规范混乱,有一段时间我们混合了使用旧 ES5 行为、ES2015 行为或 ES2016 行为的 JavaScript 引擎。一个重要的平台仍然存在错误行为:iOS。

截至 2019 年 5 月 20 日更新的答案:

  • 桌面版本的 Chrome、Firefox、Edge 甚至 IE11 都正确实现了 ES2016+ 规范。
  • Safari(桌面或 iOS)错误地解析了没有 UTC 时区指示符的日期/时间字符串。
  • 所有当前的 iOS 浏览器(当然是 Safari,还有 Chrome、Firefox、Brave、Dolphin...)也会错误地解析没有 UTC 时区指示符的日期/时间字符串。这是因为 iOS JavaScript 引擎 JavaScriptCore (JSC) 错误地实现了这一点,非 Apple iOS 应用程序无法分配可执行内存,因此浏览器无法使用它们通常使用的引擎(Chrome 的 V8、Firefox 的 SpiderMonkey 等)。 ) 因为那些优化引擎需要在运行时创建可执行代码。所以他们改用JSC。 (Chrome 的 V8 最近添加了“无 JIT”纯解释器版本,因此 Chrome 可能会或可能不会开始使用它而不是 JSC。)

您可以在这里查看您当前的浏览器:

var may20 = new Date("2019-05-20");
// UTC hours should be 0 if that was parsed as UTC
console.log(may20.getUTCHours() === 0 ? "OK:" : "Error:", may20.toLocaleString());

var may20At10 = new Date("2019-05-20T10:00");
// Local hours should be 10 if that was parsed in local time
console.log(may20At10.getHours() === 10 ? "OK:" : "Error:", may20At10.toLocaleString());

(除非您位于全年使用 GMT 的几个西非国家之一,否则该检查有效。它依赖于 UTC 和 2019 年 5 月 20 日的当地时间彼此不同。因此它适用于英国因为尽管英国在一年中的大部分时间都使用格林威治标准时间,但支票使用的日期是英国夏令时 [GMT+0100];它在纽约有效,因为纽约从不在格林威治标准时间;但它在廷巴克图不起作用因为Timbuktu uses GMT year-round。)

但是,这在 Safari 上不起作用(如果没有 T)。

没错。 JavaScript 引擎需要支持的唯一 日期/时间格式是 ISO-8601 子集 defined here(以及它从 toString 返回的任何内容,但这在规范中没有定义; 它必须是双向的)。如上所述,至少在 2019 年 5 月 20 日撰写本文时,Safari 并未正确实施规范。

但这只是。所以。丑陋的。有没有更好的解决方案可以跨浏览器运行?

一点也不丑。毕竟,Date 构造函数必须做些什么才能弄清楚。把它放到你的标准库中就可以了。

【讨论】:

  • 哎呀...所以时间应该被视为当地时间,但某些浏览器基本上会出错?!?
  • @Merc:取决于你对错误的定义。 :-) 一些 JavaScript 引擎没有更新行为以匹配 ES2015 规范,而是仍在使用 ES5 定义(这是错误的)。其他人使用 ES2015 行为(要么是因为他们更新了它,要么是因为他们最初拒绝实现 ES5 的错误定义——我相信 Firefox 一直以 ES2015 的方式做到这一点,甚至在规范说它应该这样做之前,因为他们很早就发现它是ES5 规范中的一个错误)。
  • @T.J.Crowder — 将 ISO 8601 仅日期形式视为 UTC 的当前行为与 ISO 8601 不一致。;-)
  • @RobG:啊,是的,ES2016 中的进一步更改以避免“破坏网络”。 :-)
  • @T.J.Crowder,您指的是哪个版本的 Firefox?在 Ubuntu 的 Firefox 66.0.5(64 位)中,我遇到了与 @Merc 相同的问题。 ``` let dateString = "2019-05-19";让 data1 = new Date(dateString); console.log("data hora:",data1.toLocaleString()," | ", data1.toUTCString());让 data2 = new Date(dateString+"T00:00:00-03:00"); console.log("data hora:",data2.toLocaleString()," | ", data2.toUTCString());日期:18/05/2019 21:00:00 | 2019 年 5 月 19 日星期日 00:00:00 GMT 日期:19/05/2019 00:00:00 |格林威治标准时间 2019 年 5 月 19 日星期日 03:00:00 ```
【解决方案2】:

我得到了错误的结果,因为日期被认为是 UTC:

给定:

new Date('2017-03-15T12:26')

IE 8 版本将其视为无效字符串,IE 9+ 将其视为 UTC。 Firefox 38 将其视为本地。

如果我使用空格……结果是正确的

直到你尝试 Safari 并得到一个无效的日期。

但是,这在 Safari 上不起作用(如果没有 T)。 Safari 实际上会抛出一个错误 (!)。

是的,这两个结果都与所有版本的 ECMAScript 一致。 Safari 说“这不是一个有效的 ISO 8601 字符串”,所以返回一个无效的日期。其他人也这么说,但随后又回到了实现特定的启发式方法(他们被允许这样做)。

底线是不要使用 Date 构造函数(或 Date.parse,它们等效于解析)解析字符串。

所以,“正确”的唯一方法是……

是的,手动解析位。为方便起见,您可以使用库,但它们的作用完全相同。

有没有更好的解决方案可以跨浏览器工作?

没有。使用ECMA TC39 committee 并击败他们,直到他们决定定义一个具有不错的解析器和格式化程序的 Date 对象。有很多非常好的现有实现可供选择,包括现有的 javascript 库和其他语言。

不幸的是,实际上已经忽略了解析,格式化的责任已经交给了 ECMA-402,这对于日期格式化来说非常普通,真的不适合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 2014-12-12
    相关资源
    最近更新 更多