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

is there any workaround for broken v8 date parser?

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) and also moment cant help me because moment is 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

ES5 15.9.4.2 Date.parse: /.../ If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats. Unrecognizable Strings or dates containing illegal element values in the format String shall cause Date.parse to return NaN.

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

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

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

你说moment.js是"getting rid of date detection",其实它本来就没有这个功能。人们只是假设它可以做到这一点,并且在某些情况下它有效,而在许多情况下它没有。

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

 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 object 就是这样做的——这意味着您将根据代码为 运行 的机器的设置获得不同的值。但是,moment.js 是关于所有环境的稳定性。通过 moment 自己的语言环境功能,文化设置是明确的。依赖浏览器的区域性设置会导致解释错误。

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

如果您不能做到这一点,您将不得不寻找其他背景来帮助做出决定。例如,如果您从 back-end 进程中抓取一些随机的网络位,并且想从文本中提取日期,则您必须对每个特定网页的语言和区域设置有一些了解.您可能会猜到,但很多时候您可能会猜错。

另请参阅:Garbage in, garbage out

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

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

来自docs,

dateString in new Date(dateString) is a string value representing a date. The string should be in a format recognized by the Date.parse() method (IETF-compliant RFC 2822 timestamps and also a version of 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;
        }
      }

    }
...

对于像 'asd qw 101' 这样的字符串,DateParse() 似乎没有 return NaN,因此出现了错误。您可以在 Chrome(v8) [which returns -58979943000000] 和 Gecko (Firefox) [return 是一个 NaN]。 Sat Jan 01 101 00:00:00 当您使用 -58979943000000 的时间戳播种 new Date() 时出现(在两种浏览器中)

is there any workaround for broken v8 date parser?

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

在 Chrome(V8) 和 Firefox(Gecko) 中尝试 new Date('Sun Ma 10 2015') 另一个这样的异常。 这里 chrome 无法决定天气 'Ma' 代表 'March' 或 'May' 并给出一个 无效日期 而 Firefox 没有。

解决方法:

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

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

验证字符串
^(?:(?:31(\/|-|\. |\s)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))|(?:(?:29|30)(\/|-|\.|\s)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.|\s)(?:0?2|(?:Feb))(?:(?:(?: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)))(?:(?:1[6-9]|[2-9]\d)?\d{2})$

debuggex

生成的图像

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

希望对您有所帮助!