使用一致的时区解释解析javascript日期



假设我有两种不同类型的日期字符串-mysql日期戳和mysql日期+时间戳。

"2015-12-25"
"2015-12-25 00:00:00"

当我在JavaScript中解析这些时,我得到:

new Date("2015-12-25");
Thu Dec 24 2015 19:00:00 GMT-0500 (Eastern Standard Time)
new Date("2015-12-25 00:00:00");
Fri Dec 25 2015 00:00:00 GMT-0500 (Eastern Standard Time)

或者,在UTC中:

new Date("2015-12-25").toUTCString();
"Fri, 25 Dec 2015 00:00:00 GMT"
new Date("2015-12-25 00:00:00").toUTCString();
"Fri, 25 Dec 2015 05:00:00 GMT"

在末尾添加时间会使日期解析为我的时区提前了几个小时。我想要的是某种函数,使两者解析为相同的东西-GMT或否,我可以调整,只要结果是已知的,无论传入的日期字符串是什么。

我已经实现了一个涉及正则表达式和检测日期格式的解决方案的一半,然后我发现可能有更好的方法来实现这一点。

我已经找到了一些涉及Date.prototype.getTimezoneOffset()的解决方案,但对于这两个返回相同内容的日期,因为它们被解析为在同一时区。

有什么想法吗?

ECMAScript ed 5.1(又名ES5)要求将ISO 8601日期字符串解析为UTC,但ISO要求将其解析为本地字符串。现在,当前的ECMAScript标准(版本6)与ISO一致,并且要求符合ISO且没有时区的字符串被解析为本地字符串。

<更新>ECMAScript 2016(第7版)再次更改,使得YYYY-MM-DD格式的日期字符串被解析为UTC,因此不再与ISO 8601一致。<更新>。

因此,你是否会有这种行为取决于浏览器(如果你用IE 8尝试,你会得到NaN)。在ES5之前,所有字符串的解析依赖于实现。

"2015-12-25"是一个有效的ISO 8601字符串,没有时区,因此在ES5规则下应被视为UTC,在ED6规则下应视为本地。

"2015-12-25 00:00:00"不是有效的ISO字符串(日期和时间之间缺少"T"),因此浏览器可以随心所欲地解析它(在ECMAScript的所有版本中,解析非ISO字符串取决于实现)。

底线是不要使用Date构造函数来解析字符串(或Date.parse)。自己解析它们。

要解析类似ISO的字符串,请使用以下内容。它将没有偏移量的字符串处理为UTC(根据ES5),但现在可能应该将其更改为本地:

/**
 * Parse an ISO string with or without an offset
 *   e.g. '2014-04-02T20:00:00-0600'
 *        '2014-04-02T20:00:00Z'
 *        '2014-02'
 *
 * Allows decimal seconds if supplied
 *   e.g. '2014-04-02T20:00:00.123-0600'
 *
 * If no offset is supplied (or it's Z), treat as UTC (per ECMA-262)
 *
 * If date only, e.g. '2014-04-02' or '2014-02', treat as UTC date (per ECMA-262)
 * All parts after year are optional
 * Don't allow two digit years to be converted to 20th century years
 * @param {string} s - ISO 860 date string
*/
function parseISOString(s) {
  var invalidDate = new Date(NaN);
  var t = s.split(/D+/g);
  var hasOffset = /[-+]d{4}$/.test(s);
  // Whether decimal seconds are present changes the offset field and ms value
  var hasDecimalSeconds = /[T ]d{2}:d{2}:d{2}.d+/i.test(s);
  var offset = hasDecimalSeconds? t[7] : t[6];
  var offSign;
  var yr  = +t[0],
      mo  = t[1]? --t[1] : 0,
      da  = +t[2] || 1,
      hr  = +t[3] || 0,
      min = +t[4] || 0,
      sec = +t[5] || 0,
      ms  = hasDecimalSeconds? +t[6] : 0,
      offSign = hasOffset? /-d{4}$/.test(s)? 1 : -1 : 0,
      offHr   = hasOffset? offset/100 | 0 : 0,
      offMin  = hasOffset? offset%100 : 0;
  // Ensure time values are in range, otherwise invalid date.
  // Values can't be -ve as splitting on non-digit character
  if (hr > 24 || min > 59 || sec > 59 || ms > 1000 || offHr > 24 || offMin > 59){
    return invalidDate;
  }
  // Create a date object from date parts, check for validity
  // Avoid two digit years being converted to 20th century
  var d = new Date();
  d.setUTCFullYear(yr, mo, da);
  // Check that date values are valid
  if (d.getUTCFullYear() != yr || d.getUTCDate() != da) {
    return invalidDate;
  }
  // If there's an offset, apply it to minutes to get a UTC time value
  min = hasOffset? +min + offSign * (offHr * 60 + offMin) : min;
  // Set UTC time values of d
  d.setUTCHours(hr, min, sec, ms);
  return d;
}

它可以减少到一半以下,但它写得很容易阅读和维护。

最新更新