【问题标题】:How to force Date constructor to accept format on IE如何强制日期构造函数接受 IE 上的格式
【发布时间】:2017-09-14 10:46:09
【问题描述】:

我有一个提供以下格式日期的 API:

yyyy-MM-dd'T'HH:mm:ssZ,例如: 2017-04-18T11:18:05-0300。 Chrome 可以从这样的字符串构建日期,但 IE 不能。有没有办法可以强制 IE 也接受这种格式,解决这个问题的最简单方法是什么?

我尝试了类似下面的 sn-p 但它不起作用

(function() {

var originalDateFn = Date;

var month_names_short =  ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

Date = function(date) {
   var splitted = splitIfOnApiFormat(date);
   if(splitted !== null) {
     return new Date( month_names_short[splitted[2]] + ' ' +  splitted[3] + ' ' +  splitted[1] + ' ' + splitted[4] +' GMT'+ splitted[5]);
   }
   return originalDateFn(date);
};

function splitIfOnApiFormat(date) {
  return typeof date === 'string' && date.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}\:\d{2}\:\d{2})([-+]\d{4})$/);
}



})();

【问题讨论】:

  • 在 JS 中处理日期时,我建议使用 moment.js。 momentjs.com
  • 实际上我确实使用了 momentjs,但日期输入组件使用 Date 本身,所以在我的情况下,让它在 IE 上也能工作是一种需要的解决方法
  • 如果所有平台都正确实现了所有标准,那么像 moment.js 这样的库很可能永远不会启动。只是说。什么“不应该”和什么是必要的通常相差很远。
  • @gforce301——但有许多浏览器是在当前标准要求正确解析 ISO 8601 字符串之前创建的。

标签: javascript date gmt


【解决方案1】:

这种情况下的问题是最后偏移部分的小时和分钟之间缺少:。在这方面,IE 有权将其报告为无效日期,因为它不符合definition of date in the spec,即YYYY-MM-DDTHH:mm:ss.sssZYYYY-MM-DDTHH:mm:ss.sss±hh:mm带有冒号)。

您可以通过以下方式解决此问题:

const date = "2017-04-18T11:18:05-0300";

function fixDate(date) {
  return date.replace(/(\d\d)(\d\d)$/, 
    function(_, hh, mm) { return hh + ':' + mm; });
}

console.log(new Date(fixDate(date)));

【讨论】:

  • 非常感谢你的朋友,比我下面的答案更简单、更正确,实际上我会更改 API 以提供这种格式的日期。完美,因为您还提到了我不知道的规范
【解决方案2】:

黄金法则是从不使用内置解析器解析字符串(即使用 Date 构造函数或 Date.parse)。您已经将字符串分成几部分,因此只需将它们传递给 Date 构造函数并根据时区进行调整。

以下将解析 ECMA-262 中指定的 ISO 8601 扩展格式字符串,希望 cmets 就足够了。

/* Parse string in ISO 8601 extended format: YYYY-MM-DDTHH:mm:ss.sssZ.
 ** Date only strings are treated as local (per ISO 8601)
 **
 ** @param {string} s - string to parse. Can be just a date, date and time,
 **                     or date, time and timezone.
 **                     Separator can be T or " " (space) 
 **                     Date string must be at least year and month: YYYY-MM 
 **                     All time parts are optional, if time provided then
 **                     date must have year, month and day.
 **                     All time parts are optional.
 **                     Timezone, if present, must be Z or z or +/-HH:mm or +/-HHmm
 **
 ** @returns {Date}   - if s is a invalid date, contains invalid values,
 **                     or is an invalid format, returns an invalid Date
 */
