So ignorieren Sie die Zeitzone des Benutzers und erzwingen, dass Date () eine bestimmte Zeitzone verwendet

104

In einer JS-App erhalte ich einen Zeitstempel (Gl. 1270544790922) Vom Server (Ajax).

Basierend auf diesem Zeitstempel erstelle ich ein DateObjekt mit:

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

Jetzt _datedekodierter Zeitstempel in der aktuellen Zeitzone des Benutzergebietsschemas. Das will ich nicht

Ich möchte _ datediesen Zeitstempel in die aktuelle Zeit in der Stadt Helsinki in Europa konvertieren (ohne Berücksichtigung der aktuellen Zeitzone des Benutzers).

Wie kann ich das machen?

Warpech
quelle
Ich weiß, dass der Zeitzonenversatz in Helsinki im Winter +2 und in der Sommerzeit +3 beträgt. Aber wer weiß wann wann Sommerzeit ist? Nur einige Gebietsschema-Mechanismen, die in JS
Warpech
Es ist möglich, aber keine nativen Methoden von Javascript zu verwenden, da Javascript keine Methode zum Bestimmen eines Zeitzonenübergangsverlaufs einer anderen Zeitzone als der aktuellen Zeitzone des Benutzersystems hat (und es ist übrigens zumindest vom Browser abhängig, wenn wir zu den Daten der 80er gehen). Aber auf diese Weise ist es möglich: stackoverflow.com/a/12814213/1691517 und ich denke, dass meine Antwort Ihnen das richtige Ergebnis liefert.
Timo Kähkönen

Antworten:

64

Der zugrunde liegende Wert eines Datumsobjekts ist tatsächlich in UTC. Um dies zu beweisen, beachten Sie, dass beim new Date(0)Tippen Folgendes angezeigt wird : Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 wird in GMT als 0 behandelt, aber die .toString()Methode zeigt die Ortszeit an.

UTC steht für Universal Time Code. Die aktuelle Zeit an 2 verschiedenen Orten ist dieselbe UTC, aber die Ausgabe kann unterschiedlich formatiert werden.

Was wir hier brauchen, ist eine Formatierung

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"

Dies funktioniert, aber ... Sie können keine der anderen Datumsmethoden für Ihre Zwecke verwenden, da sie die Zeitzone des Benutzers beschreiben. Was Sie wollen, ist ein Datumsobjekt, das mit der Zeitzone von Helsinki zusammenhängt. Zu diesem Zeitpunkt können Sie eine Bibliothek eines Drittanbieters verwenden (ich empfehle dies) oder das Datumsobjekt hacken, damit Sie die meisten seiner Methoden verwenden können.

Option 1 - eine Drittanbieter-ähnliche Moment-Zeitzone

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

Das sieht viel eleganter aus als das, was wir als nächstes tun werden.

Option 2 - Hacken Sie das Datumsobjekt

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)

Es glaubt immer noch, es sei GMT-0700 (PDT), aber wenn Sie nicht zu stark starren, können Sie dies möglicherweise mit einem Datumsobjekt verwechseln, das für Ihre Zwecke nützlich ist.

Ich habe bequemerweise einen Teil übersprungen. Sie müssen definieren können currentHelsinkiOffset. Wenn Sie date.getTimezoneOffset()auf der Serverseite oder nur einige if-Anweisungen verwenden können, um zu beschreiben, wann die Zeitzonenänderungen auftreten, sollte dies Ihr Problem lösen.

Fazit - Ich denke, speziell für diesen Zweck sollten Sie eine Datumsbibliothek wie Moment-Zeitzone verwenden .

Parris
quelle
auch ... eine ähnliche Aufgabe kann erledigt werden, indem einfach der gmt-Offset von einer Stelle verwendet wird. In diesem Fall benötigen Sie überhaupt kein Javascript.
Parris
Entschuldigung, aber nein, ich meine das genaue Gegenteil :) Ich habe die Frage bearbeitet, vielleicht ist es jetzt klarer
Warpech
Ok, ich habe meine Lösung geändert. Ich denke, das ist es, wonach Sie suchen.
Parris
Leider ist das das einzige, was ich mir auch ausgedacht habe. Ich dachte, dass der Browser vielleicht "_helsinkiOffset" für mich generieren könnte.
Warpech
2
Ich glaube, *60*60sollte stattdessen sein *60000, da getTime in Millisekunden und getTimezoneOffset in Minuten ist, von denen 60000 Millisekunden in einer Minute sind, nicht 60 * 60 == 3600
AaronLS
20

