如何在JavaScript中输出ISO 8601格式的字符串?

330

我有一个Date对象。如何呈现以下片段中的title部分?

<abbr title="2010-04-02T14:12:07">A couple days ago</abbr>

我有一个来自另一个库的“相对时间”的部分。

我尝试了以下方法:

function isoDate(msSinceEpoch) {

   var d = new Date(msSinceEpoch);
   return d.getUTCFullYear() + '-' + (d.getUTCMonth() + 1) + '-' + d.getUTCDate() + 'T' +
          d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds();

}

但这给了我:
"2010-4-2T3:19"
16个回答

589

已经存在一个名为toISOString()的函数:

var date = new Date();
date.toISOString(); //"2011-12-19T15:28:46.493Z"

如果您不幸使用一个不支持它的浏览器,我为您提供了解决方案:

if (!Date.prototype.toISOString) {
  (function() {

    function pad(number) {
      var r = String(number);
      if (r.length === 1) {
        r = '0' + r;
      }
      return r;
    }

    Date.prototype.toISOString = function() {
      return this.getUTCFullYear() +
        '-' + pad(this.getUTCMonth() + 1) +
        '-' + pad(this.getUTCDate()) +
        'T' + pad(this.getUTCHours()) +
        ':' + pad(this.getUTCMinutes()) +
        ':' + pad(this.getUTCSeconds()) +
        '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) +
        'Z';
    };

  }());
}

console.log(new Date().toISOString())


9
toISOString() 方法肯定会返回UTC时间的日期? - Jon Wells
1
new Date("xx").toISOString() 会产生 NaN-NaN-NaNTNaN:NaN:NaN.NZ 的结果。原生实现会抛出 RangeException 异常。 - Joseph Lennox
1
如果你想将一个日期对象传递给一个SOAP服务...那就是这样做的!:) 谢谢。 - thinklinux
2
在OP提供的示例中,没有毫秒或时区。 - Dan Dascalescu
4
@JosephLennox 这是在哥谭市的时间吗? - Dai
显示剩余3条评论

68

注意:截至2022年3月,这个答案仍在不断获得赞同。 moment.js 库已被弃用。以下是两个主要的替代品:LuxonDay.js,其他可在弃用链接中找到。

Luxon

Luxon 可以被认为是 Moment 的进化版本。它由 Moment 的长期贡献者Isaac Cambron编写。请阅读 Luxon 文档中的Why does Luxon exist?For Moment users页面。

区域设置:Intl 提供的 时区:Intl 提供的

Day.js

Day.js 设计成 Moment.js 的极简替代品,使用类似的 API。虽然它不是一个直接替代品,但如果你习惯于使用 Moment 的 API 并希望快速入门,请考虑使用 Day.js。

区域设置:可单独导入的自定义数据文件 时区:通过插件提供的国际化时间

我使用 Day.js 是因为它更小,但 Luxon 更易处理。


几乎所有关于网络上 to-ISO 方法都会在输出字符串之前将时区信息应用于转换到 "Z"ulu 时间(UTC),从而丢弃时区信息。浏览器的本机 .toISOString() 也会删除时区信息。

这样做就丢弃了有价值的信息,因为服务器或接收者可以始终将完整的 ISO 日期转换为它所需的 Zulu 时间或任何其他时区,并仍然获取发件人的时区信息。

我找到的最好的解决方案是使用 Moment.js JavaScript 库,并使用以下代码:

获取当前带有毫秒和时区信息的 ISO 时间

now = moment().format("YYYY-MM-DDTHH:mm:ss.SSSZZ")
// "2013-03-08T20:11:11.234+0100"

now = moment().utc().format("YYYY-MM-DDTHH:mm:ss.SSSZZ")
// "2013-03-08T19:11:11.234+0000"

now = moment().utc().format("YYYY-MM-DDTHH:mm:ss") + "Z"
// "2013-03-08T19:11:11Z" <- better use the native .toISOString() 

获取具有时区信息但不包含毫秒的本机JavaScript Date对象的ISO时间

var current_time = Date.now();
moment(current_time).format("YYYY-MM-DDTHH:mm:ssZZ")

这可以与 Date.js 结合使用,以获取像 Date.today() 这样的函数,其结果可以传递给 moment。

格式化为此类日期字符串符合 JSON 规范,并且很适合存储到数据库中。Python 和 C# 似乎很喜欢它。


