Comment ignorer le fuseau horaire de l'utilisateur et forcer la date () à utiliser un fuseau horaire spécifique

104

Dans une application JS, je reçois l'horodatage (eq. 1270544790922) Du serveur (Ajax).

Sur la base de cet horodatage, je crée un Dateobjet en utilisant:

var _date = new Date();
_date.setTime(1270544790922);

Désormais, _datehorodatage décodé dans le fuseau horaire des paramètres régionaux de l'utilisateur actuel. Je ne veux pas de ça.

Je voudrais _ dateconvertir cet horodatage en heure actuelle dans la ville d'Helsinki en Europe (sans tenir compte du fuseau horaire actuel de l'utilisateur).

Comment puis je faire ça?

warpech
la source
Je sais que le décalage horaire à Helsinki est +2 en hiver et +3 en heure d'été. Mais qui sait quand est l'heure d'été? Seulement un mécanisme de locale qui n'est pas disponible dans JS
warpech
C'est possible, mais pas en utilisant les méthodes natives de Javascript, car javascript n'a pas de méthode pour déterminer un historique de transition de fuseau horaire d'un autre fuseau horaire que le fuseau horaire actuel du système de l'utilisateur (et cela dépend au moins du navigateur lorsque nous passons aux dates des années 80). Mais de cette façon c'est possible: stackoverflow.com/a/12814213/1691517 et je pense que ma réponse vous donne le résultat correct.
Timo Kähkönen

Réponses:

64

La valeur sous-jacente d'un objet Date est en fait en UTC. Pour le prouver, notez que si vous tapez , new Date(0)vous verrez quelque chose comme: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 est traité comme 0 en GMT, mais la .toString()méthode affiche l'heure locale.

Remarque importante, UTC signifie code horaire universel . L'heure actuelle à 2 endroits différents est la même heure UTC, mais la sortie peut être formatée différemment.

Ce dont nous avons besoin ici, c'est d'un formatage

var _date = new Date(1270544790922); 
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"

Cela fonctionne mais ... vous ne pouvez pas vraiment utiliser les autres méthodes de date pour vos besoins puisqu'elles décrivent le fuseau horaire de l'utilisateur. Ce que vous voulez, c'est un objet de date lié au fuseau horaire d'Helsinki. Vos options à ce stade sont d'utiliser une bibliothèque tierce (je le recommande), ou de pirater l'objet de date afin que vous puissiez utiliser la plupart de ses méthodes.

Option 1 - une tierce partie comme moment-fuseau horaire

moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12

Cela semble beaucoup plus élégant que ce que nous sommes sur le point de faire ensuite.

Option 2 - Pirater l'objet de date

var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)

Il pense toujours que c'est GMT-0700 (PDT), mais si vous ne regardez pas trop fort, vous pourrez peut-être confondre cela avec un objet de date utile pour vos besoins.

J'ai facilement sauté une partie. Vous devez être capable de définir currentHelsinkiOffset. Si vous pouvez utiliser date.getTimezoneOffset()du côté serveur, ou simplement utiliser des instructions if pour décrire quand les changements de fuseau horaire se produiront, cela devrait résoudre votre problème.

Conclusion - Je pense que spécialement à cette fin, vous devriez utiliser une bibliothèque de dates comme moment-fuseau horaire .

Parris
la source
aussi ... une tâche similaire peut être effectuée, en utilisant simplement le décalage gmt à partir d'un emplacement. Vous n'avez pas du tout besoin de JavaScript dans ce cas.
Parris
désolé mais non, je veux dire exactement le contraire :) J'ai édité la question, peut-être qu'elle est plus claire maintenant
warpech
Ok j'ai changé ma solution. Je pense que c'est ce que vous recherchez.
Parris
Malheureusement, c'est la seule chose que j'ai également proposée. J'ai pensé que le navigateur pourrait peut-être générer "_helsinkiOffset" pour moi.
warpech
2
Je pense que cela *60*60devrait plutôt l'être *60000, car getTime est en millisecondes et getTimezoneOffset est en minutes, dont 60000 millisecondes dans une minute, pas 60 * 60 == 3600
AaronLS
20

Pour prendre en compte les millisecondes et le fuseau horaire de l'utilisateur, utilisez ce qui suit:

var _userOffset = _date.getTimezoneOffset()*60*1000; // user's offset time
var _centralOffset = 6*60*60*1000; // 6 for central time - use whatever you need
_date = new Date(_date.getTime() - _userOffset + _centralOffset); // redefine variable
Ehren
la source
Pour remplacer en utilisant un décalage fixe pour central, j'ai utilisé le concept de création d'une date en utilisant CST avec une heure fixe de 00:00, puis getUTCHHours de cette date.
grantwparks
+1 Cela a fonctionné pour moi. Je ne sais pas comment «la» réponse peut fonctionner sans traiter en millisecondes.
Chris Wallis
2
@Ehren ne devriez-vous pas ajouter le timezoneOffset pour accéder à gmt, puis soustraire le décalage central?
coder
15