Verwenden Sie Folgendes, um Millisekunden und die Zeitzone des Benutzers zu berücksichtigen:

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
quelle
Um die Verwendung eines festen Versatzes für die Zentrale zu ersetzen, habe ich das Konzept verwendet, ein Datum mit CST mit einer festen Zeit von 00:00 zu erstellen und dann die Stunden dieses Datums abzurufen.
Grantwparks
+1 Das hat bei mir funktioniert. Ich bin mir nicht sicher, wie die Antwort ohne Millisekunden funktionieren kann.
Chris Wallis
2
@Ehren sollten Sie nicht das timezoneOffset hinzufügen, um zu gmt zu gelangen, und dann den zentralen Offset subtrahieren?
Codierer
15

Nur ein anderer Ansatz

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

Prost!

Mohanraj Balasubramaniam
quelle
2
Dies kann aufgrund der Sommerzeit zu unbeabsichtigten Ergebnissen führen. Befindet sich der Client in einer Zeitzone, in der die Sommerzeit verwendet wird, kann das Ergebnis der Analyse um eine Stunde verschoben sein (einige Zonen verwenden Bruchteile einer Stunde). Beispiel: Der Benutzer ist in NY und heute ist der 4. Juli. Dies bedeutet, dass sich der Benutzer in GMT -0400 (Eastern Daylight Timezone seit Beginn der Sommerzeit) befindet. Der übergebene Zeitstempel ist für den 30. Januar, GMT-0500 (Eastern Standard Timezone - keine Sommerzeit zu dieser Jahreszeit). Das Ergebnis ist eine Stunde Pause, da Sie mit getTimezoneOffset () jetzt den Offset erhalten, nicht mehr den im Januar.
Dimitar Darazhanski
2
Um dieses Problem zu beheben, müssen Sie den Zeitversatz des Datums nehmen, das Sie übergeben (nicht die aktuelle Zeitverschiebung) : new Date().getTimezoneOffset()sollte innew Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski
13

Ich habe den Verdacht, dass die Antwort nicht das richtige Ergebnis liefert. In der Frage möchte der Fragesteller den Zeitstempel vom Server in die aktuelle Zeit in Hellsinki konvertieren, ohne die aktuelle Zeitzone des Benutzers zu berücksichtigen.

Es ist die Tatsache, dass die Zeitzone des Benutzers diejenige sein kann, auf die wir nicht vertrauen können.

Wenn z. Zeitstempel ist 1270544790922 und wir haben eine Funktion:

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

Wenn ein New Yorker die Seite besucht, wird die Warnung (_helsenkiTime) gedruckt:

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

Und wenn ein Finlander die Seite besucht, wird die Warnung (_helsenkiTime) gedruckt:

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

Die Funktion ist also nur dann korrekt, wenn der Seitenbesucher die Zielzeitzone (Europa / Helsinki) in seinem Computer hat, aber in fast allen anderen Teilen der Welt ausfällt. Und da der Server-Zeitstempel normalerweise ein UNIX-Zeitstempel ist, der per Definition in UTC die Anzahl der Sekunden seit der Unix-Epoche (1. Januar 1970, 00:00:00 GMT) ist, können wir die Sommerzeit oder Nicht-Sommerzeit nicht anhand des Zeitstempels ermitteln.

Die Lösung besteht also darin, die aktuelle Zeitzone des Benutzers zu ignorieren und eine Methode zur Berechnung des UTC-Versatzes zu implementieren, unabhängig davon, ob das Datum in der Sommerzeit liegt oder nicht. Javascript verfügt nicht über eine native Methode zum Bestimmen des DST-Übergangsverlaufs einer anderen Zeitzone als der aktuellen Zeitzone des Benutzers. Dies können wir am einfachsten mithilfe eines serverseitigen Skripts erreichen, da wir einfachen Zugriff auf die Zeitzonendatenbank des Servers mit dem gesamten Übergangsverlauf aller Zeitzonen haben.

