Comment initialiser une date JavaScript à un fuseau horaire particulier

232

J'ai la date et l'heure dans un fuseau horaire particulier sous forme de chaîne et je souhaite la convertir en heure locale. Mais, je ne sais pas comment définir le fuseau horaire dans l'objet Date.

Par exemple, j'ai Feb 28 2013 7:00 PM ET,alors je peux

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

Pour autant que je sache, je peux régler l'heure UTC ou l'heure locale. Mais comment régler l'heure dans un autre fuseau horaire?

J'ai essayé d'utiliser l'ajout / soustraction du décalage de l'UTC mais je ne sais pas comment contrer l'heure d'été. Je ne sais pas si je vais dans la bonne direction.

Comment puis-je convertir du temps d'un fuseau horaire différent en heure locale en javascript?

pavanred
la source
1
Les objets de date n'ont pas de fuseau horaire, ils sont UTC.
RobG

Réponses:

352

Contexte

L' Dateobjet JavaScript suit le temps en UTC en interne, mais accepte généralement les entrées et produit des sorties à l'heure locale de l'ordinateur sur lequel il s'exécute. Il a très peu d'installations pour travailler avec le temps dans d'autres fuseaux horaires.

La représentation interne d'un Dateobjet est un nombre unique, représentant le nombre de millisecondes qui se sont écoulées depuis 1970-01-01 00:00:00 UTC, sans tenir compte des secondes intercalaires. Aucun fuseau horaire ni format de chaîne n'est stocké dans l'objet Date lui-même. Lorsque diverses fonctions de l' Dateobjet sont utilisées, le fuseau horaire local de l'ordinateur est appliqué à la représentation interne. Si la fonction produit une chaîne, les informations locales de l'ordinateur peuvent être prises en considération pour déterminer comment produire cette chaîne. Les détails varient selon la fonction et certains sont spécifiques à l'implémentation.

Les seules opérations que l' Dateobjet peut effectuer avec des fuseaux horaires non locaux sont:

  • Il peut analyser une chaîne contenant un décalage UTC numérique à partir de n'importe quel fuseau horaire. Il l'utilise pour ajuster la valeur analysée et stocke l'équivalent UTC. L'heure locale et le décalage d'origine ne sont pas conservés dans l' Dateobjet résultant . Par exemple:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
    d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
  • Dans les environnements qui ont implémenté l' API d'internationalisation ECMASCript (alias "Intl"), un Dateobjet peut produire une chaîne spécifique aux paramètres régionaux ajustée à un identifiant de fuseau horaire donné. Ceci est accompli via l' timeZoneoption to toLocaleStringet ses variations. La plupart des implémentations prendront en charge les identifiants de fuseau horaire IANA, tels que 'America/New_York'. Par exemple:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toLocaleString('en-US', { timeZone: 'America/New_York' })
    //=> "4/12/2020, 12:00:00 PM"
    // (midnight in China on Apring 13th is noon in New York on April 12th)

    La plupart des environnements modernes prennent en charge l'ensemble complet des identificateurs de fuseau horaire IANA ( voir le tableau de compatibilité ici ). Cependant, gardez à l'esprit que le seul identifiant requis pour être pris en charge par Intl est 'UTC', vous devez donc vérifier attentivement si vous devez prendre en charge les navigateurs plus anciens ou les environnements atypiques (par exemple, les appareils IoT légers).

Bibliothèques

Il existe plusieurs bibliothèques qui peuvent être utilisées pour travailler avec des fuseaux horaires. Bien qu'ils ne puissent toujours pas faire en sorte que l' Dateobjet se comporte différemment, ils implémentent généralement la base de données de fuseau horaire IANA standard et fournissent des fonctions pour l'utiliser en JavaScript. Les bibliothèques modernes utilisent les données de fuseau horaire fournies par l'API Intl, mais les bibliothèques plus anciennes ont généralement des frais généraux, surtout si vous utilisez un navigateur Web, car la base de données peut devenir un peu volumineuse. Certaines de ces bibliothèques vous permettent également de réduire de manière sélective l'ensemble de données, selon les fuseaux horaires pris en charge et / ou la plage de dates avec lesquelles vous pouvez travailler.

