【问题标题】:is there any workaround for broken v8 date parser?损坏的 v8 日期解析器有什么解决方法吗?
【发布时间】:2015-09-07 00:26:57
【问题描述】:

V8 日期解析器损坏:

> new Date('asd qw 101')
Sat Jan 01 101 00:00:00 GMT+0100 (CET)

我可以像这样使用脆弱的正则表达式:

\d{1,2} (jan|feb|mar|may|jun|jul|aug|sep|oct|nov|dec) \d{1,4}

但它太脆弱了。我不能依赖 new Date (issue in V8) 也不能帮助我,因为时刻是 getting rid off date detection (github issue-thread)

对于损坏的 v8 日期解析器是否有任何解决方法?

说清楚。我们有 Gecko 和 V8,两者都有 Date。 V8 坏​​了 Date,Gecko 有工作。我需要 Gecko (Firefox) 中的 Date

更新:肯定是解析器坏了https://code.google.com/p/v8/issues/detail?id=2602
nope, Status: WorkingAsIntended

【问题讨论】:

  • 为什么会被否决?
  • 如果这是您要检测的日期,我看不出您的正则表达式解决方案有什么问题。
  • 那么你想要什么日期子集?要么接受Date 接受的内容,要么明确定义自己的格式(可能在momentjs 或类似库的帮助下)
  • 我想@Bergi 的问题是,your 对有效日期的定义是什么?我是 node 新手,但是您对解析 new Date('asd qw 101') 的预期结果是什么?
  • @VladimirStarkov:来吧,不是that bad :-) 但是,你应该问问自己,你是否真的需要那个。

标签: javascript node.js date datetime v8


【解决方案1】:

Date 对象基于时间值,即自 1970 年 1 月 1 日 UTC 以来的毫秒数,并具有以下构造函数

new Date();
new Date(value);
new Date(dateString);
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);

来自docs

new Date(dateString) 中的

dateString 是表示日期的字符串值。字符串应该在 由 Date.parse() 方法识别的格式(符合 IETF 的 RFC 2822 时间戳以及ISO8601的一个版本)。

现在查看 date.js 中的v8 sourcecode

function DateConstructor(year, month, date, hours, minutes, seconds, ms) {
  if (!%_IsConstructCall()) {
    // ECMA 262 - 15.9.2
    return (new $Date()).toString();
  }

  // ECMA 262 - 15.9.3
  var argc = %_ArgumentsLength();
  var value;
  if (argc == 0) {
    value = %DateCurrentTime();
    SET_UTC_DATE_VALUE(this, value);
  } else if (argc == 1) {
    if (IS_NUMBER(year)) {
      value = year;
    } else if (IS_STRING(year)) {
      // Probe the Date cache. If we already have a time value for the
      // given time, we re-use that instead of parsing the string again.
      var cache = Date_cache;
      if (cache.string === year) {
        value = cache.time;
      } else {
        value = DateParse(year);               <- DOES NOT RETURN NaN
        if (!NUMBER_IS_NAN(value)) {
          cache.time = value;
          cache.string = year;
        }
      }

    }
...

看起来DateParse() 没有为'asd qw 101' 之类的字符串返回NaN,因此出现错误。您可以在 Chrome(v8) [返回 -58979943000000] 和 Gecko (Firefox) [返回 NaN] 中对 Date.parse('asd qw 101') 进行交叉检查。 Sat Jan 01 101 00:00:00 在您播种 new Date() 时出现,时间戳为 -58979943000000(在两个浏览器中)

对于损坏的 v8 日期解析器是否有任何解决方法?

我不会说 V8 日期解析器坏了。它只是试图以最好的方式满足RFC 2822 standard 的字符串,但壁虎也是如此,并且在某些情况下 break 给出不同的结果

尝试 new Date('Sun Ma 10 2015') 在 Chrome(V8) 和 Firefox(Gecko) 中查看另一个此类异常。 这里 chrome 无法确定天气 'Ma' 代表 'March' 或 'May' 并给出 Invalid Date 而 Firefox 没有。

解决方法:

您可以围绕Date() 创建自己的包装器来过滤V8 自己的解析器无法过滤的那些字符串。但是,对 ECMA-5 中的内置子类化是不可行的。在 ECMA-6 中,可以对内置构造函数(数组、日期和错误)进行子类化 - reference

但是,您可以使用更强大的正则表达式来根据 RFC 2822/ISO 8601

验证字符串
^(?:(?:31(\/|-|\. |\s)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.|\s)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.|\s)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.|\s)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

图片由debuggex生成

所以,看起来 v8 并没有损坏,只是工作方式不同。

希望对你有帮助!

【讨论】:

  • 帮助很大。顺便说一句,今天我发现了 2013 年 3 月的一个问题,即 V8 解析器应该返回 NaN btw 我仍然认为它已损坏 code.google.com/p/v8/issues/detail?id=2602,所以它可能已正式损坏
  • 或者可能不是“状态:WorkingAsIntended”
  • 没错!恕我直言,这取决于解析器对标准 (RFC/ISO) 的支持程度。它们主要涵盖应该按预期工作的场景,但在某些用例中会抛出垃圾。例如,V8 确实在 new Date('asd qw 21') 上抛出了一个 TypeError(如预期的那样),但将类似结构的字符串 new Date('asd qw 121') 限定为日期(它不应该)。人们总是可以想出一种更好的正则表达式/解析技术来获得更广泛的支持,但仍然不能保证它适用于每种有效格式和垃圾字符串。在这种情况下进行详尽的测试既不实际也不可能:)
  • 那个正则表达式和图表很漂亮 - 但我似乎无法让它与 RFC2822 一起使用,而且它当然不适合 ISO8601。
