ISO 8601如何在JavaScript中格式化带有时区偏移的日期?


100

目标:找到local timeUTC time offset然后以以下格式构建网址。

示例网址:/ Actions / Sleep?duration = 2002-10-10T12:00:00−05:00

该格式基于W3C建议:http : //www.w3.org/TR/xmlschema11-2/#dateTime

该文档说:

例如,2002-10-10T12:00:00-05:00(2002年10月10日中午,美国中部夏令时以及美国东部标准时间)等于2002-10-10T17:00:00Z,比2002-10-10T12:00:00Z晚五个小时。

因此,根据我的理解,我需要通过新的Date()查找我的本地时间,然后使用getTimezoneOffset()函数计算时差,然后将其附加到字符串的末尾。

1.获取本地时间格式

var local = new Date().format("yyyy-MM-ddThh:mm:ss"); //today (local time)

输出

2013-07-02T09:00:00

2.获取UTC时间偏移小时

var offset = local.getTimezoneOffset() / 60;

输出

7

3.构造URL(仅限时间部分)

var duration = local + "-" + offset + ":00";

输出:

2013-07-02T09:00:00-7:00

上面的输出表示我的本地时间是2013/07/02 9am,与UTC的时差是7小时(UTC比本地时间早7小时)

到目前为止,它似乎仍然有效,但是如果getTimezoneOffset()返回负值(如-120)怎么办?

我想知道这种情况下的格式应该如何,因为我无法从W3C文档中弄清楚。提前致谢。

Answers:


174

以下内容应该可以正常运行,并且适用于所有浏览器(感谢@MattJohnson作为提示)

Date.prototype.toIsoString = function() {
    var tzo = -this.getTimezoneOffset(),
        dif = tzo >= 0 ? '+' : '-',
        pad = function(num) {
            var norm = Math.floor(Math.abs(num));
            return (norm < 10 ? '0' : '') + norm;
        };
    return this.getFullYear() +
        '-' + pad(this.getMonth() + 1) +
        '-' + pad(this.getDate()) +
        'T' + pad(this.getHours()) +
        ':' + pad(this.getMinutes()) +
        ':' + pad(this.getSeconds()) +
        dif + pad(tzo / 60) +
        ':' + pad(tzo % 60);
}

var dt = new Date();
console.log(dt.toIsoString());


2
该符号表示当地时间与格林尼治标准时间的偏差
史蒂文·莫斯利

1
@ masato-san您必须反转符号,请参阅developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…
bfavaretto

2
此处pad函数会为日期的每个部分(毫秒)返回正确的字符串。例如,输入5毫秒将返回05(应为005)。这是具有相同功能jsbin的
Safique Ahmed Faruque

9
请注意:此答案中有一个细微的错误。如果时区的偏移量分钟为负(例如-09:30),例如法国和加拿大的某些位置,则时区偏移量将无法正确计算。Mod返回一个负数,因此在abs()之前执行floor()会使偏移量MORE进一步为负。要更正此错误,请使用abs()和THEN floor()。试图编辑答案,但显然“此编辑旨在解决帖子的作者,并且没有任何意义”。
tytk

5
@tytk-很棒!那确实是微妙的。我将您的修复程序添加到了答案中。感谢您的评论!
史蒂文·莫斯利

67

getTimezoneOffset() 返回所引用规范要求的格式的相反符号。

此格式也称为ISO8601,或更确切地说称为RFC3339

在这种格式下,UTC用表示,Z而所有其他格式则用与UTC的偏移量表示。含义与JavaScript相同,但是减法顺序相反,因此结果带有相反的符号。

另外,本机Date对象上没有名为的方法format,因此除非您使用库来实现此功能,否则#1中的函数将失败。请参阅本文档

如果您正在寻找可以直接使用此格式的库,建议您尝试一下moment.js。实际上,这是默认格式,因此您只需执行以下操作:

var m = moment();    // get "now" as a moment
var s = m.format();  // the ISO format is the default so no parameters are needed

// sample output:   2013-07-01T17:55:13-07:00

这是一个经过充分测试的跨浏览器解决方案,并具有许多其他有用的功能。


使用toISOString()无法正常工作。+01:00格式要求时间部分为当地时间。toISOString()将给出一个UTC时间字符串。
奥斯汀法国

1
@AustinFrance-你是对的!令我惊讶的是我当时犯了这个错误,因为我经常在这一点上纠正其他人。抱歉,两年前我没看到您的评论!答案已编辑。
Matt Johnson-Pint 2015年

9

我认为值得考虑的是,只需对标准库的一个API调用就可以获取所需的信息...

new Date().toLocaleString( 'sv', { timeZoneName: 'short' } );

// produces "2019-10-30 15:33:47 GMT−4"

如果要添加'T'分隔符,删除'GMT-'或在末尾附加':00',则必须进行文本交换。

但是,例如,您可以轻松使用其他选项。使用12h时间或忽略秒等。

请注意,我将瑞典用作语言环境,因为它是使用ISO 8601格式的国家之一。我认为大多数ISO国家/地区都将这种“ GMT-4”格式用于时区偏移,而加拿大使用时区缩写,例如加拿大。东部夏令时为“ EDT”。

