Come formattare ISO 8601 una data con offset di fuso orario in JavaScript?


100

Obiettivo: trova local timee UTC time offsetcostruisci l'URL nel seguente formato.

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

Il formato si basa sulla raccomandazione del W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

La documentazione dice:

Ad esempio, 2002-10-10T12: 00: 00−05: 00 (mezzogiorno del 10 ottobre 2002, Central Daylight Savings Time e Eastern Standard Time negli Stati Uniti) è uguale a 2002-10-10T17: 00: 00Z, cinque ore dopo il 2002-10-10T12: 00: 00Z.

Quindi, in base alla mia comprensione, ho bisogno di trovare la mia ora locale con new Date (), quindi utilizzare la funzione getTimezoneOffset () per calcolare la differenza, quindi collegarla alla fine della stringa.

1. Ottieni l'ora locale con il formato

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

produzione

2013-07-02T09:00:00

2.Ottieni un offset di ora UTC per ora

var offset = local.getTimezoneOffset() / 60;

produzione

7

3.Costruisci URL (solo parte temporale)

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

produzione:

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

L'output sopra indica che la mia ora locale è 2013/07/02 9am e la differenza dall'UTC è 7 ore (UTC è 7 ore avanti rispetto all'ora locale)

Finora sembra funzionare ma cosa succede se getTimezoneOffset () restituisce un valore negativo come -120?

Mi chiedo come dovrebbe apparire il formato in questo caso perché non riesco a capire dal documento W3C. Grazie in anticipo.

Risposte:


174

Quanto segue dovrebbe funzionare correttamente e per tutti i browser (grazie a @MattJohnson per il suggerimento)

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
Il segno indica la differenza dell'ora locale dal GMT
Steven Moseley

1
@ masato-san Devi invertire il segno, vedi la definizione su developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto

2
Qui la funzione pad restituisce la stringa corretta per ogni sezione della data tranne i millisecondi. Ad esempio, per 5 ms un input restituirà 05, che dovrebbe essere 005. Ecco un collegamento con la versione minima modificata della stessa funzione jsbin
Safique Ahmed Faruque

9
Nota: c'è un bug sottile in questa risposta. La differenza di fuso orario non verrà calcolata correttamente se la zona aveva minuti di offset ed era negativa (ad es. -09: 30), come ad esempio alcune località in Francia e Canada. Mod restituisce un numero negativo, quindi fare floor () prima di abs () inavvertitamente ha reso l'offset PIÙ negativo. Per correggere questo bug, quindi abs () e THEN floor (). Ho provato a modificare la risposta, ma a quanto pare "questa modifica era destinata a rivolgersi all'autore del post e non ha senso come modifica".
tytk

5
@tytk - Ottima cattura! Questo è, in effetti, sottile. Ho aggiunto la tua correzione alla risposta. Grazie per aver commentato!
Steven Moseley

67

getTimezoneOffset() restituisce il segno opposto del formato richiesto dalla specifica a cui si fa riferimento.

Questo formato è noto anche come ISO8601 , o più precisamente come RFC3339 .

In questo formato, UTC è rappresentato con un Zmentre tutti gli altri formati sono rappresentati da uno scostamento da UTC. Il significato è lo stesso di JavaScript, ma l'ordine di sottrazione è invertito, quindi il risultato porta il segno opposto.

Inoltre, non esiste alcun metodo Datesull'oggetto nativo chiamato format, quindi la tua funzione in # 1 fallirà a meno che tu non stia usando una libreria per ottenere ciò. Fare riferimento a questa documentazione .

Se stai cercando una libreria che possa funzionare direttamente con questo formato, ti consiglio di provare moment.js . In effetti, questo è il formato predefinito, quindi puoi semplicemente farlo:

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

Questa è una soluzione cross-browser ben testata e ha molte altre utili funzionalità.


L'uso di toISOString () non funzionerà. Il formato +01: 00 richiede che la parte dell'ora sia l'ora locale. toISOString () darebbe una stringa dell'ora UTC.
Austin France

1
@AustinFrance - Hai ragione! Sono sorpreso di aver commesso quell'errore in quel momento, poiché correggo spesso gli altri su questo punto. Scusa non ho visto il tuo commento due anni fa! Risposta modificata.
Matt Johnson-Pint,

9

Penso che valga la pena considerare che puoi ottenere le informazioni richieste con una sola chiamata API alla libreria standard ...

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

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

Dovresti eseguire lo scambio di testo se desideri aggiungere il delimitatore "T", rimuovere "GMT-" o aggiungere ": 00" alla fine.

Ma poi puoi giocare facilmente con le altre opzioni se vuoi, ad es. utilizzare 12 ore o omettere i secondi ecc.

Nota che sto usando la Svezia come locale perché è uno dei paesi che utilizza il formato ISO 8601. Penso che la maggior parte dei paesi ISO utilizzi questo formato "GMT-4" per l'offset del fuso orario, oltre al Canada che utilizza l'abbreviazione del fuso orario, ad es. "EDT" per l'ora legale orientale.

Puoi ottenere la stessa cosa dalla funzione i18n standard più recente "Intl.DateTimeFormat ()" ma devi dirgli di includere l'ora tramite le opzioni o ti darà solo la data.


1
Hm, per sv, in realtà ottengo "CET" e per una data dell'ora legale, "CEST" ... Ma grazie per l'input, l'API standard è sufficiente per me (è necessario anteporre ai messaggi di log una data). Se volessi fare sul serio su tempo e fusi orari, immagino che andrei per la libreria dei momenti ...
mmey

4

Questa è la mia funzione per il fuso orario dei clienti, è leggera e semplice

  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, sei sicuro? Viene visualizzato lo scostamento dell'ora corrente, che viene successivamente utilizzato per simulare l'ora locale. Forse a quel punto l'offset era diverso, ma non importa perché ti interessa solo ora.
Andrew

2

Controllare questo:

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

Nessun moment.js necessario: ecco una risposta di andata e ritorno completa, da un tipo di input "datetime-local" che restituisce una stringa ISOLocal a UTCseconds al GMT e viceversa:

<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

Solo i miei due mandati qui

Stavo affrontando questo problema con i datetimes, quindi quello che ho fatto è questo:

const moment = require('moment-timezone')

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

Quindi salva la data su db per poterla confrontare da qualche query.


Installare moment-timezone

npm i moment-timezone

0

La mia risposta è una leggera variazione per coloro che vogliono solo la data odierna nel fuso orario locale nel formato AAAA-MM-GG.

Sia chiaro:

Il mio obiettivo: ottenere la data odierna nel fuso orario dell'utente ma formattato come ISO8601 (AAAA-MM-GG)

Ecco il codice:

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

Questo funziona perché la locale svedese utilizza il formato ISO 8601.


Risposta non valida, questa risposta, ma altra domanda ... Questa risposta può essere facilmente trovata su SO.
PsychoX

0

Ecco le funzioni che ho usato per questo scopo:

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

Puoi ottenere ciò con alcuni semplici metodi di estensione. Il seguente metodo di estensione della data restituisce solo il componente del fuso orario in formato ISO, quindi è possibile definirne un altro per la parte data / ora e combinarli per una stringa di offset data-ora completa.

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

Utilizzo di esempio:

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

So che è il 2020 e la maggior parte delle persone probabilmente sta usando Moment.js ormai, ma una semplice soluzione di copia e incollabile a volte è ancora utile avere.

(Il motivo per cui ho diviso i metodi data / ora e offset è perché sto usando una vecchia libreria Datejs che fornisce già un toStringmetodo flessibile con specificatori di formato personalizzati, ma semplicemente non include l'offset del fuso orario. Quindi, ho aggiunto toISOLocaleStringper chiunque non ha detto la biblioteca.)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.