【问题标题】:JavaScript: Which browsers support parsing of ISO-8601 Date String with Date.parseJavaScript:哪些浏览器支持使用 Date.parse 解析 ISO-8601 日期字符串
【发布时间】:2011-08-13 17:35:59
【问题描述】:

我未能在 IE8 和 Safari 5 上解析 ISO-8601 日期“2011-04-26T13:16:50Z”,但它在 Chrome 10、FF4 上有效。支持似乎很复杂?

有谁知道哪些浏览器可以解析这种格式的实际状态?我假设 IE6 和 7 也会失败。

var d = Date.parse("2011-04-26T13:16:50Z");

【问题讨论】:

  • 可能我们无法弄清楚这一点,因为没有很好地发布 ECMAScript-262v5 支持。有用链接:Mozilla DocsW3 datetime

标签: javascript internet-explorer datetime safari


【解决方案1】:

我只在需要通过一些测试时才说它,

这是我已经写过的一个:

(function() {

var d = window.Date,
    regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,3})(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;

if (d.parse('2011-11-29T15:52:30.5') !== 1322581950500 ||
    d.parse('2011-11-29T15:52:30.52') !== 1322581950520 ||
    d.parse('2011-11-29T15:52:18.867') !== 1322581938867 ||
    d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
    d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
    d.parse('2011-11-29') !== 1322524800000 ||
    d.parse('2011-11') !== 1320105600000 ||
    d.parse('2011') !== 1293840000000) {

    d.__parse = d.parse;

    d.parse = function(v) {

        var m = regexIso8601.exec(v);

        if (m) {
            return Date.UTC(
                m[1],
                (m[2] || 1) - 1,
                m[3] || 1,
                m[4] - (m[8] ? m[8] + m[9] : 0) || 0,
                m[5] - (m[8] ? m[8] + m[10] : 0) || 0,
                m[6] || 0,
                ((m[7] || 0) + '00').substr(0, 3)
            );
        }

        return d.__parse.apply(this, arguments);

    };
}

d.__fromString = d.fromString;

d.fromString = function(v) {

    if (!d.__fromString || regexIso8601.test(v)) {
        return new d(d.parse(v));
    }

    return d.__fromString.apply(this, arguments);
};

})();

并且在您的代码中始终使用Date.fromString(...) 而不是new Date(...)

测试浏览器以查看是否会使用 shim:

http://jsbin.com/efivib/1/edit

适用于所有主流浏览器,使用了以下参考:

http://dev.w3.org/html5/spec/common-microsyntaxes.html

http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15

http://msdn.microsoft.com/en-us/library/windows/apps/ff743760(v=vs.94).aspx

http://msdn.microsoft.com/en-us/library/windows/apps/wz6stk2z(v=vs.94).aspx

http://msdn.microsoft.com/en-us/library/windows/apps/k4w173wk(v=vs.94).aspx

!- microsoft connect 需要登录才能查看:

IE9 以毫秒为单位失败,位数不是 3:(已在 IE10 中修复) https://connect.microsoft.com/IE/feedback/details/723740/date-parse-and-new-date-fail-on-valid-formats

当时区被省略时,IE10 仍然(截至 2013 年 1 月 17 日)失败(根据 ECMA,这应该默认为 Z 或 UTC,而不是本地): https://connect.microsoft.com/IE/feedback/details/776783/date-parse-and-new-date-fail-on-valid-formats

-- 如果您关心标准现在/未来的发展方向以及为什么我无法让 IE 团队认识到他们的 IE10 实施在技术上不正确,请阅读此内容:

ECMAScript-262 v6.0 将移至更符合 iso8601 的版本“如果省略时区指示符,则假定为本地时间”......所以现在存在差异,这个实现,chrome,移动 safari和 opera 都遵循 ECMAScript-262 v5.1,而 IE10、firefox、桌面 safari 似乎都遵循更符合 iso8601 的 ECMAScript-262 v6.0 规范……这至少可以说是令人困惑的。当 chrome 或移动 safari 触发并转移到 ES6 实现时,我认为这个实现应该与它一起使用,而 ES5.1 则占少数。我读过这在 5.1 版的“勘误表”中列出,但我还没有找到。我更认为现在在 ES6 上扣动扳机还为时过早,但我也认为代码需要实用,不理想,并转移到浏览器制造商转移的地方。也就是说,现在似乎是一个 50/50 的决定,所以下面是这段代码的“未来”版本......

