Jak wyprowadzić ciąg w formacie ISO 8601 w JavaScript?

247

Mam Dateprzedmiot Jak renderować titlefragment następującego fragmentu kodu?

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

Mam część „względny czas w słowach” z innej biblioteki.

Próbowałem następujące:

function isoDate(msSinceEpoch) {

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

}

Ale to daje mi:

"2010-4-2T3:19"
James A. Rosen
źródło

Odpowiedzi:

453

Istnieje już funkcja o nazwie toISOString():

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

Jeśli w jakiś sposób korzystasz z przeglądarki, która go nie obsługuje, zapewniam Ci:

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

  }() );
}
Anatolij Mironow
źródło
1
.toISOString () zdecydowanie zwraca datę w UTC?
Jon Wells,
new Date("xx").toISOString()tworzy NaN-NaN-NaNTNaN:NaN:NaN.NZNatywna implementacja zgłasza RangeException.
Joseph Lennox,
Jeśli chcesz przekazać obiekt daty do usługi mydła ... tak to jest! :) Dzięki.
thinklinux,
1
W próbce dostarczonej przez PO nie ma milisekund ani strefy czasowej.
Dan Dascalescu
Wszystkie te wywołania funkcji, takie jak „getUTCFullYear”, kończą się niepowodzeniem, ponieważ nie są znane w trybie dokumentu IE 5
Elaskanator
63

Zobacz ostatni przykład na stronie 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
dev-null-dweller
źródło
1
Próbka dostarczona przez OP ( <abbr title="2010-04-02T14:12:07">) nie ma strefy czasowej. Może chcieli czasu lokalnego, co miałoby sens dla ciągu czasu interfejsu użytkownika?
Dan Dascalescu
60

Prawie każda metoda ISO w sieci upuszcza informacje o strefie czasowej, stosując konwersję do czasu ulu „Z” (UTC) przed wyprowadzeniem ciągu. Natywna funkcja .toISOString () przeglądarki również usuwa informacje o strefie czasowej.

To odrzuca cenne informacje, ponieważ serwer lub odbiorca mogą zawsze przekonwertować pełną datę ISO na czas Zulu lub dowolną strefę czasową, której potrzebuje, jednocześnie otrzymując informacje o strefie czasowej nadawcy.

Najlepszym rozwiązaniem, z jakim się zetknąłem, jest użycie biblioteki javascript Moment.js i użycie następującego kodu:

Aby uzyskać bieżący czas ISO z informacjami o strefie czasowej i milisekundach

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

Aby uzyskać czas ISO natywnego obiektu JavaScript Date z informacjami o strefie czasowej, ale bez milisekund

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

Można to połączyć z Date.js, aby uzyskać funkcje takie jak Date.today (), których wynik można następnie przekazać do momentu.

Tak sformatowany ciąg daty jest zgodny z JSON i dobrze nadaje się do zapisania w bazie danych. Python i C # wydają się to lubić.

Daniel F.
źródło
21
nie faszeruj ludzi randkami. Wystarczy użyć moment.js i zapisz swoje włosy.
Valamas
1
właściwie na Pythonie i dbach okazało się to uciążliwe. db używa UTC (bez problemu, ponieważ można łatwo przekonwertować na serwer UTC), więc jeśli chcesz zachować informacje o przesunięciu, potrzebujesz innego pola. A Python woli używać nanosekund zamiast milisekund javascript, które zwykle wystarczają i są lepsze niż zwykłe sekundy. W Pythonie tylko dateutil.parser.parse parsuje go poprawnie, a do zapisu ISO w milisekundach wymagane jest „_when = when.strftime (”% Y-% m-% dT% H:% M:% S.% f% z "); zwróć _when [: - 8] + _when [-5:]", aby przekonwertować nanosy na milis. to nie jest miłe.
Daniel F
3
Rzeczywiście można po prostu pominąć format tak: moment(new Date()).format(). „Począwszy od wersji 1.5.0, moment wywołania formatu # bez formatu będzie domyślnie ustawiony na ... format ISO8601 YYYY-MM-DDTHH:mm:ssZ”. Dokument: przewiń w górę od momentjs.com/docs/#/displaying/fromnow
user193130
1
Dobry punkt @ user193130, ale naprawdę musisz być ostrożny, ponieważ dane wyjściowe różnią się od metody natywnej. moment().format() "2015-03-04T17:16:05+03:00" (new Date()).toISOString() "2015-03-04T14:16:24.555Z"
Olga,
1
Być może wybredny, ale te przykłady zwracają bieżące przesunięcie względem UTC, a nie strefę czasową. Strefa czasowa to region geograficzny powszechnie wyrażany jako np. „Ameryka / Toronto”. Wiele stref czasowych zmienia przesunięcie UTC dwa razy w roku, a strefa czasowa nie może (zawsze) zostać ustalona na podstawie aktualnego przesunięcia UTC ... dlatego ta odpowiedź również usuwa informacje o strefie czasowej :-)
Martin Fido
27

