【问题标题】:Javascript Date parsing returning strange results in ChromeJavascript 日期解析在 Chrome 中返回奇怪的结果
【发布时间】:2019-05-09 10:12:49
【问题描述】:

我在 Chrome(版本 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)

我已经阅读了有关在不同浏览器中通过 Date ctor 进行非标准日期解析的信息,尽管提供了有效的 ISO8601 值。 但这不仅仅是奇怪的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?
  • 最重要的问题是,我该如何解决这个问题? (希望不用自己解析输入字符串)

【问题讨论】:

  • 也许 1893 年发生了一些与时间有关的事件? (请求历史学家 ITT)
  • 我有Sun Jan 01 1893 01:02:04 GMT+0202 (Eastern European Standard Time)Mon Jan 01 1894 01:02:04 GMT+0202 (Eastern European Standard Time)。看起来直到 1925 年,格林威治标准时间都发生了变化或其他什么(因为我从 1925 年开始才得到明确的格林威治标准时间偏移)
  • 好吧,仅供参考,在我的 PC 上,中断日期是 1924-05-01。我想,Chrome 不知何故使用了有关时间的历史信息(1924 年,格林威治天文台首次发出时间信号,因此,这可能以某种方式影响了全球时间)。这个想法很疯狂,我知道:D
  • 有趣:Chrome 在版本 67 中对日期解析添加了一些更改,原因是一些 ecma 脚本规范更新了有关 timezonoffsets:chromium-review.googlesource.com/c/v8/v8/+/572148

标签: javascript google-chrome date


【解决方案1】:

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

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

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

1。分别获取日期和时间

字符串中的T 将日期与时间分开。所以,我们可以用T分割ISO字符串

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)

瞧!这是完整的工作示例。

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% 满足您的需求。

希望有所帮助:)

【讨论】:

  • 谢谢@Limbo!
【解决方案2】:

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

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

【讨论】:

  • 好的,所以为了显示目的,这会做。但我需要检查 Chrome 格式的计算是否仍能正常工作。
  • 希望计算能够正常工作,因为每个 JavaScript 引擎都会将日期转换为整数然后对其进行操作,从而获得一致的体验。 @cmxl
猜你喜欢
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2018-05-11
  • 2012-08-08
相关资源
最近更新 更多