Jak sformatować datę w ISO 8601 z przesunięciem strefy czasowej w JavaScript?

100

Cel: znajdź, local timea UTC time offsetnastępnie utwórz adres URL w następującym formacie.

Przykładowy adres URL: / Actions / Sleep? Duration = 2002-10-10T12: 00: 00-05: 00

Format jest oparty na zaleceniach W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

Dokumentacja mówi:

Na przykład 2002-10-10T12: 00: 00−05: 00 (południe 10 października 2002 r., Środkowy czas letni i wschodni standardowy w USA) jest równy 2002-10-10T17: 00: 00Z, pięć godzin później niż 2002-10-10T12: 00: 00Z.

Więc opierając się na moim zrozumieniu, muszę znaleźć mój czas lokalny za pomocą nowej funkcji Date (), a następnie użyć funkcji getTimezoneOffset (), aby obliczyć różnicę, a następnie dołączyć go do końca ciągu.

1. Uzyskaj czas lokalny z formatem

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

wynik

2013-07-02T09:00:00

2.Uzyskaj przesunięcie czasu UTC o godzinę

var offset = local.getTimezoneOffset() / 60;

wynik

7

3.Skonstruuj adres URL (tylko część czasu)

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

wynik:

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

Powyższe dane wyjściowe oznaczają, że mój czas lokalny to 2013/07/02 9 rano, a różnica w stosunku do UTC to 7 godzin (UTC jest 7 godzin przed czasem lokalnym)

Jak dotąd wydaje się, że działa, ale co się stanie, jeśli getTimezoneOffset () zwróci wartość ujemną, taką jak -120?

Zastanawiam się, jak powinien wyglądać format w takim przypadku, ponieważ nie mogę się domyślić z dokumentu W3C. Z góry dziękuję.

Miauczeć
źródło

Odpowiedzi:

174

Poniższe powinno działać poprawnie i dla wszystkich przeglądarek (dzięki @MattJohnson za wskazówkę)

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
źródło
2
Znak wskazuje przesunięcie czasu lokalnego od GMT
Steven Moseley
1
@ masato-san Musisz odwrócić znak, zobacz definicję na developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
bfavaretto
2
Tutaj funkcja pad zwraca właściwy ciąg dla każdej sekcji daty z wyjątkiem milisekund. Na przykład, dla 5ms jak wejście powróci 05, które rzekomo być 005. Tu jest link z minimalnym zmodyfikowana wersja funkcji samego jsbin
Safique Ahmed Faruque
9
Uwaga: w tej odpowiedzi jest subtelny błąd. Przesunięcie strefy czasowej nie zostanie poprawnie obliczone, jeśli strefa miała przesunięcie minut i była ujemna (np. -09: 30), na przykład w niektórych lokalizacjach we Francji i Kanadzie. Mod zwraca liczbę ujemną, więc wykonanie floor () przed abs () nieumyślnie uczyniło przesunięcie MORE ujemnym. Aby naprawić ten błąd, więc abs () i THEN floor (). Próbowałem edytować odpowiedź, ale najwyraźniej „Ta zmiana miała na celu skierowanie do autora postu i nie ma sensu jako zmiana”.
tytk
5
@tytk - Świetny chwyt! To jest rzeczywiście subtelne. Dodałem twoją poprawkę do odpowiedzi. Dzięki za komentarz!
Steven Moseley
67

getTimezoneOffset() zwraca przeciwny znak formatu wymaganego przez specyfikację, do której się odwołujesz.

Ten format jest również znany jako ISO8601 , a dokładniej jako RFC3339 .

W tym formacie UTC jest reprezentowany przez Zchwilę, a wszystkie inne formaty są reprezentowane przez przesunięcie względem UTC. Znaczenie jest takie samo jak w JavaScript, ale kolejność odejmowania jest odwrócona, więc wynik ma przeciwny znak.

Ponadto nie ma metody dla Datewywoływanego obiektu natywnego format, więc funkcja w # 1 zakończy się niepowodzeniem, chyba że do tego celu użyjesz biblioteki. Zapoznaj się z tą dokumentacją .

Jeśli szukasz biblioteki, która może współpracować bezpośrednio z tym formatem, polecam wypróbować moment.js . W rzeczywistości jest to format domyślny, więc możesz po prostu zrobić to:

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

Jest to dobrze przetestowane rozwiązanie dla różnych przeglądarek, które ma wiele innych przydatnych funkcji.