function parseISO(s) {

  // Create base Date object
  var date = new Date();
  date.setHours(12, 0, 0, 0);
  var invalidDate = new Date(NaN);

  // Set some defaults
  var sign = -1,
    tzMins = 0;
  var tzHr, tzMin;

  // Trim leading and trailing whitespace
  s = s.replace(/^\s*|\s*$/g, '').toUpperCase();

  // Get parts of string and split into numbers
  var d = (s.match(/^\d+(-\d+){0,2}/) || [''])[0].split(/\D/);
  var t = (s.match(/[\sT]\d+(:\d+){0,2}(\.\d+)?/) || [''])[0].split(/\D/);
  var tz = (s.match(/Z|[+\-]\d\d:?\d\d$/) || [''])[0];

  // Resolve timezone to minutes, may be Z, +hh:mm or +hhmm
  // substr is old school but more compatible than slice
  // Don't need to split into parts but makes validation easier
  if (tz) {
    sign = /^-/.test(tz) ? 1 : -1;
    tzHr = tz == 'Z' ? 0 : tz.substr(1, 2);
    tzMin = tz == 'Z' ? 0 : tz.substr(tz.length - 2, 2) * 1;
    tzMins = sign * (tzHr * 60 + tzMin);
  }

  // Validation
  function isLeap(year) {
    return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
  }

  // Check number of date parts and month is valid
  if (d.length > 3 || d[1] < 1 || d[1] > 12) return invalidDate;

  // Test day is valid
  var monthDays = [, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  var monthMax = isLeap(d[0]) && d[1] == 2 ? 29 : monthDays[+d[1]];
  if (d[2] < 1 || d[2] > monthMax) return invalidDate;

  // Test time parts
  if (t.length > 5 || t[1] > 23 || t[2] > 59 || t[3] > 59 || t[4] > 999) return invalidDate;

  // Test tz within bounds
  if (tzHr > 12 || tzMin > 59) return invalidDate;

  // If there's a timezone, use UTC methods, otherwise local
  var method = tz ? 'UTC' : '';

  // Set date values
  date['set' + method + 'FullYear'](d[0], (d[1] ? d[1] - 1 : 0), d[2] || 1);
  // Set time values - first memeber is '' from separator \s or T
  date['set' + method + 'Hours'](t[1] || 0, (+t[2] || 0) + tzMins, t[3] || 0, t[4] || 0);

  return date;
}

// Some tests
['2017-04-18T11:18:05-0300',  // No colon in timezone
  '2017-04-18 11:18:05-0300', // Space separator instead of T
  '2016-04-12T04:31+05:30',   // Colon in timezone
  '2016-02-29T04:31+05:30',   // Colon, leap year
  '2016-04-12T04:31:56.004Z', // GMT
  '2000-02-29T04:31+05:30',   // Colon, leap year
  '1900-02-29T04:31+05:30',   // Colon, not leap year (invalid date)
  '2016-04-12T04:31:56.004',  // Local
  '2016-04-12',               // Date only (local)
  '2016-04'                   // Minimal date (local)
].forEach(function(d) {
  console.log(d + ': ' + parseISO(d).toString());
});

或者,使用库,有很多好的可供选择。

【讨论】:

  • 例外情况是,如果您确定输入是YYYY-MM-DDThh:mm:ss.sssZYYYY-MM-DDThh:mm:ss.sss±hh:mm 形式,则可以安全地使用Date.parsenew Date
  • @torazaburo——我还是不相信它。内置解析器有很多怪癖,最好不要使用它们——当然是恕我直言。 ;-)
【解决方案3】:

好的,知道了 对于其他可能需要的人,以下代码修复了它:

(function() {

var originalDateFn = Date.prototype.constructor;

var month_names_short =  ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

Date = function(date) {
   var splitted = splitIfOnApiFormat(date);
   if(splitted !== null) {
     return new originalDateFn( month_names_short[(Number(splitted[2])) -1] + ' ' +  splitted[3] + ' ' +  splitted[1] + ' ' + splitted[4] +' GMT'+ splitted[5]);
   }
   return new originalDateFn(date);
};

function splitIfOnApiFormat(date) {
  return typeof date === 'string' ? date.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}\:\d{2}\:\d{2})([-+]\d{4})$/) : null;
}



})();

【讨论】:

  • 不!解析字符串后,直接将部分提供给构造函数,不要创建另一个必须解析的字符串。见Why does Date.parse give incorrect results?
  • 如果有小数秒,您的正则表达式将失败。
猜你喜欢
  • 2023-01-25
  • 2018-01-12
  • 1970-01-01
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多