返回首页

JavaScript 日期解析器:隐藏错误及修复

本文分析了 JavaScript 中 Date 构造函数的意外行为,它能从任意文本中解析日期。描述了 V8 算法、时区问题以及生产环境中安全解析的推荐方法。

为什么 JS 将文本转为日期:V8 解析器详解
Advertisement 728x90

JavaScript日期解析的危险陷阱:如何避免常见错误

JavaScript中的new Date(string)构造函数会尝试从任意字符串输入中提取日期,常常导致意外结果。这并非漏洞——而是V8和SpiderMonkey引擎的特性,其解析器会主动解读文本。

以下示例揭示了问题所在:

new Date("2020-01-23")
// Wed Jan 22 2020 19:00:00 GMT-0500(被解释为UTC)

new Date("Today is 2020-01-23")
// Thu Jan 23 2020 00:00:00 GMT-0500(本地时间)

new Date("Route 66")
// Sat Jan 01 1966 00:00:00 GMT-0500

new Date("Beverly Hills, 90210")
// Mon Jan 01 90210 00:00:00 GMT-0500

这些转换生成了有效的Date对象,但数值错误,使得调试变得困难。而“进退两难”情况返回Invalid Date,虽然罕见,但实际上是正确行为。

Google AdInline article slot

V8解析器的工作原理

ECMAScript规范仅要求解析ISO 8601的一个子集,其余部分由实现决定。

V8算法步骤如下:

  • 检查是否为ISO 8601:字符串必须以四位年份或符号开头。
  • 回退到旧式解析器:忽略文字,直到找到第一个数字。
  • DayComposer中处理数字:

- 1–12:解释为月份。

Google AdInline article slot

- 13–31:存在歧义,通常被拒绝。

- 32+:视为年份(50–99 +1900)。

- 默认:1月1日。

Google AdInline article slot

这解释了为何"Route 66"变成1966,而"90210"变成90210年。

时间区混淆问题

"2020-01-23"这样的ISO字符串会被解析为UTC,而旧格式则使用本地时间。差异源于前缀:

  • 纯ISO:解析为UTC。
  • 包含文字的字符串:进入旧模式,使用本地时间。

前后空白字符会改变解析行为,根据时区不同,结果可能偏移多达一天。

Safari(JavaScriptCore)更严格:它拒绝旧式示例及带尾部空格的字符串。

实际项目中的错误与修复方案

在真实项目中,对用户输入回退使用new Date()可能导致"Route 66"被误解析为日期,从而破坏数据表。解决方案:仅依赖带有正则验证的自定义解析器。

中级及以上开发者的最佳实践:

  • 在调用new Date()前,用正则表达式验证格式。
  • 使用date-fns parseISO()进行严格的ISO解析。
  • 如需完全控制,选用Luxon或Day.js等库并搭配自定义解析器。
  • 绝对不要对不可信输入使用new Date(string)
  • 跨浏览器测试:V8、SpiderMonkey、JSC。

JavaScript与Python对比:谁更可靠?

Python避免歧义:

  • datetime.fromisoformat():仅接受ISO 8601格式。
  • strptime():严格格式匹配,不允许额外字符。

JavaScript的旧式解析器会默默猜测;而Python则抛出ValueError

关键总结

  • Date解析器能从随机文本中生成看似有效却错误的对象。
  • 解析行为(ISO vs 旧式)取决于前缀和空格。
  • 生产环境:始终在解析前验证输入。
  • 建议迁移到Temporal API,确保代码未来兼容性。
  • 由于引擎差异,跨浏览器测试至关重要。

— Editorial Team

Advertisement 728x90

继续阅读