我还应该提到,任何一个版本的代码都会规范“不兼容”浏览器以匹配另一个版本的行为,因为这就是 shims 所做的;)

这是一个与 ECMAScript-262 v6.0(JavaScript 未来)兼容的改编版本

在此处查看相关部分:(这是我能找到的唯一在线 html 规范版本)http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.9.1.15

(function() {

    var d = window.Date,
        regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/,
        lOff, lHrs, lMin;

    if (d.parse('2011-11-29T15:52:30.5') !== 1322599950500 ||
        d.parse('2011-11-29T15:52:30.52') !== 1322599950520 ||
        d.parse('2011-11-29T15:52:18.867') !== 1322599938867 ||
        d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
        d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
        d.parse('2011-11-29') !== 1322524800000 ||
        d.parse('2011-11') !== 1320105600000 ||
        d.parse('2011') !== 1293840000000) {

        d.__parse = d.parse;

        lOff = -(new Date().getTimezoneOffset());
        lHrs = Math.floor(lOff / 60);
        lMin = lOff % 60;

        d.parse = function(v) {

            var m = regexIso8601.exec(v);

            if (m) {
                return Date.UTC(
                    m[1],
                    (m[2] || 1) - 1,
                    m[3] || 1,
                    m[4] - (m[8] ? m[9] ? m[9] + m[10] : 0 : lHrs) || 0,
                    m[5] - (m[8] ? m[9] ? m[9] + m[11] : 0 : lMin) || 0,
                    m[6] || 0,
                    ((m[7] || 0) + '00').substr(0, 3)
                );
            }

            return d.__parse.apply(this, arguments);

        };
    }

    d.__fromString = d.fromString;

    d.fromString = function(v) {

        if (!d.__fromString || regexIso8601.test(v)) {
            return new d(d.parse(v));
        }

        return d.__fromString.apply(this, arguments);
    };

})();

希望这有助于 -ck

【讨论】:

  • 对于没有秒和毫秒的日期,您的正则表达式无法正常工作。也许改用this blogpost 的正则表达式。
  • @klaaspieter 感谢您的反馈,您是正确的,我确实错过了 Thh:mm 和 Thh:mm:ss 的变体,但是您断言我应该“从这篇博文中使用”是非常错误的,他正在做一些与我不同的事情,他正在尝试匹配 ISO8601 的完整标准,我正在尝试创建一个 shim 来模拟 ECMA 概述并在其他浏览器中演示的正确行为,这是 ISO8601 的一个子集。令人困惑,我知道,正则表达式不符合我的目的,但我会更正我自己的,以包括我错过的一些边缘案例
  • 我认为这个 shim 可能过于渴望应用自身 - 如果您不在 GMT TZ 中,那么将没有 TZ 指示的日期解析为本地时间不会产生单个纪元值 - 而这个脚本假设它会例如行 d.parse('2011-11-29T15:52:18.867') !== 1322599938867
  • @ckozl 当然,我明白这一点。但是,如果您在符合 v6 的浏览器上运行该 shim,它将适用,因为它将非 TZ 解析为与常量不匹配的本地时间。在符合 v6 的实现中,我建议这个 shim 应该激活
  • @AlexDixon 谢谢,错过了那个.. 你对两位数日期的正确 Date.UTC 解释超级有用
【解决方案2】:

我今天遇到了这个问题。我发现momentjs 是在跨浏览器庄园中解析 ISO 8601 日期的好方法。

momentjs 也可以用来输出不同格式的日期。

【讨论】:

  • 关心性能的人,小心MomentJS
【解决方案3】:

在任何浏览器中解析 ISO8601 日期格式的简单函数:

function dateFromISO8601(isoDateString) {
  var parts = isoDateString.match(/\d+/g);
  var isoTime = Date.UTC(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);
  var isoDate = new Date(isoTime);

  return isoDate;
}