Matt Johnson-Pint
źródło
Użycie toISOString () nie zadziała. Format +01: 00 wymaga, aby część czasowa była czasem lokalnym. toISOString () dałoby ciąg czasu UTC.
Austin Francja
1
@AustinFrance - Masz rację! Dziwię się, że popełniłem wtedy ten błąd, ponieważ często poprawiam innych w tej kwestii. Przepraszam, że dwa lata temu nie widziałem twojego komentarza! Odpowiedź edytowana.
Matt Johnson-Pint
9

Myślę, że warto wziąć pod uwagę, że żądane informacje można uzyskać za pomocą jednego wywołania interfejsu API do biblioteki standardowej ...

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

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

Będziesz musiał dokonać zamiany tekstu, jeśli chcesz dodać ogranicznik „T”, usunąć „GMT-” lub dopisać „: 00” na końcu.

Ale wtedy możesz łatwo bawić się innymi opcjami, jeśli chcesz np. użyj 12 godzin lub pomiń sekundy itp.

Zauważ, że używam Szwecji jako ustawień regionalnych, ponieważ jest to jeden z krajów, w których używany jest format ISO 8601. Myślę, że większość krajów ISO używa tego formatu „GMT-4” dla przesunięcia strefy czasowej innego niż Kanada, który używa skrótu strefy czasowej, np. „EDT” dla wschodniego czasu dziennego.

Możesz uzyskać to samo z nowszej standardowej funkcji i18n "Intl.DateTimeFormat ()", ale musisz powiedzieć jej, aby zawierała czas za pomocą opcji, albo po prostu poda datę.

Tomek
źródło
1
Hm, dla sv faktycznie dostaję "CET", a na letnią datę "CEST" ... Ale dzięki za dane wejściowe wystarczy mi standardowe API (trzeba poprzedzić komunikaty dziennika datą). Gdybym chciał poważnie
potraktować
4

To jest moja funkcja dla strefy czasowej klienta, jest lekka i prosta

  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
źródło
@tsh, czy na pewno? Otrzymujesz aktualne przesunięcie czasu, które jest później używane do symulacji czasu lokalnego. Być może w tym momencie przesunięcie było inne, ale to nie ma znaczenia, ponieważ teraz ci zależy.
Andrew
2

Sprawdź to:

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

Moment.js nie jest potrzebny: oto pełna odpowiedź w obie strony, od typu wejściowego „datetime-local”, który generuje ciąg ISOLokalny do sekund UTC w czasie GMT i wstecz:

<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
Kapitanie Fantastyczny
źródło
0

Tylko moje dwie wysyłają tutaj

Miałem do czynienia z tym problemem z datami, więc zrobiłem to:

const moment = require('moment-timezone')

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

Następnie zapisz datę do bazy danych, aby móc ją porównać z jakimś zapytaniem.


Żeby zainstalować moment-timezone

npm i moment-timezone
Arnold Gandarillas
źródło
0

Moja odpowiedź to niewielka zmiana dla tych, którzy chcą po prostu dzisiejszą datę w lokalnej strefie czasowej w formacie RRRR-MM-DD.

Wyjaśnię:

Mój cel: uzyskaj dzisiejszą datę w strefie czasowej użytkownika, ale w formacie ISO8601 (RRRR-MM-DD)

Oto kod:

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

To działa, ponieważ ustawienia narodowe Szwecji używają formatu ISO 8601.

Jonathan Steele
źródło
Nieprawidłowa odpowiedź, to odpowiedź, ale inne pytanie ... Tę odpowiedź można łatwo znaleźć na SO.
PsychoX
0

Oto funkcje, których użyłem w tym celu:

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

Możesz to osiągnąć za pomocą kilku prostych metod rozszerzania. Poniższa metoda rozszerzenia daty zwraca tylko składnik strefy czasowej w formacie ISO, a następnie można zdefiniować inny dla części daty / godziny i połączyć je w celu uzyskania pełnego ciągu przesunięcia daty i godziny.

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

Przykładowe użycie:

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

Wiem, że jest rok 2020 i większość ludzi prawdopodobnie używa już Moment.js, ale proste rozwiązanie do kopiowania i wklejania jest nadal przydatne.

(Powodem, dla którego podzieliłem metody daty / godziny i przesunięcia, jest to, że używam starej biblioteki Datejs, która już zapewnia elastyczną toStringmetodę z niestandardowymi specyfikatorami formatu, ale po prostu nie zawiera przesunięcia strefy czasowej. Dlatego dodałem toISOLocaleStringdla każdego bez wspomniana biblioteka).

Ekstragoria
źródło