Voici les bibliothèques à considérer:

Bibliothèques basées sur Internet

Les nouveaux développements doivent choisir l'une de ces implémentations, qui s'appuient sur l'API Intl pour leurs données de fuseau horaire:

Bibliothèques non internationales

Ces bibliothèques sont maintenues, mais portent le fardeau de regrouper leurs propres données de fuseau horaire, qui peuvent être assez volumineuses.

* Alors que Moment et Moment-Timezone étaient précédemment recommandés, l'équipe Moment préfère désormais que les utilisateurs choisissent Luxon pour un nouveau développement.

Bibliothèques abandonnées

Ces bibliothèques ont été officiellement fermées et ne devraient plus être utilisées.

Propositions futures

La proposition temporelle TC39 vise à fournir un nouvel ensemble d'objets standard pour travailler avec les dates et les heures dans le langage JavaScript lui-même. Cela inclura la prise en charge d'un objet sensible au fuseau horaire.

Matt Johnson-Pint
la source
1
Veuillez remplacer «représenter» par «sortie / analyser», car les horodatages représentés sont indépendants du fuseau horaire
Bergi
1
@Bergi - J'ai repensé cela et je suis d'accord avec vous. Mis à jour ma réponse en conséquence.
Matt Johnson-Pint
1
Lorsque vous faites cela dans la console Firebug:, var date_time = new Date()la valeur de date_timeest (pour moi dans AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}et donc il semble qu'il ait stocké un fuseau horaire. Comment puis-je m'assurer qu'il date_times'agit uniquement de l'heure UTC, sans fuseau horaire inclus?
user1063287
Hmm, selon cette réponse ( stackoverflow.com/a/8047885/1063287 ), c'est ceci: new Date().getTime();
user1063287
1
@ user1063287: la méthode toString utilise le décalage du fuseau horaire de l'hôte pour produire une date et une heure dans le fuseau horaire "local". L'objet date lui-même a une valeur d'heure qui est un décalage par rapport au 1970-01-01T00: 00: 00Z, donc efficacement UTC. Pour voir la date et l'heure UTC, utilisez toISOString .
RobG
69

Comme l'a dit Matt Johnson

Si vous pouvez limiter votre utilisation aux navigateurs Web modernes, vous pouvez désormais effectuer les opérations suivantes sans bibliothèques spéciales:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

Ce n'est pas une solution complète, mais cela fonctionne pour de nombreux scénarios qui nécessitent uniquement une conversion de sortie (de l'heure UTC ou locale vers un fuseau horaire spécifique, mais pas dans l'autre sens).

Ainsi, bien que le navigateur ne puisse pas lire les fuseaux horaires IANA lors de la création d'une date, ou dispose de méthodes pour modifier les fuseaux horaires sur un objet Date existant, il semble y avoir un hack autour de lui:

function changeTimezone(date, ianatz) {

  // suppose the date is 12:00 UTC
  var invdate = new Date(date.toLocaleString('en-US', {
    timeZone: ianatz
  }));

  // then invdate will be 07:00 in Toronto
  // and the diff is 5 hours
  var diff = date.getTime() - invdate.getTime();

  // so 12:00 in Toronto is 17:00 UTC
  return new Date(date.getTime() + diff);

}

// E.g.
var there = new Date();
var here = changeTimezone(there, "America/Toronto");

console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);