【讨论】:

    【解决方案4】:

    是的,Date.parse 对于不同的浏览器是不一致的。你可以:

    【讨论】:

    • 请注意,它是 jQuery UI datepicker 的 parseDate。原生 jQuery 没有日期解析库。虽然我认为它非常需要一个。 Javascript 的日期实现很烂!
    • 另一个注意事项:jQuery UI 的 parseDate 方法实际上只解析日期——它根本不处理时间。
    【解决方案5】:

    如果您解析 ISO 日期字符串,一些旧版浏览器会返回 错误 日期(而不是 NaN)。

    您可以在所有浏览器中使用自己的方法,或者如果正确实现,则使用 Date.parse- 检查一个已知的时间戳。

    Date.fromISO= (function(){
        var diso= Date.parse('2011-04-26T13:16:50Z');
        if(diso=== 1303823810000) return function(s){
            return new Date(Date.parse(s));
        }
        else return function(s){
            var day, tz, 
            rx= /^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/, 
            p= rx.exec(s) || [];
            if(p[1]){
                day= p[1].split(/\D/).map(function(itm){
                    return parseInt(itm, 10) || 0;
                });
                day[1]-= 1;
                day= new Date(Date.UTC.apply(Date, day));
                if(!day.getDate()) return NaN;
                if(p[5]){
                    tz= parseInt(p[5], 10)*60;
                    if(p[6]) tz += parseInt(p[6], 10);
                    if(p[4]== "+") tz*= -1;
                    if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
                }
                return day;
            }
            return NaN;
        }
    })()
    

    【讨论】:

    • 这是一个处理更多版本的rx = /^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d{3}))?$/,,如下所示:jsfiddle.net/mplungjan/QkasD
    • 以下codevar diso = Date.parse('2012-06-25T22:19:43.4859618Z'); if (diso === 1340662783485) return function (s) {code 更好地检测 IE 9 的检查
    【解决方案6】:

    ES5 规范偏离了 ISO8601 规范,尤其是在处理没有时区指示符/偏移量的日期时。 https://bugs.ecmascript.org/show_bug.cgi?id=112 有一个 bug 票,描述了这个问题,看起来它会在 ES6 中修复。

    目前,我建议查看https://github.com/csnover/js-iso8601 以了解跨浏览器的实现。我使用https://github.com/csnover/js-iso8601/tree/lax,它不符合 ES5 规范,但与 JSON.NET 等其他 JSON 序列化库具有更好的互操作性。

    【讨论】:

      【解决方案7】:

      我发现 ckozl 答案非常有用且有趣,但正则表达式并不完美,在我的情况下也不起作用。

      除了不解析没有分钟、秒或毫秒的日期之外,ISO 8501 规范规定“-”和“:”分隔符是可选的,因此“2013-12-27”和“20131227”都是有效的。就我而言,这很重要,因为我在 PHP 的 JavaScript 变量中设置服务器日期和时间:

      var serverDate = new Date(Date.parse("<?php date(DateTime::ISO8601); ?>"));
      

      此代码生成如下内容:

      <script>
      var serverDate = new Date(Date.parse("2013-12-27T15:27:34+0100"));
      </script>
      

      重要的部分是缺少“:”的时区指示符“+0100”。尽管 Firefox 正确解析了该字符串,但 IE (11) 失败(如果添加了 ':',则 IE 也可以工作)。 ckozl 描述的 zonetime 和 ECMAScript 规范对我来说并不重要,因为 PHP 总是添加时区指示符。

      我正在使用的 RegExp,而不是来自 ckozl 的那个是:

      var regexIso8601 = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/;
      

      请记住,这个正则表达式也不完美。 ISO 8501 允许指定星期(2007-W01-1 表示星期一,2007 年 1 月 1 日)或小时和分钟的小数部分(18.50 表示 18:30:00,或 18:30.25 表示 18:30:15)。但它们很不寻常。

      P.D.我想这个答案应该是对原始 chozl 答案的评论,但我没有足够的声誉:(

      【讨论】:

      • 与 PHP 不输出列的相同问题。以下转换似乎有效。不确定它是否在任何情况下都有效 PHP 可以输出 string.substr(0, string.length- 2)+':'+string.substr(string.length- 2, 10);
      【解决方案8】:

      如前所述,在 ECMAScript 版本 5 中添加了 ISO 8601 样式日期,其中的实现不一致,并且并非在所有浏览器中都可用。有一个 number of 脚本存根可用,但您可能希望简单地添加自己的 Date.parse* 方法。

      (function() {
        //ISO-8601 Date Matching
        var reIsoDate = /^(\d{4})-(\d{2})-(\d{2})((T)(\d{2}):(\d{2})(:(\d{2})(\.\d*)?)?)?(Z|[+-]00(\:00)?)?$/;
        Date.parseISO = function(val) {
          var m;
      
          m = typeof val === 'string' && val.match(reIsoDate);
          if (m) return new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[6] || 0, +m[7] || 0, +m[9] || 0, parseInt((+m[10]) * 1000) || 0));
      
          return null;
        }
      
        //MS-Ajax Date Matching
        var reMsAjaxDate = /^\\?\/Date\((\-?\d+)\)\\?\/$/;
        Date.parseAjax = function(val) {
          var m;
      
          m = typeof val === 'string' && val.match(reMsAjaxDate);
          if (m) return new Date(+m[1]);
      
          return null;
        }
      }();

      我使用上述方法进行 JSON.parse 日期的水合...

      JSON.parse(text, function(key, val) {
        return Date.parseISO(val) || Date.parseAjax(val) || val;
      });

      【讨论】:

      • 您的正则表达式失败:"2016-10-24T20:50:06+00:00"。来源:en.wikipedia.org/wiki/ISO_8601
      • @TheDembinski 是的,它只支持 UTC/Z 日期时间解析...... ES5 shim 将支持它......在这一点上,除非你需要支持 IE
      • 我想我不明白 - 出于某种原因,我觉得 iso8601 是那种格式。就像,它们在我心目中是同义词。
      • 已更新以允许 [+-]00:00,无论如何,您可能只需删除尾部检查,它应该可以工作......这些天我只是做 new Date(输入)如果 yyyy-mm-dd 部分在那里......因为所有当前的浏览器都直接支持该格式,并且已经有很长时间了。
      • 你是舞会上最英俊的男孩/女孩 :] 谢谢你这样做。
      【解决方案9】:

      ISO 8601 日期格式已添加到 ECMAScript-262 v5。因此,如果浏览器不兼容 v5,您根本无法期望能够处理 ISO 8601 格式。

      不兼容 v5 的浏览器可以使用他们想要的任何特定于实现的日期格式。不过,它们中的大多数至少支持RFC822/RFC1123 日期格式。示例:

      var d = Date.parse("Wed, 26 Apr 2011 13:16:50 GMT+0200");
      

      【讨论】:

      • 接受 T24:00 作为有效值也是一个问题。 FF、Opera 和 IE9 在这方面确实遵循 ECMAScript 2011 标准(和 ISO8601 5.3.2)并接受 T24:00; Chrome 和 Safari 不合规并报告无效日期。
      • @Tim—ES5 需要支持仅日期形式,但不支持仅时间形式。 Section 15.9.1.15 说:It also includes “date-time” forms that consist of one of the above date-only forms immediately followed by one of the following time forms with an optional time zone offset appended
      • @Rob:很抱歉我的表述不够清晰,但我并不是建议使用仅限时间的表格。我指的是时间元素为T24:00 的日期时间值。第 15.9.1.15 节说: 由于每一天都以午夜开始和结束,因此可以使用 00:00 和 24:00 这两个符号来区分可以与一个日期相关联的两个午夜。这意味着以下两个符号指的是完全相同的时间点:1995-02-04T24:00 和 1995-02-05T00:00
      【解决方案10】:

      Microsoft Sharepoint 2013 也使用不同的表示法,例如“2013-04-30T22:00:00Z”

      如果您想结合 Internet Explorer 8 (IE8) 使用 sharepoint 2013 中的 REST 服务,那么来自 ckozl 的解决方案不起作用。 你会得到 NaN

      将正则表达式行更改为:

      regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})(\.(\d{1,3}))?(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;
      

      这将使微秒位可选!

      利奥

      【讨论】:

        猜你喜欢
        • 2021-01-17
        • 1970-01-01
        • 1970-01-01
        • 2011-06-17
        • 2012-11-28
        • 2013-05-14
        • 1970-01-01
        • 1970-01-01
        • 2012-11-22
        相关资源
        最近更新 更多