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ę.

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


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.


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ę.


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

@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'

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

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

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.


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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.