如何强制日期构造函数在 IE 上接受格式

How to force Date constructor to accept format on IE

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

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

我尝试了类似下面代码片段的方法,但没有成功

(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})$/);
}



})();

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

(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;
}



})();

这种情况下的问题是最后的偏移部分中的小时和分钟之间缺少 :。在这方面,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)));

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

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

/* 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());
});

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