您可以从更新的标准i18n函数“ Intl.DateTimeFormat()”获得相同的结果,但必须通过选项告诉它包括时间,否则它将给出日期。


1
嗯,对于sv,我实际上得到了“ CET”,对于夏季时间,我得到了“ CEST” ...但是感谢您的输入,标准API对我来说就足够了(需要在日志消息前加上日期作为前缀)。如果我想认真对待时区和时区,我想我会
花点时间去找

4

这是我为客户所在时区提供的功能,它轻巧,简单

  function getCurrentDateTimeMySql() {        
      var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
      var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, 19).replace('T', ' ');
      var mySqlDT = localISOTime;
      return mySqlDT;
  }

@tsh,确定吗?您将获得当前时间偏移,该偏移以后可用于模拟本地时间。也许此时偏移量是不同的,但这并不重要,因为您只关心现在。
Andrew

2

检查一下:

function dateToLocalISO(date) {
    const off    = date.getTimezoneOffset()
    const absoff = Math.abs(off)
    return (new Date(date.getTime() - off*60*1000).toISOString().substr(0,23) +
            (off > 0 ? '-' : '+') + 
            (absoff / 60).toFixed(0).padStart(2,'0') + ':' + 
            (absoff % 60).toString().padStart(2,'0'))
}

// Test it:
d = new Date()

dateToLocalISO(d)
// ==> '2019-06-21T16:07:22.181-03:00'

// Is similar to:

moment = require('moment')
moment(d).format('YYYY-MM-DDTHH:mm:ss.SSSZ') 
// ==> '2019-06-21T16:07:22.181-03:00'

1

无需moment.js:这是一个完整的往返答案,其输入类型为“ datetime-local”,该类型将ISOLocal字符串输出到格林尼治标准时间的UTCseconds并返回:

<input type="datetime-local" value="2020-02-16T19:30">

isoLocal="2020-02-16T19:30"
utcSeconds=new Date(isoLocal).getTime()/1000

//here you have 1581899400 for utcSeconds

let isoLocal=new Date(utcSeconds*1000-new Date().getTimezoneOffset()*60000).toISOString().substring(0,16)
2020-02-16T19:30

0

只有我两个寄给这里

我在日期时间遇到了这个问题,所以我要做的是:

const moment = require('moment-timezone')

const date = moment.tz('America/Bogota').format()

然后将日期保存到数据库,以便能够从某些查询中进行比较。


安装 moment-timezone

npm i moment-timezone

0

对于那些只想以YYYY-MM-DD格式在本地时区显示今天日期的人,我的答案是一个微小的变化。

让我清楚一点:

我的目标:用户的时区中获取今天的日期,但格式为ISO8601(YYYY-MM-DD)

这是代码:

new Date().toLocaleDateString("sv") // "2020-02-23" // 

之所以可行,是因为瑞典的语言环境使用ISO 8601格式。


无效的答案,确实可以回答,但还有其他问题...在SO上可以轻松找到该答案。
PsychoX

0

这是我为此使用的功能:

function localToGMTStingTime(localTime = null) {
    var date = localTime ? new Date(localTime) : new Date();
    return new Date(date.getTime() + (date.getTimezoneOffset() * 60000)).toISOString();
};

function GMTToLocalStingTime(GMTTime = null) {
    var date = GMTTime ? new Date(GMTTime) : new Date();;
    return new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
};

0

您可以通过一些简单的扩展方法来实现。以下日期扩展方法仅返回ISO格式的时区组件,然后您可以为日期/时间部分定义另一个组件,并将它们组合为一个完整的date-time-offset字符串。

Date.prototype.getISOTimezoneOffset = function () {
    const offset = this.getTimezoneOffset();
    return (offset < 0 ? "+" : "-") + Math.floor(Math.abs(offset / 60)).leftPad(2) + ":" + (Math.abs(offset % 60)).leftPad(2);
}

Date.prototype.toISOLocaleString = function () {
    return this.getFullYear() + "-" + (this.getMonth() + 1).leftPad(2) + "-" +
        this.getDate().leftPad(2) + "T" + this.getHours().leftPad(2) + ":" +
        this.getMinutes().leftPad(2) + ":" + this.getSeconds().leftPad(2) + "." +
        this.getMilliseconds().leftPad(3);
}

Number.prototype.leftPad = function (size) {
    var s = String(this);
    while (s.length < (size || 2)) {
        s = "0" + s;
    }
    return s;
}

用法示例:

var date = new Date();
console.log(date.toISOLocaleString() + date.getISOTimezoneOffset());
// Prints "2020-08-05T16:15:46.525+10:00"

我知道现在是2020年,现在大多数人可能正在使用Moment.js,但是有时仍然很容易使用简单的复制和粘贴解决方案。

(之所以划分日期/时间和偏移量方法的原因是,因为我使用的是旧的Datejs库,该库已经提供了toString带有自定义格式说明符的灵活方法,但不包括时区偏移量。因此,我toISOLocaleString为没有说图书馆。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.