Juste une autre approche

function parseTimestamp(timestampStr) {
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000));
};

//Sun Jan 01 2017 12:00:00
var timestamp = 1483272000000;
date = parseTimestamp(timestamp);
document.write(date);

À votre santé!

Mohanraj Balasubramaniam
la source
2
Cela pourrait avoir des résultats inattendus en raison de l'heure d'été. Si le client est dans un fuseau horaire qui utilise DST, le résultat de l'analyse peut être décalé d'une heure (certaines zones utilisent des fractions d'heure). Exemple: l'utilisateur est à New York et c'est aujourd'hui le 4 juillet. Cela signifie que l'utilisateur est au GMT -0400 (fuseau horaire d'été depuis le début de l'heure d'été); L'horodatage passé est le 30 janvier, qui est GMT-0500 (fuseau horaire standard de l'Est - pas d'heure d'été à cette période de l'année). Le résultat sera une heure de congé, car getTimezoneOffset () vous donne le décalage maintenant, pas ce qu'il était en janvier.
Dimitar Darazhanski
2
Pour résoudre ce problème, vous devez prendre le décalage horaire de la date à laquelle vous passez (pas le décalage horaire actuel) : new Date().getTimezoneOffset()devrait être remplacé parnew Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski
13

Je soupçonne que la réponse ne donne pas le résultat correct. Dans la question, le demandeur souhaite convertir l'horodatage du serveur en heure actuelle à Hellsinki sans tenir compte du fuseau horaire actuel de l'utilisateur.

C'est le fait que le fuseau horaire de l'utilisateur peut être ce que jamais nous ne pouvons pas lui faire confiance.

Si par exemple. l'horodatage est 1270544790922 et nous avons une fonction:

var _date = new Date();
_date.setTime(1270544790922);
var _helsenkiOffset = 2*60*60;//maybe 3
var _userOffset = _date.getTimezoneOffset()*60*60; 
var _helsenkiTime = new Date(_date.getTime()+_helsenkiOffset+_userOffset);

Lorsqu'un New-Yorkais visite la page, l'alerte (_helsenkiTime) s'imprime:

Tue Apr 06 2010 05:21:02 GMT-0400 (EDT)

Et lorsqu'un Finlandais visite la page, l'alerte (_helsenkiTime) s'imprime:

Tue Apr 06 2010 11:55:50 GMT+0300 (EEST)

Ainsi, la fonction n'est correcte que si le visiteur de la page a le fuseau horaire cible (Europe / Helsinki) dans son ordinateur, mais échoue dans presque toutes les autres parties du monde. Et comme l'horodatage du serveur est généralement un horodatage UNIX, qui est par définition en UTC, le nombre de secondes depuis l'époque Unix (1er janvier 1970 00:00:00 GMT), nous ne pouvons pas déterminer l'heure d'été ou non à partir de l'horodatage.

La solution consiste donc à ignorer le fuseau horaire actuel de l'utilisateur et à implémenter un moyen de calculer le décalage UTC, que la date soit à l'heure d'été ou non. Javascript n'a pas de méthode native pour déterminer l'historique de transition DST d'un autre fuseau horaire que le fuseau horaire actuel de l'utilisateur. Nous pouvons y parvenir le plus simplement en utilisant un script côté serveur, car nous avons un accès facile à la base de données des fuseaux horaires du serveur avec l'historique complet des transitions de tous les fuseaux horaires.

Mais si vous n'avez pas accès à la base de données de fuseaux horaires du serveur (ou de tout autre serveur) ET que l'horodatage est en UTC, vous pouvez obtenir la fonctionnalité similaire en codant en dur les règles DST en Javascript.

Pour couvrir les dates des années 1998 à 2099 en Europe / Helsinki, vous pouvez utiliser la fonction suivante ( jsfiddled ):

function timestampToHellsinki(server_timestamp) {
    function pad(num) {
        num = num.toString();
        if (num.length == 1) return "0" + num;
        return num;
    }

    var _date = new Date();
    _date.setTime(server_timestamp);

    var _year = _date.getUTCFullYear();

    // Return false, if DST rules have been different than nowadays:
    if (_year<=1998 && _year>2099) return false;

    // Calculate DST start day, it is the last sunday of March
    var start_day = (31 - ((((5 * _year) / 4) + 4) % 7));
    var SUMMER_start = new Date(Date.UTC(_year, 2, start_day, 1, 0, 0));

    // Calculate DST end day, it is the last sunday of October
    var end_day = (31 - ((((5 * _year) / 4) + 1) % 7))
    var SUMMER_end = new Date(Date.UTC(_year, 9, end_day, 1, 0, 0));

    // Check if the time is between SUMMER_start and SUMMER_end
    // If the time is in summer, the offset is 2 hours
    // else offset is 3 hours
    var hellsinkiOffset = 2 * 60 * 60 * 1000;
    if (_date > SUMMER_start && _date < SUMMER_end) hellsinkiOffset = 
    3 * 60 * 60 * 1000;

    // Add server timestamp to midnight January 1, 1970
    // Add Hellsinki offset to that
    _date.setTime(server_timestamp + hellsinkiOffset);
    var hellsinkiTime = pad(_date.getUTCDate()) + "." + 
    pad(_date.getUTCMonth()) + "." + _date.getUTCFullYear() + 
    " " + pad(_date.getUTCHours()) + ":" +
    pad(_date.getUTCMinutes()) + ":" + pad(_date.getUTCSeconds());

    return hellsinkiTime;
}

