Como faço para gerar uma string formatada ISO 8601 em JavaScript?


247

Eu tenho um Dateobjeto Como renderizo a titleparte do seguinte snippet?

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

Eu tenho a parte "tempo relativo em palavras" de outra biblioteca.

Eu tentei o seguinte:

function isoDate(msSinceEpoch) {

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

}

Mas isso me dá:

"2010-4-2T3:19"

Respostas:


453

Já existe uma função chamada toISOString():

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

Se, de alguma forma, você estiver em um navegador que não suporta, eu o protegerei:

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';
    };

  }() );
}

1
.toISOString () definitivamente retorna a data no UTC?
Jon Wells

new Date("xx").toISOString()produz NaN-NaN-NaNTNaN:NaN:NaN.NZA implementação nativa lança RangeException.
Joseph Lennox

Se você deseja passar um objeto de data para um serviço de sabão ... esse é o caminho! :) Obrigado.
thinklinux

1
Na amostra fornecida pelo OP, não há milissegundos ou fuso horário.
Dan Dascalescu

Todas as chamadas aquelas funções como "getUTCFullYear" estão a falhar, porque eles não são conhecidos para o modo de documento IE 5.
Elaskanator

63

Veja o último exemplo na página 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

1
A amostra fornecida pelo OP ( <abbr title="2010-04-02T14:12:07">) não possui fuso horário. Talvez eles quisessem a hora local, o que faria sentido para uma sequência de horas da interface do usuário?
Dan Dascalescu 14/09/16

60

Quase todos os métodos ISO na Web descartam as informações do fuso horário aplicando uma conversão em "Z" ulu time (UTC) antes de emitir a string. .ToISOString () nativo do navegador também descarta informações de fuso horário.

Isso descarta informações valiosas, como o servidor ou destinatário, sempre pode converter uma data ISO completa em hora do Zulu ou em qualquer fuso horário necessário, além de obter as informações de fuso horário do remetente.

A melhor solução que encontrei é usar a biblioteca javascript Moment.js e usar o seguinte código:

Para obter a hora ISO atual com informações de fuso horário e milissegundos

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() 

Para obter a hora ISO de um objeto Data JavaScript nativo com informações de fuso horário, mas sem milissegundos

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

Isso pode ser combinado com o Date.js para obter funções como Date.today () cujo resultado pode ser passado para o momento.

Uma string de data formatada como esta é compiladora JSON e se presta bem para ser armazenada em um banco de dados. Python e C # parecem gostar.


21
não brinque com as pessoas de datas. Basta usar moment.js e salvar seu cabelo.
Valamas

1
na verdade, em python e db's acabou sendo uma dor. Os bancos de dados usam UTC (sem problemas, pois você pode facilmente converter para o UTC no lado do servidor); portanto, se você deseja manter as informações de deslocamento, precisa de outro campo. E o Python prefere o uso de nanossegundos em vez dos milissegundos de javascript, que geralmente são suficientes e preferíveis em segundos simples. No python, apenas dateutil.parser.parse analisa-o corretamente e, para escrever um ISO de milissegundos, é necessário um "_when = when.strftime ("% Y-% m-% dT% H:% M:% S.% f% z "); retorne _when [: - 8] + _when [-5:]" para converter os nanos em milis. isso não é legal.
Daniel F

3
Você pode realmente apenas omitir o formato assim: moment(new Date()).format(). "A partir da versão 1.5.0, o momento de chamada no formato # sem um formato assumirá o formato ISO8601 YYYY-MM-DDTHH:mm:ssZ". Doc: Role para cima em momentjs.com/docs/#/displaying/fromnow
user193130

1
Bom ponto @ user193130, mas você realmente precisa ter cuidado, porque a saída difere do método nativo. moment().format() "2015-03-04T17:16:05+03:00" (new Date()).toISOString() "2015-03-04T14:16:24.555Z"
Olga

1
Talvez seja exigente, mas esses exemplos retornam o deslocamento atual do UTC, não o fuso horário. Um fuso horário é uma região geográfica comumente expressa como, por exemplo, "América / Toronto". Muitos fusos horários mudar sua UTC compensado duas vezes por ano e o fuso horário pode não (sempre) ser determinada a partir da UTC offset atual ... assim esta resposta também é deixar cair a informação de fuso horário :-)
Martin Fido

27

A pergunta foi o formato ISO com precisão reduzida. Voila:

 new Date().toISOString().slice(0, 19) + 'Z'
 // '2014-10-23T13:18:06Z'

Supondo que o Z à direita seja desejado, caso contrário, omita.


14

Mais curto, mas não suportado pelo Internet Explorer 8 e versões anteriores:

