跨浏览器和面向未来的从ISO 8601格式中提取日期的方法



我们正在使用facebook的API来获取一些帖子及其相应的日期,并将它们显示在我们构建的网站上。Facebook API 将按以下格式返回日期:

2016-05-22T10:38:38+0000

我已经确定这是ISO 8601格式。直到最近,我们还使用类似于以下内容的代码来提取 mm/dd/yyyy 格式的可读日期:

//created_time = '2016-05-22T10:38:38+0000'
var date = new Date(created_time).toLocaleDateString();

但是,我们注意到这在iOS safari中不起作用,所有解决方案都包括拆分字符串并将其解析为Date()函数。

我认为像这样简单而肮脏的东西应该有效:

var getDate = function(date) {
    var day   = date.substr(8, 2);
    var month = date.substr(5, 2);
    var year  = date.substr(0, 4);
    return [day, month, year].join('/');
}

但我的同事对其未来的证明性持保留态度。如果Facebook决定更改其返回日期的格式怎么办?完全合理的关注。那么任何人都可以推荐一种编程的跨浏览器方法来从该字符串返回可读的 dd/mm/yyyy 格式日期吗?谢谢。

如果您不知道字符串的格式,则无法自信地解析它。您可以猜测格式并生成日期,但即使您最终得到一个有效日期,您仍然不知道是否正确解析了它。无论如何,您都应该手动解析字符串(库可以提供帮助,但如果您只有一种格式,一个简单的函数就足够了)。

以下内容将可靠地解析 ISO 8601 扩展格式的日期和时间字符串。

/* Parse ISO date string in format yyyy-mm-ddThh:mm:ss.sss+hh:mm or Z
**
** @param (string} s - string to parse in ISO 8601 extended format
**                     yyyy-mm-ddThh:mm:ss.sss+/-hh:mm or z
**                     Ttime zone separator can be omitted,
**                       e.g. +05:30 or +0530
**                     Date and time separator "T" can be replaced by space
**                       e.g. 2016-04-23 12:00:00-0700
**                     Date only form (2016-04-30) is treated as local (per ISO 8601,
**                     but ECMA-262 requires as GMT (Z or 0 offset).
**
** @returns {Date}   - returns a Date object. If any value out of range,
**                     returns an invalid date.
*/
function parseISO(s) {
  // Create base Date object
  var date = new Date();
  // Object to return if invalid date
  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|[+-]dd:?dd$/)          || [''])[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[1] > 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 member 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;
}
['2016-04-12T04:31:56.200+05:30','2016-02-01','2016-01-01 17:51:12-0700','2016-01-01T17:51:12Z'].forEach(function (s) {
  var d = parseISO(s);
  document.write('String: ' + s + '<br>' +
                 'Local time: ' + d + '<br>' +
                 'toISOString: ' + d.toISOString() + '<br><br>'
                );
 });

以上可以减少到更少的代码,它旨在易于阅读和维护,更多的代码并不一定意味着它比较短的版本慢(尽管缓存正则表达式会有所帮助如果性能是一个问题)。

最新更新