25
别把日期玩坏了,用moment.js吧,省心不少。 - Valamas
1
实际上,在Python和数据库中,这证明是很麻烦的。数据库使用UTC(没有问题,因为您可以轻松地在服务器端转换为UTC),因此如果要保留偏移信息,则需要另一个字段。而Python更喜欢使用纳秒而不是JavaScript的毫秒,它们通常足够并且优于普通秒。在Python中,只有dateutil.parser.parse可以正确解析它,写入毫秒ISO需要一个“_when = when.strftime(“%Y-%m-%dT%H:%M:%S.%f%z”);返回_when [:-8] + _when [-5:]”来将纳秒转换为毫秒。这不是很好。 - Daniel F
3
实际上,您可以省略格式,像这样写:moment(new Date()).format()。 "从版本1.5.0开始,调用moment#format而不使用格式将默认为... ISO8601格式 YYYY-MM-DDTHH:mm:ssZ"。文档:从http://momentjs.com/docs/#/displaying/fromnow/向上滚动。 - user193130
1
@user193130 说得不错,但你需要非常小心,因为输出结果与本地方法不同。moment().format() "2015-03-04T17:16:05+03:00" (new Date()).toISOString() "2015-03-04T14:16:24.555Z" - Olga
1
可能有点挑剔,但这些示例返回的是当前与UTC的偏移量,而不是时区。时区是一个通常表示为“美洲/多伦多”等地理区域。许多时区每年更改两次其UTC偏移量,因此无法(总是)从当前UTC偏移量确定时区... 因此,此答案也省略了时区信息 :-) - Martin Fido
显示剩余3条评论

67
请看在页面https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference:Global_Objects:Date上的最后一个示例:
/* Use a function for the exact format desired... */
function ISODateString(d) {
    function pad(n) {return n<10 ? '0'+n : n}
    return d.getUTCFullYear()+'-'
         + pad(d.getUTCMonth()+1)+'-'
         + pad(d.getUTCDate())+'T'
         + pad(d.getUTCHours())+':'
         + pad(d.getUTCMinutes())+':'
         + pad(d.getUTCSeconds())+'Z'
}

var d = new Date();
console.log(ISODateString(d)); // Prints something like 2009-09-28T19:03:12Z

2
OP提供的示例(<abbr title="2010-04-02T14:12:07">)没有时区信息。也许他们想要本地时间,这对于UI时间字符串来说是有意义的? - Dan Dascalescu

47

所问问题是ISO格式带有降低的精度。嘿:

 new Date().toISOString().slice(0, 19) + 'Z'
 // '2014-10-23T13:18:06Z'
假设需要保留末尾的Z,则保留;否则请省略。

18

最短,但不支持Internet Explorer 8及更早版本:

new Date().toJSON()

15

如果您不需要支持IE7,以下是一个简洁的好方法:

console.log(
  JSON.parse(JSON.stringify(new Date()))
)


