Javascript日期解析在Chrome中返回奇怪的结果

时间:2019-05-09 10:12:50

标签: javascript google-chrome date

我在Chrome浏览器中发现了一些奇怪的Date行为(版本74.0.3729.131(官方内部版本)(64位))。 在Chrome开发者控制台中执行了以下javascript:

new Date('1894-01-01T00:00:00+01:00')
// result: Mon Jan 01 1894 00:00:00 GMT+0100 (Central European Standard Time)

new Date('1893-01-01T00:00:00+01:00')
// result: Sat Dec 31 1892 23:53:28 GMT+0053 (Central European Standard Time)

尽管提供了有效的ISO8601值,但我已经通过Date ctor在不同的浏览器中阅读了有关非标准日期解析的信息。 但这不仅仅是奇怪的o_o

在Firefox(Quantum 66.0.3(64位))中,相同的调用会导致预期的Date对象:

new Date('1894-01-01T00:00:00+01:00')
// result: > Date 1892-12-31T23:00:00.000Z

new Date('1893-01-01T00:00:00+01:00')
// result: > Date 1893-12-31T23:00:00.000Z
  • 这是Chrome中的错误吗?
  • 我输入的内容是有效的ISO8601吗?
  • 最重要的问题是,如何解决此问题? (希望自己不解析输入字符串)

2 个答案:

答案 0 :(得分:2)

好的,似乎无法避免这种现象,因此您应该手动解析日期。但是解析它的方法非常简单。

如果我们以ISO 8601格式解析日期,则日期字符串的掩码如下所示:

<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>(.<ms>)?(Z|(+|-)<hh>:<mm>)?

1。分别获取日期和时间

字符串T中的日期和时间是分开的。因此,我们可以将ISO字符串除以T

var isoString = `2019-05-09T13:26:10.979Z`
var [dateString, timeString] = isoString.split("T")

2。从日期字符串中提取日期参数

因此,我们有dateString == "2019-05-09"。现在,分别获取此参数非常简单

var [year, month, date] = dateString.split("-").map(Number)

3。处理时间字符串

对于时间字符串,由于其可变性,我们应该执行更复杂的操作。
我们有timeString == "13:26:10Z" 也有可能timeString == "13:26:10"timeString == "13:26:10+01:00

var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)

var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
    // then clearTimeString references the UTC time
    offset = new Date().getTimezoneOffset() * -1
} else {
    var clearOffset = timeString.split(/[+-]/)[1]
    if (clearOffset) {
        // then we have offset tail
        var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
        var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
        offset = (offsetMinutes + offsetHours * 60) * negation
    } // otherwise we do nothing because there is no offset marker
}

目前,我们以数字格式表示数据:
在几分钟内yearmonthdatehoursminutessecondsoffset

4。使用... native JS Date构造器

是的,我们无法避免,因为它太酷了。 JS Date自动为所有负值和太大的值匹配日期。这样我们就可以以原始格式传递所有参数,并且JS Date构造函数将自动为我们创建正确的日期!

new Date(year, month - 1, date, hours, minutes + offset, seconds)

Voila!这是完整的示例。

function convertHistoricalDate(isoString) {
  var [dateString, timeString] = isoString.split("T")
  var [year, month, date] = dateString.split("-").map(Number)
  
  var clearTimeString = timeString.split(/[Z+-]/)[0]
  var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
  
  var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
  if (timeString.includes("Z")) {
    // then clearTimeString references the UTC time
    offset = new Date().getTimezoneOffset() * -1
  } else {
    var clearOffset = timeString.split(/[+-]/)[1]
    if (clearOffset) {
      // then we have offset tail
      var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
      var [offsetHours, offsetMinutes] =   clearOffset.split(":").map(Number)
      offset = (offsetMinutes + offsetHours * 60) * negation
    } // otherwise we do nothing because there is no offset marker
  }

  return new Date(year, month - 1, date, hours, minutes + offset, seconds)
}

var testDate1 = convertHistoricalDate("1894-01-01T00:00:00+01:00")
var testDate2 = convertHistoricalDate("1893-01-01T00:00:00+01:00")
var testDate3 = convertHistoricalDate("1894-01-01T00:00:00-01:00")
var testDate4 = convertHistoricalDate("1893-01-01T00:00:00-01:00")

console.log(testDate1.toLocaleDateString(), testDate1.toLocaleTimeString())
console.log(testDate2.toLocaleDateString(), testDate2.toLocaleTimeString())
console.log(testDate3.toLocaleDateString(), testDate3.toLocaleTimeString())
console.log(testDate4.toLocaleDateString(), testDate4.toLocaleTimeString())

注意

在这种情况下,我们得到的Date实例具有自己的所有值(例如.getHours()),已被标准化,包括时区偏移。 testDate1.toISOString仍将返回奇怪的结果。但是,如果您正在使用这个日期,它可能会100%满足您的需求。

希望有帮助:)

答案 1 :(得分:1)

所有浏览器都遵循其自己的日期格式编码标准时可能会发生这种情况(但我不确定这部分内容)。无论如何,一个简单的解决方法是应用toISOString方法。

const today = new Date();
console.log(today.toISOString());