Exemples d'utilisation:

var server_timestamp = 1270544790922;
document.getElementById("time").innerHTML = "The timestamp " + 
server_timestamp + " is in Hellsinki " + 
timestampToHellsinki(server_timestamp);

server_timestamp = 1349841923 * 1000;
document.getElementById("time").innerHTML += "<br><br>The timestamp " + 
server_timestamp + " is in Hellsinki " + timestampToHellsinki(server_timestamp);

var now = new Date();
server_timestamp = now.getTime();
document.getElementById("time").innerHTML += "<br><br>The timestamp is now " +
server_timestamp + " and the current local time in Hellsinki is " +
timestampToHellsinki(server_timestamp);​

Et cela imprime ce qui suit quel que soit le fuseau horaire de l'utilisateur:

The timestamp 1270544790922 is in Hellsinki 06.03.2010 12:06:30

The timestamp 1349841923000 is in Hellsinki 10.09.2012 07:05:23

The timestamp is now 1349853751034 and the current local time in Hellsinki is 10.09.2012 10:22:31

Bien sûr, si vous pouvez renvoyer l'horodatage sous une forme que le décalage (DST ou non DST) est déjà ajouté à l'horodatage sur le serveur, vous n'avez pas à le calculer côté client et vous pouvez simplifier beaucoup la fonction. MAIS rappelez-vous de NE PAS utiliser timezoneOffset (), car vous devez alors gérer le fuseau horaire de l'utilisateur et ce n'est pas le comportement souhaité.

Timo Kähkönen
la source
2
nb. Helsinki n'a qu'un seul «l». Cette erreur nuit vraiment à cette réponse.
Ben McIntyre
@BenMcIntyre C'est une petite blague. Ou censé en être un. :)
Timo Kähkönen
ah, ne
codez
3

En supposant que vous obtenez l'horodatage à l'heure d'Helsinki, je créerais un objet de date réglé sur minuit le 1er janvier 1970 UTC (pour ne pas tenir compte des paramètres de fuseau horaire local du navigateur). Ensuite, ajoutez simplement le nombre de millisecondes nécessaire.

var _date	= new Date( Date.UTC(1970, 0, 1, 0, 0, 0, 0) );
_date.setUTCMilliseconds(1270544790922);

alert(_date); //date shown shifted corresponding to local time settings
alert(_date.getUTCFullYear());    //the UTC year value
alert(_date.getUTCMonth());       //the UTC month value
alert(_date.getUTCDate());        //the UTC day of month value
alert(_date.getUTCHours());       //the UTC hour value
alert(_date.getUTCMinutes());     //the UTC minutes value

Attention plus tard, à toujours demander les valeurs UTC à l'objet date. De cette façon, les utilisateurs verront les mêmes valeurs de date indépendamment des paramètres locaux. Sinon, les valeurs de date seront décalées en fonction des paramètres d'heure locale.

Tibor
la source
0

Vous pourriez utiliser setUTCMilliseconds()

var _date = new Date();
_date.setUTCMilliseconds(1270544790922);
Jimasun
la source
Cette réponse est fausse. setUTCMilliseconds ajoute le nombre de millisecondes spécifié à la date.
Tibor
@Tibor: Depuis MozDev: La setUTCMilliseconds()méthode définit les millisecondes pour une date spécifiée en fonction du temps universel. [...] Si un paramètre que vous spécifiez est en dehors de la plage attendue, setUTCMilliseconds()tente de mettre à jour les informations de date dans l'objet Date en conséquence. En d'autres termes, vous créez un Dateobjet ayant l'horodatage Unix donné, à UTC.
jimasun
De w3schools: La méthode setUTCMilliseconds () définit les millisecondes (de 0 à 999), en fonction du temps universel. Veuillez essayer la réponse ci-dessus et voyez par vous-même.
Tibor
De MDN: Paramètres millisecondesValeur: Un nombre entre 0 et 999, représentant les millisecondes ...
Tibor
Votre exemple ne fonctionne pas comme prévu, car new Date () crée un objet date avec la date locale actuelle. Et après cela, ajoute le nombre de millisecondes spécifié. Donc, dans votre exemple, si la date locale actuelle est le 04/05/2017, la date résultante sera quelque part après l'année 4027 ...
Tibor