pic commun
la source
6
Je me demande pourquoi cette réponse ne reçoit pas plus d'amour. Pour mes besoins, c'est absolument la meilleure solution. Dans mon application, toutes les dates sont en UTC, mais elles doivent être entrées ou sorties dans des fuseaux horaires IANA explicites dans l'interface utilisateur. Pour cela, j'utilise cette solution et elle fonctionne parfaitement. Simple, efficace, standard.
logidelic
2
@logidelic c'est un nouveau post. pour être honnête, je me suis étonné quand il m'a frappé que vous pourriez utiliser toLocaleStringpour obtenir l'effet inverse, et oui, cela devrait être de notoriété publique! allez écrire un article à ce sujet et mentionnez-moi :-)
commonpike
1
Pour un environnement de noeud qui n'est pas déjà en GMT, le code ci-dessus doit changer de var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); dans var invdate = new Date ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.
5
Les navigateurs ne sont pas tenus d'analyser la sortie de toLocaleString , c'est donc basé sur une prémisse défectueuse.
RobG
3
"... pourquoi cette réponse ne reçoit pas plus d'amour?" - parce que l' Dateobjet qu'il retourne est un mensonge. Même dans l'exemple illustré, la sortie de chaîne inclut le fuseau horaire de la machine locale. Sur mon ordinateur, les deux résultats indiquent «GMT-0700 (heure avancée du Pacifique)», où cela n'est correct que pour le premier (Toronto est en fait en heure de l'Est). Au-delà de la sortie de chaîne, l'horodatage contenu dans l' Dateobjet a été décalé à un autre moment dans le temps. Cela peut être une technique utile, mais comporte de nombreuses mises en garde - principalement que les Datefonctions d'objet n'auront pas de sortie normale.
Matt Johnson-Pint
30

Vous pouvez spécifier un décalage de fuseau horaire sur new Date(), par exemple:

new Date('Feb 28 2013 19:00:00 EST')

ou

new Date('Feb 28 2013 19:00:00 GMT-0500')

Depuis le Datestockage de l'heure UTC (c'est-à-dire getTimeretourne en UTC), javascript convertira l'heure en UTC, et lorsque vous appelez des choses comme toStringjavascript convertira l'heure UTC en fuseau horaire local du navigateur et renverra la chaîne dans le fuseau horaire local, c'est-à-dire si j'utilise UTC+8:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

Vous pouvez également utiliser la getHours/Minute/Secondméthode normale :

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(Cela 8signifie qu'une fois l'heure convertie en heure locale UTC+8, le nombre d'heures est 8.)

maowtm
la source
16
L'analyse de tout format autre que le format étendu ISO 8601 dépend de l'implémentation et ne doit pas être utilisée. Il n'y a pas de norme pour les abréviations de fuseau horaire, par exemple "EST" peut représenter l'une des 3 zones différentes.
RobG
1
Les exemples de ce commentaire ne fonctionnent souvent pas dans Internet Explorer. Comme mentionné dans le commentaire précédent de cet article, ISO 8601 est important. J'ai confirmé en lisant la spécification du langage ECMA-262 (Javascript 5th).
Dakusan
12

Cela devrait résoudre votre problème, n'hésitez pas à proposer des correctifs. Cette méthode tiendra également compte de l'heure d'été pour la date indiquée.

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

Comment utiliser avec le fuseau horaire et l'heure locale:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
poulets
la source
Cette solution était adaptée à mon besoin. Je ne savais pas exactement comment je pouvais tenir compte de l'heure d'été.
Hossein Amin
Pourquoi ne pas simplement revenir tzDatedirectement?
levi
8

J'ai trouvé que la façon la plus prise en charge pour ce faire, sans se soucier d'une bibliothèque tierce, était getTimezoneOffsetde calculer l'horodatage approprié ou de mettre à jour l'heure, puis d'utiliser les méthodes normales pour obtenir la date et l'heure nécessaires.

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

ÉDITER

J'utilisais auparavant des UTCméthodes lors de l'exécution des transformations de date, ce qui était incorrect. En ajoutant le décalage à l'heure, l'utilisation des getfonctions locales retournera les résultats souhaités.

Shaun Cockerill
la source
Où avez-vous calculé timezone_offset?
Anand Somani
1
Oups, j'ai mal étiqueté une de mes variables. timezone_offsetaurait dû être offset, ou vice versa. J'ai modifié ma réponse pour afficher le nom de variable correct.
Shaun Cockerill
3

J'ai rencontré un problème similaire avec les tests unitaires (en particulier pour plaisanter lorsque les tests unitaires s'exécutent localement pour créer les instantanés, puis le serveur CI s'exécute (potentiellement) dans un fuseau horaire différent, ce qui entraîne l'échec de la comparaison des instantanés). J'ai moqué notre Dateet certaines des méthodes de soutien comme ceci:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});
JRulle
la source
2

