Comment formater ISO 8601 une date avec décalage de fuseau horaire en JavaScript?

100

Objectif: Trouver la local timeet UTC time offsetpuis construire l'URL au format suivant.

Exemple d'URL: / Actions / Sleep? Duration = 2002-10-10T12: 00: 00−05: 00

Le format est basé sur la recommandation du W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

La documentation dit:

Par exemple, 2002-10-10T12: 00: 00−05: 00 (midi le 10 octobre 2002, heure d'été centrale et heure normale de l'Est aux États-Unis) est égal à 2002-10-10T17: 00: 00Z, cinq heures plus tard que le 2002-10-10T12: 00: 00Z.

Donc, sur la base de ma compréhension, je dois trouver mon heure locale par new Date () puis utiliser la fonction getTimezoneOffset () pour calculer la différence, puis l'attacher à la fin de la chaîne.

1. obtenez l'heure locale avec le format

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

production

2013-07-02T09:00:00

2.Obtenir le décalage horaire UTC par heure

var offset = local.getTimezoneOffset() / 60;

production

7

3. URL de construction (partie temporelle uniquement)

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

production:

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

La sortie ci-dessus signifie que mon heure locale est le 02/07/2013 à 9h et que la différence par rapport à l'UTC est de 7 heures (UTC est 7 heures en avance sur l'heure locale)

Jusqu'à présent, cela semble fonctionner, mais que faire si getTimezoneOffset () renvoie une valeur négative comme -120?

Je me demande à quoi devrait ressembler le format dans un tel cas, car je ne peux pas comprendre à partir du document W3C. Merci d'avance.

Miaou
la source

Réponses:

174

Ce qui suit devrait fonctionner correctement, et pour tous les navigateurs (merci à @MattJohnson pour le conseil)

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
la source
2
Le panneau indique le décalage de l'heure locale par rapport à GMT
Steven Moseley
1
@ masato-san Vous devez inverser le signe, voir la définition sur developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto
2
Ici, la fonction pad renvoie la chaîne appropriée pour chaque section de date, sauf les millisecondes. Par exemple, pendant 5 ms, une entrée retournera 05, qui est supposé être 005. Voici un lien avec la version minimale modifiée de la même fonction jsbin
Safique Ahmed Faruque
9
Remarque: il y a un bug subtil dans cette réponse. Le décalage du fuseau horaire ne sera pas calculé correctement si la zone avait des minutes de décalage et était négative (par exemple -09: 30), comme certains emplacements en France et au Canada. Mod renvoie un nombre négatif, donc faire floor () avant abs () a rendu le décalage PLUS négatif par inadvertance. Pour corriger ce bug, donc abs () et ALORS floor (). J'ai essayé de modifier la réponse, mais apparemment "Cette modification était destinée à s'adresser à l'auteur du message et n'a aucun sens en tant que modification".
tytk
5
@tytk - Excellente prise! C'est, en effet, subtile. J'ai ajouté votre correctif à la réponse. Merci d'avoir commenté!
Steven Moseley
67

getTimezoneOffset() renvoie le signe opposé du format requis par la spécification que vous avez référencée.

Ce format est également appelé ISO8601 , ou plus précisément RFC3339 .

Dans ce format, UTC est représenté avec un Ztandis que tous les autres formats sont représentés par un décalage par rapport à UTC. La signification est la même que celle de JavaScript, mais l'ordre de soustraction est inversé, donc le résultat porte le signe opposé.

De plus, il n'y a pas de méthode sur l' Dateobjet natif appelé format, donc votre fonction dans # 1 échouera à moins que vous n'utilisiez une bibliothèque pour y parvenir. Reportez-vous à cette documentation .

Si vous recherchez une bibliothèque qui peut fonctionner directement avec ce format, je vous recommande d'essayer moment.js . En fait, c'est le format par défaut, vous pouvez donc simplement le faire:

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

Il s'agit d'une solution multi-navigateurs bien testée et dotée de nombreuses autres fonctionnalités utiles.

Matt Johnson-Pint
la source
L'utilisation de toISOString () ne fonctionnera pas. Le format +01: 00 nécessite que la partie heure soit l'heure locale. toISOString () donnerait une chaîne d'heure UTC.
Austin France
1
@AustinFrance - Vous avez raison! Je suis surpris d'avoir commis cette erreur à l'époque, car j'en corrige souvent d'autres sur ce point. Désolé je n'ai pas vu votre commentaire il y a deux ans! Réponse modifiée.
Matt Johnson-Pint
9