对于IE7而言,如果包含了json3库(http://bestiejs.github.io/json3),这个决定也是适合的。谢谢 :) - vladimir
也在IE8中失败了。("'JSON'未定义") - Cees Timmerman
1
通过 JSON 进行往返转换很丑陋,特别是如果你的目标是简洁明了的话;相反,使用日期的 toJSON 方法。JSON.stringify 在底层已经在使用它了。 - Mark Amery
@CeesTimmerman IE8支持JSON对象,但不支持某些兼容模式。请参见https://dev59.com/SG445IYBdhLWcg3wwcyg - Mark Amery
3
这种方法比.toISOString()好在哪里? - xyhhx

10

通常我不想显示UTC日期,因为顾客不喜欢在脑海中进行转换。为了显示本地 ISO日期,我使用以下函数:

function toLocalIsoString(date, includeSeconds) {
    function pad(n) { return n < 10 ? '0' + n : n }
    var localIsoString = date.getFullYear() + '-'
        + pad(date.getMonth() + 1) + '-'
        + pad(date.getDate()) + 'T'
        + pad(date.getHours()) + ':'
        + pad(date.getMinutes()) + ':'
        + pad(date.getSeconds());
    if(date.getTimezoneOffset() == 0) localIsoString += 'Z';
    return localIsoString;
};

上述函数省略了时区偏移信息(除非本地时间恰好是UTC),因此我使用下面的函数在单个位置显示本地偏移量。如果您希望在每个时间中显示偏移量,还可以将其输出附加到上述函数的结果中:

function getOffsetFromUTC() {
    var offset = new Date().getTimezoneOffset();
    return ((offset < 0 ? '+' : '-')
        + pad(Math.abs(offset / 60), 2)
        + ':'
        + pad(Math.abs(offset % 60), 2))
};

toLocalIsoString使用pad。如有需要,它的作用类似于任何一个填充函数,但为了完整起见,这是我使用的内容:

// Pad a number to length using padChar
function pad(number, length, padChar) {
    if (typeof length === 'undefined') length = 2;
    if (typeof padChar === 'undefined') padChar = '0';
    var str = "" + number;
    while (str.length < length) {
        str = padChar + str;
    }
    return str;
}

6
toISOString存在的问题是它只提供了带有"Z"的日期时间。ISO-8601还定义了带有时区差异(以小时和分钟为单位)的日期时间,例如2016-07-16T19:20:30+5:30(当时区比UTC快)和2016-07-16T19:20:30-01:00(当时区比UTC慢)。我认为对于这样一个小任务使用另一个插件moment.js并不是一个好主意,特别是当你可以用几行代码来完成它时。一旦你获得了以小时和分钟为单位的时区偏移量,就可以将其附加到日期时间字符串中。我在这篇博客文章中写了关于此的内容:http://usefulangle.com/post/30/javascript-get-date-time-with-offset-hours-minutes

var timezone_offset_min = new Date().getTimezoneOffset(),
  offset_hrs = parseInt(Math.abs(timezone_offset_min / 60)),
  offset_min = Math.abs(timezone_offset_min % 60),
  timezone_standard;

if (offset_hrs < 10)
  offset_hrs = '0' + offset_hrs;

if (offset_min > 10)
  offset_min = '0' + offset_min;

// getTimezoneOffset returns an offset which is positive if the local timezone is behind UTC and vice-versa.
// So add an opposite sign to the offset
// If offset is 0, it means timezone is UTC
if (timezone_offset_min < 0)
  timezone_standard = '+' + offset_hrs + ':' + offset_min;
else if (timezone_offset_min > 0)
  timezone_standard = '-' + offset_hrs + ':' + offset_min;
else if (timezone_offset_min == 0)
  timezone_standard = 'Z';

// Timezone difference in hours and minutes
// String such as +5:30 or -6:00 or Z
console.log(timezone_standard);


笔误:if (offset_min > 10) 应该是 <.. - Gerard ONeill
我同意你的帖子 - 话虽如此,'Z' 暗示了一个有目的的“zulu”转换,而00:00则意味着它就是它本身(本地时间在GMT区域内)。如果这是一个函数,我会传递一个“convertToZulu”参数来转换日期并附加 z;否则不进行转换,使用00:00代替 Z。 - Gerard ONeill

3
function timeStr(d) { 
  return ''+
    d.getFullYear()+
    ('0'+(d.getMonth()+1)).slice(-2)+
    ('0'+d.getDate()).slice(-2)+
    ('0'+d.getHours()).slice(-2)+
    ('0'+d.getMinutes()).slice(-2)+
    ('0'+d.getSeconds()).slice(-2);
}

1
在IE5模式下,我不得不选择这种方法,因为所有其他答案都使用了不存在的函数。 - Elaskanator

3

'T' 后面缺少一个 '+' 符号。

isoDate: function(msSinceEpoch) {
  var d = new Date(msSinceEpoch);
  return d.getUTCFullYear() + '-' + (d.getUTCMonth() + 1) + '-' + d.getUTCDate() + 'T'
         + d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds();
}

应该这样做。

对于前导零,您可以使用此处的内容:here

function PadDigits(n, totalDigits) 
{ 
    n = n.toString(); 
    var pd = ''; 
    if (totalDigits > n.length) 
    { 
        for (i=0; i < (totalDigits-n.length); i++) 
        { 
            pd += '0'; 
        } 
    } 
    return pd + n.toString(); 
} 

使用方法如下:

PadDigits(d.getUTCHours(),2)

太棒了!但它没有解决缺少“0”的问题。 - James A. Rosen
1
编写一个函数,将整数转换为2个字符的字符串(如果参数小于10,则在前面添加“0”),并为日期/时间的每个部分调用它。 - dan04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接