Essayez d'utiliser ctoc à partir de npm. https://www.npmjs.com/package/ctoc_timezone

Il a des fonctionnalités simples pour changer les fuseaux horaires (la plupart des fuseaux horaires environ 400) et tous les formats personnalisés que vous souhaitez afficher.

Sandeep Chowdary
la source
2

En m'appuyant sur les réponses ci-dessus, j'utilise cette doublure native pour convertir la longue chaîne de fuseau horaire en une chaîne de trois lettres:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

Cela donnera PDT ou PST selon la date fournie. Dans mon cas d'utilisation particulier, développé sur Salesforce (Aura / Lightning), nous sommes en mesure d'obtenir le fuseau horaire de l'utilisateur au format long à partir du backend.

Shane
la source
Tout simplement parce que j'aime jouer au pédant de temps en temps, 'America / Los_Angeles' n'est pas un fuseau horaire, c'est un "emplacement représentatif" pour un décalage particulier et des règles d'heure d'été et l'historique de ces changements (selon la base de données de fuseau horaire IANA ) . ;-)
RobG
Haha, vous devez poster ce commentaire sur plusieurs réponses ici: P
Shane
2

Je sais que c'est 3 ans trop tard, mais peut-être que cela peut aider quelqu'un d'autre parce que je n'ai rien trouvé de tel sauf la bibliothèque moment-fuseau horaire, qui n'est pas exactement la même que celle qu'il demande ici.

J'ai fait quelque chose de similaire pour le fuseau horaire allemand, c'est un peu complexe en raison de l'heure d'été et des années bissextiles où vous avez 366 jours.

cela peut nécessiter un peu de travail avec la fonction "isDaylightSavingTimeInGermany" tandis que différents fuseaux horaires changent à différentes heures, l'heure d'été.

de toute façon, consultez cette page: https://github.com/zerkotin/german-timezone-converter/wiki

les principales méthodes sont les suivantes: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

J'ai fait un effort pour le documenter, donc ce ne sera pas si déroutant.

zerkotin
la source
Ce devrait être un commentaire, ce n'est pas une réponse.
RobG
1

Essayez: date-from-timezone , il résout la date prévue à l'aide de nativement disponible Intl.DateTimeFormat.

J'ai utilisé cette méthode dans l'un de mes projets depuis quelques années déjà, mais c'est maintenant que j'ai décidé de la publier en tant que petit projet OS :)

Mariusz Nowak
la source
1

Peut-être que cela vous aidera

/**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
);

JuanM04
la source
0

Pour les utilisateurs Ionic, j'ai eu un enfer avec cela car il .toISOString()doit être utilisé avec le modèle html.

Cela saisira la date actuelle, mais peut bien sûr être ajouté aux réponses précédentes pour une date sélectionnée.

Je l'ai réparé en utilisant ceci:

date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();

Le * 60000 indique l'UTC -6 qui est CST donc quel que soit TimeZone nécessaire, le nombre et la différence peuvent être modifiés.

Stephen Romero
la source
Ce n'est pas la réponse que l'interrogateur cherche. Il cherchait à obtenir la date du fuseau horaire spécifique.
Richard Vergis
@RichardVergis La question indique clairement que l'utilisateur souhaite passer à l'heure locale, mais ne précise pas dans quel fuseau horaire il se trouve. J'ai indiqué mon fuseau horaire à titre d'exemple et l'utilisateur peut lire clairement sur la base de mon exemple, il peut entrer leur décalage horaire.
Stephen Romero
-2

Était confronté au même problème, utilisé celui-ci

Console.log (Date.parse ("13 juin 2018 10:50:39 GMT + 1"));

Il retournera des millisecondes auxquelles vous pouvez vérifier que +100 fuseau horaire initialise l'heure britannique.

sandeep kumar
la source
3
(Ce message ne semble pas fournir une réponse de qualité à la question. Veuillez soit modifier votre réponse, soit simplement la publier en commentaire de la question).
sɐunıɔ ןɐ qɐp