Je pense qu'il vaut la peine de considérer que vous pouvez obtenir les informations demandées avec un seul appel API à la bibliothèque standard ...

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

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

Vous devrez faire un échange de texte si vous souhaitez ajouter le délimiteur «T», supprimer le «GMT-» ou ajouter le «: 00» à la fin.

Mais alors vous pouvez facilement jouer avec les autres options si vous le souhaitez, par exemple. utilisez 12 heures ou omettez les secondes, etc.

Notez que j'utilise la Suède comme locale car c'est l'un des pays qui utilise le format ISO 8601. Je pense que la plupart des pays ISO utilisent ce format «GMT-4» pour le décalage du fuseau horaire autre que le Canada qui utilise l'abréviation du fuseau horaire, par exemple. "EDT" pour l'heure avancée de l'Est.

Vous pouvez obtenir la même chose de la fonction i18n standard plus récente "Intl.DateTimeFormat ()" mais vous devez lui dire d'inclure l'heure via les options ou il donnera simplement la date.

À M
la source
1
Hm, pour sv, j'obtiens effectivement "CET" et pour une date d'heure d'été, "CEST" ... Mais merci pour l'entrée, l'API standard me suffit (besoin de préfixer les messages du journal avec une date). Si je voulais prendre au sérieux le temps et les fuseaux horaires, je suppose que j'irais pour l'instant à la bibliothèque ...
mmey
4

C'est ma fonction pour le fuseau horaire des clients, c'est léger et simple

  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
la source
@tsh, êtes-vous sûr? Vous obtenez le décalage horaire actuel, qui est ensuite utilisé pour simuler l'heure locale. Peut-être qu'à ce moment-là, le décalage était différent, mais cela n'a pas d'importance parce que vous vous souciez juste de maintenant.
Andrew
2

Vérifie ça:

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
la source
1

Aucun moment.js nécessaire: Voici une réponse aller-retour complète, à partir d'un type d' entrée "datetime-local" qui génère une chaîne ISOLocal en secondes UTC à GMT et inversement:

<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
Capitaine fantastique
la source
0

Juste mes deux envois ici

J'étais confronté à ce problème avec les datetimes, alors ce que j'ai fait est le suivant:

const moment = require('moment-timezone')

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

Enregistrez ensuite la date dans db pour pouvoir la comparer à partir d'une requête.


À installer moment-timezone

npm i moment-timezone
Arnold Gandarillas
la source
0

Ma réponse est une légère variation pour ceux qui veulent juste la date d'aujourd'hui dans le fuseau horaire local au format AAAA-MM-JJ.

Laisse moi être clair:

Mon objectif: obtenir la date du jour dans le fuseau horaire de l'utilisateur mais au format ISO8601 (AAAA-MM-JJ)

Voici le code:

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

Cela fonctionne car les paramètres régionaux de la Suède utilisent le format ISO 8601.

Jonathan Steele
la source
Réponse invalide, cela répond, mais autre question ... Cette réponse peut être facilement trouvée sur SO.
PsychoX le
0

Voici les fonctions que j'ai utilisées à cette fin:

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
la source
0

Vous pouvez y parvenir avec quelques méthodes d'extension simples. La méthode d'extension de date suivante renvoie uniquement le composant de fuseau horaire au format ISO, vous pouvez en définir un autre pour la partie date / heure et les combiner pour obtenir une chaîne complète de décalage date-heure.

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

Exemple d'utilisation:

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

Je sais que nous sommes en 2020 et que la plupart des gens utilisent probablement Moment.js maintenant, mais une simple solution de copie et de collage est encore parfois pratique.

(La raison pour laquelle j'ai divisé les méthodes de date / heure et de décalage est que j'utilise une ancienne bibliothèque Datejs qui fournit déjà une toStringméthode flexible avec des spécificateurs de format personnalisés, mais n'inclut tout simplement pas le décalage du fuseau horaire. Par conséquent, j'ai ajouté toISOLocaleStringpour quiconque sans ladite bibliothèque.)

Extragorey
la source