【问题标题】:How to assume local time zone when parsing ISO 8601 date string?解析 ISO 8601 日期字符串时如何假设本地时区?
【发布时间】:2013-03-20 06:47:09
【问题描述】:

我有一个如下的 ISO 日期字符串

 var startTimeISOString = "2013-03-10T02:00:00Z";

当我使用下面的代码将其转换为 javascript 中的日期对象时,它会返回

var startTimeDate = new Date(startTimeISOString);

输出是

Date {Sun Mar 10 2013 07:30:00 GMT+0530 (India Standard Time)}

它确实会将 ISOString 转换为日期,但它会转换为本地时间,因为 new Date() 取决于客户端。如何将 iso 日期时间字符串转换为日期和时间而不是本地日期时间..?

谢谢

【问题讨论】:

  • 您的 ISO 时间以“Z”结尾,表示 UTC (GMT)。如果时间实际上是从本地开始的,则应将“Z”替换为“+0530”。但是,如果您想往返您的时间,则应将字符串版本保留为 UTC。
  • 您能否澄清一下您正在尝试创建一个保留原始时区的字符串表示形式,在本例中为 Zulu (Z),而不是重新解释运行代码的计算机的时区中的时间.听起来你是,但不是 100% 清楚。

标签: javascript


【解决方案1】:

According to MDN:

假定时区的差异

给定日期字符串“2014 年 3 月 7 日”,parse() 假定为本地时间 区域,但给定一个 ISO 格式,例如“2014-03-07”,它将假定为 UTC 时区。因此使用这些字符串生成的日期对象 除非系统设置为 UTC 的本地时区。这意味着出现的两个日期字符串 等效可能会导致两个不同的值,具体取决于格式 正在转换的字符串(此行为在 ECMAScript ed 6,因此两者都将被视为本地)。

我已经这样做了,现在正在获取 ISO 日期字符串中的确切时间,而不是本地时间

 var startTimeISOString = "2013-03-10T02:00:00Z";

 var startTime = new Date(startTimeISOString );
 startTime =   new Date( startTime.getTime() + ( startTime.getTimezoneOffset() * 60000 ) );

这将在 iso 日期字符串中给出相同的日期时间,这里的输出是

o/p

Date {Sun Mar 10 2013 02:00:00 GMT+0530 (India Standard Time)}

【讨论】:

  • 我看不出 parse 函数的细节与此有什么关系。引用的 MDN 段落只是说明 parse 查看字符串的格式并确定格式是 RFC2822 还是 ISO8601。
  • 这里的答案给出了一天中的正确时间,但这很危险,因为您仍处于与原始时间不同的时区。正确的答案是创建一个与原始时区相同的日期对象。
  • 请注意,getTimezoneOffset 是 UTC 和本地之间的差异,产生的结果与人们预期的相反。来自 MDN:“请注意,这意味着如果本地时区落后于 UTC,则偏移量为正,如果超过则为负。例如,如果您的时区是 UTC+10(澳大利亚东部标准时间),则将返回 -600 . "
  • 我只是在阅读这个问题的标题,而这个接受答案并没有回答这个问题 - 因为它绝对不假设 startTimeISOString 的时区。
  • @EdSykes—另外,EMA-262 ed 6(当前标准)将没有时区的 ISO 字符串解析更改为本地而不是 UTC(根据 ES5),因此 2014-03-07 将是在某些浏览器中为本地,在其他浏览器中为 UTC(其余为 NaN)。
【解决方案2】:

总结一下 tracevipin 帖子中的对话:

所有 Date 对象都基于自 1970-01-01T00:00:00Z 以来 毫秒time value,因此它们的核心是 UTC。这与 UNIX 不同,UNIX 使用的值表示自同一纪元以来的

Date.prototype.toString 方法返回一个依赖于实现的字符串,该字符串表示基于系统设置和客户端时区偏移的时间(也称为本地时间)。

如果需要 UTC ISO8601 时间字符串,可以使用Date.prototype.toISOString 方法。如果需要,可以很容易地为此方法编写“shim”。

最后,不要相信Date.parse 来解析字符串。 ES5 中指定了对 ISO8601 格式的 UTC 字符串的支持,但是它并没有在使用的浏览器中一致地实现。如果需要广泛的浏览器支持(例如典型的 Web 应用程序),手动解析字符串会更好(这并不难,有关于如何做的示例)。

简单的 ISO8601 UTC 时间戳解析器:

function dateObjectFromUTC(s) {
  s = s.split(/\D/);
  return new Date(Date.UTC(+s[0], --s[1], +s[2], +s[3], +s[4], +s[5], 0));
}