【解决方案2】:

您似乎在寻求一种方法来解析可能采用任何特定格式的字符串并确定所表示的数据。一般来说,这是一个坏主意的原因有很多。

你说 moment.js 是“摆脱日期检测”,但实际上它一开始就没有这个功能。人们只是假设它可以做到这一点,并且在某些情况下它有效,而在许多情况下却没有。

这是一个说明问题的示例。

 var s = "01.02.03";

那是约会吗?可能是。也许不吧。它可以是文档中的部分标题。即使我们说这是约会,那是什么日子?它可以解释为以下任何一种:

  • 2003 年 1 月 2 日
  • 0003 年 1 月 2 日
  • 2003 年 2 月 1 日
  • 0003 年 2 月 1 日
  • 2001 年 2 月 3 日
  • 0001 年 2 月 3 日

消除歧义的唯一方法是了解当前的文化日期设置。 Javascript 的Date 对象就是这样做的——这意味着您将根据运行代码的机器的设置获得不同的值。然而,moment.js 是关于所有环境的稳定性。通过 moment 自己的语言环境功能,文化设置是明确的。依赖浏览器的文化设置会导致解释错误。

最好的办法是明确说明您正在使用的格式。不允许随机垃圾输入。期待您以特定格式输入,并使用正则表达式提前验证该格式,而不是仅仅尝试构造 Date 并在事后查看它是否有效。

如果您不能这样做,则必须找到其他上下文来帮助您做出决定。例如,如果您正在从后端进程中抓取 Web 的一些随机位,并且想要从文本中提取日期,则您必须了解每个特定网页的语言和区域设置。你可以猜到,但在相当多的时间里你可能会错。

另请参阅:Garbage in, garbage out

【讨论】:

  • 感谢您的回复,基本上我根本不需要依赖momentjs。我可以使用 Date 并且会非常满意。关键是 Firefox 的实现正在运行,而 Chrome 的实现已经坏了。
  • @VladimirStarkov 我不会说 chrome 坏了,它只是试图解析输入字符串
  • @maioman 可能没有坏,但它的工作做得不够好
  • @VladimirStarkov 不知道子集就像在黑暗中爬行;但是如果你用setFullYearsetMonthsetDate分割字符串并将它提供给Date对象实例,你应该有一个更一致的结果,看看fiddle
【解决方案3】:

ES5 15.9.4.2 Date.parse: /.../ 如果String不符合 该函数的格式可以回退到任何特定于实现的 启发式或特定于实现的日期格式。面目全非 格式中包含非法元素值的字符串或日期 字符串应导致 Date.parse 返回 NaN。

所以没关系,根据上面 v8 日期解析器的引用结果:

  1. new Date('asd qw 101') : 星期六 1 月 1 日 101 00:00:00 GMT+0100 (中欧)
  2. new Date('asd qw') : 无效日期

【讨论】:

  • 抱歉,我不明白为什么是Sat jan 01 101 而不是NaN NaN 101
  • @VladimirStarkov 因为NaN 不是日期的有效部分。
  • @VladimirStarkov:因为日期不由部分组成,它们只是时间戳。 asd qw 101 被解析为代表 101 年第二个零的那个。
  • @royhowie 这部分承诺 parse 将返回 NaN “无法识别的字符串或包含非法元素值的日期字符串格式应导致 Date.parse 返回 NaN。”
  • @VladimirStarkov 因为any implementation-specific heuristics or implementation-specific date formats
猜你喜欢
  • 2016-02-19
  • 1970-01-01
  • 2019-05-30
  • 1970-01-01
  • 1970-01-01
  • 2017-09-29
  • 1970-01-01
  • 1970-01-01
  • 2014-11-14
相关资源
最近更新 更多