new Date().toJSON()

12

Se você não precisa oferecer suporte ao IE7, o seguinte é um ótimo e conciso hack:

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

para o IE7, essa decisão também é adequada se foi incluído o json3-library ( bestiejs.github.io/json3 ). Obrigado :)
vladimir

Também falha no IE8. ("'JSON' está indefinido")
Cees Timmerman

1
Percorrer o JSON é feio, especialmente se seu objetivo declarado for concisão; use o toJSONmétodo da data . JSON.stringifyestá usando debaixo das cobertas de qualquer maneira.
Mark Amery

O @CeesTimmerman IE8 suporta o JSONobjeto, embora não em alguns modos de compatibilidade. Veja stackoverflow.com/questions/4715373/…
Mark Amery 2/15

3
De que maneira isso é melhor do que .toISOString()?
Martin Martin

7

Normalmente, não quero exibir uma data UTC, pois os clientes não gostam de fazer a conversão na cabeça. Para exibir uma data ISO local , eu uso a função:

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;
};

A função acima omite as informações de deslocamento do fuso horário (exceto se a hora local for UTC), então eu uso a função abaixo para mostrar o deslocamento local em um único local. Você também pode anexar sua saída aos resultados da função acima, se desejar mostrar o deslocamento em todas as vezes:

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

toLocalIsoStringusos pad. Se necessário, funciona como praticamente qualquer função de teclado, mas, para completar, é isso que eu uso:

// 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;
}

3

Falta um '+' após o 'T'

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

deve fazê-lo.

Para os zeros à esquerda, você pode usar isso daqui :

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(); 
} 

Usando assim:

PadDigits(d.getUTCHours(),2)

Great catch! Porém, não trata dos "0" ausentes.
James A. Rosen

1
Escreva uma função para converter um número inteiro em uma sequência de 2 caracteres (acrescentando um '0' se o argumento for menor que 10) e chame-o para cada parte da data / hora.
dan04

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
No modo IE5, tive que seguir esse caminho, pois todas as outras respostas aqui usavam funções que não existem.
Elaskanator

3

O problema com o toISOString é que ele fornece data e hora apenas como "Z".

A ISO-8601 também define data e hora com diferença de fuso horário em horas e minutos, nas formas 2016-07-16T19: 20: 30 + 5: 30 (quando o fuso horário estiver à frente do UTC) e 2016-07-16T19: 20: 30-01 : 00 (quando o fuso horário estiver atrás do UTC).

Não acho que seja uma boa ideia usar outro plugin, moment.js, para uma tarefa tão pequena, especialmente quando você pode obtê-lo com algumas linhas de código.



    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); 

Depois de compensar o fuso horário em horas e minutos, você pode anexar a uma sequência de data e hora.

Eu escrevi uma postagem no blog: http://usefulangle.com/post/30/javascript-get-date-time-with-offset-hours-minutes



2

Consegui obter abaixo da saída com muito menos código.

var ps = new Date('2010-04-02T14:12:07')  ;
ps = ps.toDateString() + " " + ps.getHours() + ":"+ ps.getMinutes() + " hrs";

Resultado:

Fri Apr 02 2010 19:42 hrs

0
function getdatetime() {
    d = new Date();
    return (1e3-~d.getUTCMonth()*10+d.toUTCString()+1e3+d/1)
        .replace(/1(..)..*?(\d+)\D+(\d+).(\S+).*(...)/,'$3-$1-$2T$4.$5Z')
        .replace(/-(\d)T/,'-0$1T');
}

Encontrei o básico do Stack Overflow em algum lugar (acredito que fazia parte de algum outro código de golfe do Stack Exchange) e o aprimorei para que funcione também no Internet Explorer 10 ou anterior. É feio, mas faz o trabalho.


0

Para estender a excelente e concisa resposta de Sean com um pouco de açúcar e sintaxe moderna:

// date.js

const getMonthName = (num) => {
  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Oct', 'Nov', 'Dec'];
  return months[num];
};

const formatDate = (d) => {
  const date = new Date(d);
  const year = date.getFullYear();
  const month = getMonthName(date.getMonth());
  const day = ('0' + date.getDate()).slice(-2);
  const hour = ('0' + date.getHours()).slice(-2);
  const minutes = ('0' + date.getMinutes()).slice(-2);

  return `${year} ${month} ${day}, ${hour}:${minutes}`;
};

module.exports = formatDate;

Então por exemplo.

import formatDate = require('./date');

const myDate = "2018-07-24T13:44:46.493Z"; // Actual value from wherever, eg. MongoDB date
console.log(formatDate(myDate)); // 2018 Jul 24, 13:44
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.