Wie formatiere ich ein Datum mit Zeitzonenversatz nach ISO 8601 in JavaScript?

100

Ziel: Suchen Sie die URL local timeund UTC time offseterstellen Sie sie im folgenden Format.

Beispiel-URL: / Actions / Sleep? Duration = 2002-10-10T12: 00: 00−05: 00

Das Format basiert auf der W3C-Empfehlung: http://www.w3.org/TR/xmlschema11-2/#dateTime

Die Dokumentation sagt:

Zum Beispiel ist 2002-10-10T12: 00: 00−05: 00 (Mittag am 10. Oktober 2002, zentrale Sommerzeit sowie Eastern Standard Time in den USA) gleich 2002-10-10T17: 00: 00Z, fünf Stunden später als 2002-10-10T12: 00: 00Z.

Nach meinem Verständnis muss ich meine Ortszeit anhand des neuen Datums () ermitteln und dann die Funktion getTimezoneOffset () verwenden, um die Differenz zu berechnen, und sie dann an das Ende der Zeichenfolge anhängen.

1. Holen Sie sich die Ortszeit mit Format

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

Ausgabe

2013-07-02T09:00:00

2. Holen Sie sich die UTC-Zeit um eine Stunde versetzt

var offset = local.getTimezoneOffset() / 60;

Ausgabe

7

3. URL erstellen (nur Zeitteil)

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

Ausgabe:

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

Die obige Ausgabe bedeutet, dass meine Ortszeit 2013/07/02 9 Uhr ist und die Differenz zu UTC 7 Stunden beträgt (UTC ist 7 Stunden vor Ortszeit).

Bisher scheint es zu funktionieren, aber was ist, wenn getTimezoneOffset () einen negativen Wert wie -120 zurückgibt?

Ich frage mich, wie das Format in einem solchen Fall aussehen soll, da ich aus dem W3C-Dokument nichts herausfinden kann. Danke im Voraus.

Miau
quelle
Nur eine kurze Anmerkung, dass die akzeptierte Antwort mehr als 10 Jahre veraltet ist und JS toISOString()seit 2009 aufgenommen hat . Siehe meine Antwort unten.
Mikemaccana

Antworten:

174

Das Folgende sollte ordnungsgemäß und für alle Browser funktionieren (danke an @MattJohnson für den Tipp)

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

Steven Moseley
quelle
2
Das Zeichen zeigt den Versatz der Ortszeit von GMT
Steven Moseley
1
@ masato-san Sie müssen das Zeichen
umkehren
2
Hier gibt die Pad-Funktion die richtige Zeichenfolge für jeden Datumsabschnitt außer Millisekunden zurück. Zum Beispiel wird für 5 ms als Eingabe 05 zurückgegeben, was 005 sein soll. Hier ist ein Link mit der minimal modifizierten Version derselben Funktion jsbin
Safique Ahmed Faruque
9
Bitte beachten Sie: Diese Antwort enthält einen subtilen Fehler. Der Zeitzonenversatz wird nicht korrekt berechnet, wenn die Zone Versatzminuten hatte und negativ war (z. B. -09: 30), z. B. an bestimmten Orten in Frankreich und Kanada. Mod gibt eine negative Zahl zurück. Wenn Sie also floor () vor abs () ausführen, wird der Offset versehentlich negativer. Um diesen Fehler zu beheben, also abs () und THEN floor (). Versucht, die Antwort zu bearbeiten, aber anscheinend "Diese Bearbeitung sollte den Autor des Beitrags ansprechen und macht als Bearbeitung keinen Sinn".
Tytk
5
@tytk - Toller Fang! Das ist in der Tat subtil. Ich habe der Antwort Ihren Fix hinzugefügt. Danke für den Kommentar!
Steven Moseley
67

getTimezoneOffset() Gibt das entgegengesetzte Vorzeichen des Formats zurück, das für die von Ihnen angegebene Spezifikation erforderlich ist.

Dieses Format wird auch als ISO8601 oder genauer als RFC3339 bezeichnet .

In diesem Format wird UTC mit einer ZWeile dargestellt, während alle anderen Formate durch einen Versatz von UTC dargestellt werden. Die Bedeutung ist dieselbe wie bei JavaScript, aber die Reihenfolge der Subtraktion ist invertiert, sodass das Ergebnis das entgegengesetzte Vorzeichen trägt.

Außerdem gibt es keine Methode für das Dateaufgerufene native Objekt format, sodass Ihre Funktion in # 1 fehlschlägt, es sei denn, Sie verwenden eine Bibliothek, um dies zu erreichen. Siehe diese Dokumentation .

Wenn Sie eine Bibliothek suchen, die direkt mit diesem Format arbeiten kann, empfehle ich, moment.js auszuprobieren . Tatsächlich ist dies das Standardformat, sodass Sie dies einfach tun können:

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

Dies ist eine bewährte browserübergreifende Lösung mit vielen weiteren nützlichen Funktionen.