这是 toISOString 的垫片:

if (typeof Date.prototype.toISOString != 'function') {

  Date.prototype.toISOString = (function() {
  
    function z(n){return (n<10? '0' : '') + n;}
    function p(n){
      n = n < 10? z(n) : n;
      return n < 100? z(n) : n;
    }
    
    return function() {
      return this.getUTCFullYear() + '-' +
             z(this.getUTCMonth() + 1) + '-' +
             z(this.getUTCDate()) + 'T' +
             z(this.getUTCHours()) + ':' +
             z(this.getUTCMinutes()) + ':' +
             z(this.getUTCSeconds()) + '.' +
             p(this.getUTCMilliseconds()) + 'Z';
    } 
  }());
}

【讨论】:

  • 你应该把你的正则表达式改成/[\-\.\+: TZ]/g
  • @OnurYıldırım——你说得对,它没有正确分割字符串,最简单的正则表达式是/\D/。它不打算作为通用的 ISO 8601 字符串解析器,这是一项非常艰巨的工作。
  • ISO 字符串和 UTC 是两个不同的东西。这不会产生 ISO 8601 字符串。 ISO 8601 编码时区,这不会(由于使用 getUTC...)
  • @EdSykes——我很清楚这一点。有多种形式的 ISO 8601 日期和时间字符串不包括时区,例如2015-07、2006-W52-5。这是 ECMAScript 的 Date.prototype.toISOString 的 shim,它是 UTC,所以这个 shim 也是 UTC。
【解决方案3】:

这是因为日期是使用toString 方法打印的,该方法默认返回本地时区的日期和时间。方法toUTCString 会给你你需要的字符串。

Date 实际上将日期保存为以毫秒为单位的 unix 时间,并提供了对其进行操作的方法。

【讨论】:

  • 我可以将它作为日期对象而不是字符串吗?现在它返回一个字符串,但我需要它是一个日期对象,因为我正在与其他日期进行日期比较
  • 由于日期对象存储 unix 时间,您可以在需要时使用 UTC 方法。请参阅 Date 中的 UTC 方法:developer.mozilla.org/en-US/docs/JavaScript/Reference/…
  • getUTCDate、getUTCMonth 等
  • 我明白了,但是不能直接将 iso 日期字符串转换为正确的日期,而不是 JavaScript 中的本地时间吗?
  • 当我们给出一个 iso 日期字符串时,会创建一个 Date 对象及其等效的 unix 时间。所以 Date 对象是独立于时区的。然后 getDate、getMonth、toString 等方法返回本地时间值,getUTCDate、getUTCMonth、toUTCString 等方法返回 UTC 值。您不能指定时区。如果您需要这种功能,您可以在 Date 上构建它,或者使用 momentjs.comgithub.com/mde/timezone-js 之类的库
【解决方案4】:

在 vanilla javascript 中,无法创建一个假定您提供的 ISO 格式字符串的本地时间的日期。当您将 ISO 8601 格式的字符串传递给 javascript 时,会发生以下情况。我将使用非 UTC 时间,因为它比使用 ISO 格式的字符串更能说明问题:

  1. var startTime = new Date("2013-03-10T02:00:00+06:00")。请注意,这也可以是 2013-03-10T02:00:00Z 或任何其他 ISO 格式的字符串。
  2. 读取时间,应用偏移量并计算自 1970-01-01T00:00:00Z 以来的毫秒数
  3. 您现在只有几毫秒 - 您已经丢失了所有时区信息。在这种情况下 1362859200000

除了为您提供该数字的 UTC 表示的函数外,所有函数都将使用运行代码的计算机的时区将该数字解释为时间。

要做原发帖人想做的事,你需要做。

  1. 解析 ISO 字符串,将偏移量('Z' 或 '+06:00')解释为时区偏移量
  2. 存储时区偏移量
  3. 使用时区偏移量计算并存储自纪元以来的毫秒数
  4. 保持该偏移量
  5. 每当尝试进行计算或打印日期时,应用时区偏移量。

这不是小事,需要对 8601 规范进行完整的解释。太多代码放在这里。

这正是moment.js 的设计目的。我强烈推荐使用它。使用 moment.js:

moment("2013-03-10T02:00:00Z").format()
"2013-03-10T02:00:00Z"

这将导致打印原始字符串的 ISO 时间,保留偏移量。

【讨论】:

    【解决方案5】:

    它将返回 ISOdate

    var getDate = () => {
        var dt = new Date();
        var off = dt.getTimezoneOffset() * 60000
        var newdt = new Date(dt - off).toISOString()
        return newdt.slice(0, 19)
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多