Pytanie dotyczyło formatu ISO ze zmniejszoną precyzją. Voila:

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

Zakładając, że końcowy Z jest pożądany, w przeciwnym razie po prostu pomiń.

arcseldon
źródło
14

Najkrótszy, ale nie obsługiwany przez Internet Explorer 8 i wcześniejsze:

new Date().toJSON()
younes0
źródło
12

Jeśli nie potrzebujesz obsługi IE7, to świetny, zwięzły hack:

JSON.parse(JSON.stringify(new Date()))
Russell Davis
źródło
w przypadku IE7 ta decyzja jest również odpowiednia, jeśli została dołączona biblioteka json3 ( bestiejs.github.io/json3 ). Dzięki :)
Vladimir
Również zawodzi w IE8. („JSON” jest niezdefiniowany ”)
Cees Timmerman
1
Podróż w obie strony przez JSON jest brzydka, szczególnie jeśli twoim celem jest zwięzłość; toJSONzamiast tego użyj metody date . JSON.stringifyi tak używa go pod przykryciem.
Mark Amery
@CeesTimmerman IE8 obsługuje JSONobiekt, ale nie w niektórych trybach zgodności. Zobacz stackoverflow.com/questions/4715373/…
Mark Amery
3
W jaki sposób jest to lepsze niż .toISOString()?
Martin
7

Zwykle nie chcę wyświetlać daty UTC, ponieważ klienci nie lubią konwersji w głowie. Aby wyświetlić lokalną datę ISO, używam funkcji:

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

Powyższa funkcja pomija informację o przesunięciu strefy czasowej (z wyjątkiem sytuacji, gdy czasem lokalnym jest UTC), więc używam funkcji poniżej, aby pokazać lokalne przesunięcie w jednym miejscu. Możesz również dołączyć jego wynik do wyników powyższej funkcji, jeśli chcesz wyświetlać przesunięcie za każdym razem:

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

toLocalIsoStringwykorzystuje pad. W razie potrzeby działa jak prawie każda funkcja padu, ale ze względu na kompletność używam tego:

// 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;
}
Charles Burns
źródło
3

Po „T” brakuje „+”

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

powinien to zrobić.

W przypadku zer wiodących możesz użyć tego stąd :

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

Używając tego w ten sposób:

PadDigits(d.getUTCHours(),2)
kaiz.net
źródło
Ładny chwyt! Nie usuwa jednak brakujących „0”.
James A. Rosen
1
Napisz funkcję, aby przekonwertować liczbę całkowitą na 2-znakowy ciąg znaków (poprzedzając „0”, jeśli argument jest mniejszy niż 10), i wywołaj ją dla każdej części daty / godziny.
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);
}
Sean
źródło
1
W trybie IE5 musiałem iść tą drogą, ponieważ wszystkie inne odpowiedzi wykorzystywały funkcje, które nie istnieją.
Elaskanator
3

Problem z toISOString polega na tym, że podaje datetime tylko jako „Z”.

ISO-8601 definiuje również datę i godzinę z różnicą stref czasowych w godzinach i minutach, w postaciach takich jak 2016-07-16T19: 20: 30 + 5: 30 (kiedy strefa czasowa jest przed UTC) i 2016-07-16T19: 20: 30-01 : 00 (gdy strefa czasowa znajduje się za UTC).

Nie sądzę, że dobrym pomysłem jest użycie innej wtyczki, moment.js do tak małego zadania, zwłaszcza gdy można go uzyskać za pomocą kilku linii kodu.



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

Po przesunięciu strefy czasowej w godzinach i minutach możesz dołączyć do ciągu datetime.

Napisałem na nim post na blogu: http://usefulangle.com/post/30/javascript-get-date-time-with-offset-hours-minutes

Przydatny kąt
źródło
2

Byłem w stanie uzyskać poniżej danych wyjściowych przy bardzo mniejszym kodzie.

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

Wynik:

Fri Apr 02 2010 19:42 hrs
Srikanth Srikanth
źródło
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');
}

Znalazłem gdzieś podstawy przepełnienia stosu (uważam, że było to częścią innego golfowego kodu Stack Exchange) i poprawiłem go, aby działał również w przeglądarce Internet Explorer 10 lub wcześniejszej. Jest brzydka, ale wykonuje zadanie.

Jonas Byström
źródło
0

Aby rozszerzyć świetną i zwięzłą odpowiedź Seana za pomocą cukru i nowoczesnej składni:

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

Następnie np.

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
Juha Untinen
źródło