Matt Johnson-Pint
quelle
Die Verwendung von toISOString () funktioniert nicht. Für das Format +01: 00 muss der Zeitteil die Ortszeit sein. toISOString () würde eine UTC-Zeitzeichenfolge angeben.
Austin Frankreich
1
@AustinFrance - Du hast recht! Ich bin überrascht, dass ich diesen Fehler damals gemacht habe, da ich andere in diesem Punkt oft korrigiere. Entschuldigung, ich habe Ihren Kommentar vor zwei Jahren nicht gesehen! Antwort bearbeitet.
Matt Johnson-Pint
9

Ich denke, es lohnt sich zu bedenken, dass Sie die angeforderten Informationen mit nur einem einzigen API-Aufruf der Standardbibliothek erhalten können ...

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

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

Sie müssten Text austauschen, wenn Sie das Trennzeichen 'T' hinzufügen, das 'GMT-' entfernen oder das ': 00' an das Ende anhängen möchten.

Aber dann können Sie leicht mit den anderen Optionen spielen, wenn Sie z. Verwenden Sie die 12-Stunden-Zeit oder lassen Sie die Sekunden usw. weg.

Beachten Sie, dass ich Schweden als Gebietsschema verwende, da es eines der Länder ist, die das ISO 8601-Format verwenden. Ich denke, die meisten ISO-Länder verwenden dieses 'GMT-4'-Format für den Zeitzonenversatz außer Kanada, das die Zeitzonenabkürzung verwendet, z. "EDT" für östliche Sommerzeit.

Sie können dasselbe von der neueren Standard-i18n-Funktion "Intl.DateTimeFormat ()" erhalten, müssen jedoch angeben, dass die Uhrzeit über die Optionen angegeben werden soll, da sonst nur das Datum angegeben wird.

Tom
quelle
1
Hm, für sv bekomme ich tatsächlich "CET" und für ein Sommerzeitdatum "CEST" ... Aber danke für die Eingabe ist die Standard-API für mich ausreichend (Protokollnachrichten müssen ein Datum vorangestellt werden). Wenn ich Zeit und Zeitzonen ernst nehmen wollte, würde ich wohl für einen Moment in die Bibliothek gehen ...
mmey
4

Dies ist meine Funktion für die Zeitzone des Kunden, es ist leicht und einfach

  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;
  }
Bbb
quelle
@tsh, bist du sicher? Sie erhalten den aktuellen Zeitversatz, der später zur Simulation der Ortszeit verwendet wird. Vielleicht war der Versatz zu diesem Zeitpunkt anders, aber es spielt keine Rolle, weil Sie sich gerade darum kümmern.
Andrew
2

Überprüfen Sie dies:

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'
Nahuel Greco
quelle
1

Kein moment.js erforderlich: Hier ist eine vollständige Roundtrip- Antwort von einem Eingabetyp von "datetime-local", der eine ISOLocal-Zeichenfolge in UTCseconds bei GMT und zurück ausgibt:

<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
Captain Fantastic
quelle
0

Nur meine beiden schicken hierher

Ich war mit Datumsangaben mit diesem Problem konfrontiert. Ich habe also Folgendes getan:

const moment = require('moment-timezone')

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

Speichern Sie dann das Datum in db, um es anhand einer Abfrage vergleichen zu können.


Installieren moment-timezone

npm i moment-timezone
Arnold Gandarillas
quelle
0

Meine Antwort ist eine geringfügige Abweichung für diejenigen, die nur das heutige Datum in der lokalen Zeitzone im Format JJJJ-MM-TT wünschen.

Lass mich deutlich sein:

Mein Ziel: Holen Sie sich das heutige Datum in die Zeitzone des Benutzers, aber formatiert als ISO8601 (JJJJ-MM-TT)

Hier ist der Code:

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

Dies funktioniert, weil das Gebietsschema Schweden das ISO 8601-Format verwendet.

Jonathan Steele
quelle
Ungültige Antwort, dies beantwortet, aber andere Frage ... Diese Antwort kann leicht auf SO gefunden werden.
PsychoX
0

Hier sind die Funktionen, die ich zu diesem Zweck verwendet habe:

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

Sie können dies mit ein paar einfachen Erweiterungsmethoden erreichen. Die folgende Datumserweiterungsmethode gibt nur die Zeitzonenkomponente im ISO-Format zurück. Anschließend können Sie eine andere für den Datums- / Zeitteil definieren und diese zu einer vollständigen Datums- / Zeitversatzzeichenfolge kombinieren.

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

Anwendungsbeispiel:

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

Ich weiß, es ist 2020 und die meisten Leute verwenden Moment.js wahrscheinlich schon, aber eine einfache Lösung zum Kopieren und Einfügen ist manchmal immer noch praktisch.

(Der Grund, warum ich die Datums- / Zeit- und Versatzmethoden aufgeteilt habe, ist, dass ich eine alte Datejs- Bibliothek verwende, die bereits eine flexible toStringMethode mit benutzerdefinierten Formatspezifizierern bietet , aber den Zeitzonenversatz nicht enthält. Daher habe ich toISOLocaleStringfür jeden ohne hinzugefügt sagte Bibliothek.)

Extragorey
quelle