Wenn Sie jedoch keinen Zugriff auf die Zeitzonendatenbank des Servers (oder eines anderen Servers) haben UND der Zeitstempel in UTC ist, können Sie die ähnliche Funktionalität erhalten, indem Sie die DST-Regeln in Javascript fest codieren.

Um Daten in den Jahren 1998 - 2099 in Europa / Helsinki abzudecken, können Sie die folgende Funktion verwenden ( 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;
}

Anwendungsbeispiele:

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

Und dies druckt Folgendes unabhängig von der Zeitzone des Benutzers:

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

Wenn Sie den Zeitstempel in einer Form zurückgeben können, in der der Versatz (Sommerzeit oder Nicht-Sommerzeit) bereits zum Zeitstempel auf dem Server hinzugefügt wurde, müssen Sie ihn natürlich nicht clientseitig berechnen und können die Funktion erheblich vereinfachen. ABER denken Sie daran, timezoneOffset () NICHT zu verwenden, da Sie sich dann mit der Zeitzone des Benutzers befassen müssen und dies nicht das gewünschte Verhalten ist.

Timo Kähkönen
quelle
2
nb. Helsinki hat nur ein 'l'. Dieser Fehler lenkt wirklich von dieser Antwort ab.
Ben McIntyre
@ BenMcIntyre Es ist ein kleiner Witz. Oder sollte so einer sein. :)
Timo Kähkönen
Ah, codieren Sie niemals Ihre eigene Zeit- / Zeitzonenimplementierung ... aber ich bin zu faul, um -1
mb21
3

Vorausgesetzt, Sie erhalten den Zeitstempel in Helsinki-Zeit, würde ich ein Datumsobjekt erstellen, das auf Mitternacht am 1. Januar 1970 UTC festgelegt ist (um die lokalen Zeitzoneneinstellungen des Browsers nicht zu berücksichtigen). Fügen Sie dann einfach die erforderliche Anzahl von Millisekunden hinzu.

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

Achten Sie später darauf, immer UTC-Werte vom Datumsobjekt abzufragen. Auf diese Weise sehen Benutzer unabhängig von den lokalen Einstellungen dieselben Datumswerte. Andernfalls werden Datumswerte entsprechend den lokalen Zeiteinstellungen verschoben.

Tibor
quelle
0

Du könntest benutzen setUTCMilliseconds()

var _date = new Date();
_date.setUTCMilliseconds(1270544790922);
Jimasun
quelle
Diese Antwort ist falsch. setUTCMilliseconds fügt dem Datum die Anzahl der angegebenen Millisekunden hinzu.
Tibor
@Tibor: Aus MozDev: Die setUTCMilliseconds()Methode legt die Millisekunden für ein bestimmtes Datum entsprechend der Weltzeit fest. [...] Wenn ein von Ihnen angegebener Parameter außerhalb des erwarteten Bereichs liegt, wird setUTCMilliseconds()versucht, die Datumsinformationen im Date-Objekt entsprechend zu aktualisieren. Mit anderen Worten, Sie erstellen Dateum UTC ein Objekt mit dem angegebenen Unix-Zeitstempel.
Jimasun
Von w3schools: Die Methode setUTCMilliseconds () legt die Millisekunden (von 0 bis 999) entsprechend der Weltzeit fest. Bitte versuchen Sie die Antwort oben und überzeugen Sie sich selbst.
Tibor
Von MDN: Parameter MillisekundenWert: Eine Zahl zwischen 0 und 999, die die Millisekunden darstellt ...
Tibor
Ihr Beispiel funktioniert nicht wie erwartet, da new Date () ein Datumsobjekt mit dem aktuellen lokalen Datum erstellt. Danach wird die angegebene Anzahl von Millisekunden hinzugefügt. Wenn in Ihrem Beispiel das aktuelle lokale Datum 2017-05-04 ist, liegt das resultierende Datum irgendwo nach dem Jahr 4027 ...
Tibor