Come ignorare il fuso orario dell'utente e forzare l'uso di Date () in un fuso orario specifico


104

In un'app JS, ricevo il timestamp (eq. 1270544790922) Dal server (Ajax).

Basandomi su quel timestamp creo un Dateoggetto usando:

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

Ora, _datetimestamp decodificato nel fuso orario locale dell'utente corrente. Non lo voglio.

Vorrei dateconvertire questo timestamp nell'ora corrente nella città di Helsinki in Europa (ignorando il fuso orario corrente dell'utente).

Come lo posso fare?


So che la differenza di fuso orario a Helsinki è +2 in inverno e +3 in DST. Ma chissà quando è l'ora legale? Solo alcuni meccanismi locali che non sono disponibili in JS
warpech

È possibile, ma non utilizzando metodi nativi di Javascript, perché JavaScript non ha alcun metodo per determinare una cronologia di transizione del fuso orario di un fuso orario diverso dal fuso orario corrente del sistema dell'utente (ed è comunque dipendente dal browser almeno quando andiamo alle date degli anni '80). Ma in questo modo è possibile: stackoverflow.com/a/12814213/1691517 e penso che la mia risposta ti dia il risultato corretto.
Timo Kähkönen

Risposte:


64

Il valore sottostante di un oggetto Date è effettivamente in UTC. Per dimostrare questo, notare che se si digita new Date(0)vedrete qualcosa di simile a: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 viene considerato come 0 in GMT, ma il .toString()metodo mostra l'ora locale.

Nota importante, UTC sta per codice temporale universale . L'ora corrente in questo momento in 2 luoghi diversi è la stessa UTC, ma l'output può essere formattato in modo diverso.

Ciò di cui abbiamo bisogno qui è un po 'di formattazione

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"

Funziona ma ... non puoi davvero usare nessuno degli altri metodi di data per i tuoi scopi poiché descrivono il fuso orario dell'utente. Quello che vuoi è un oggetto data correlato al fuso orario di Helsinki. Le tue opzioni a questo punto sono utilizzare alcune librerie di terze parti (lo consiglio) o hackerare l'oggetto data in modo da poter utilizzare la maggior parte dei suoi metodi.

Opzione 1: una terza parte come il fuso orario del momento

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

Questo sembra molto più elegante di quello che stiamo per fare dopo.

Opzione 2: hackerare l'oggetto data

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)

Pensa ancora che sia GMT-0700 (PDT), ma se non lo fissi troppo potresti essere in grado di scambiarlo per un oggetto data utile per i tuoi scopi.

Ho opportunamente saltato una parte. Devi essere in grado di definire currentHelsinkiOffset. Se puoi usare date.getTimezoneOffset()sul lato server, o semplicemente usare alcune istruzioni if ​​per descrivere quando si verificheranno i cambiamenti di fuso orario, questo dovrebbe risolvere il tuo problema.

Conclusione - Penso che soprattutto per questo scopo dovresti usare una libreria di date come momento-fuso orario .


inoltre ... un'attività simile può essere eseguita, semplicemente utilizzando l'offset gmt da una posizione. In questo caso non hai bisogno di JavaScript.
Parris

scusa ma no, intendo l'esatto contrario :) Ho modificato la domanda, forse ora è più chiara
warpech

Ok ho cambiato la mia soluzione. Penso che questo sia quello che stai cercando.
Parris

Purtroppo è l'unica cosa che mi è venuta in mente. Ho pensato che forse il browser potrebbe generare "_helsinkiOffset" per me.
warpech

2
Credo che *60*60dovrebbe invece essere *60000, dato che getTime è in millisecondi e getTimezoneOffset è in minuti, di cui 60000 millisecondi in un minuto, non 60 * 60 == 3600
AaronLS

20

Per tenere conto dei millisecondi e del fuso orario dell'utente, utilizza quanto segue:

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

Per sostituire utilizzando un offset fisso per centrale, ho utilizzato il concetto di creare una data utilizzando CST con un orario fisso di 00:00, quindi getUTCHHours di quella data.
grantwparks

+1 Questo ha funzionato per me. Non sono sicuro di come "la" risposta possa funzionare senza trattare in millisecondi.
Chris Wallis

2
@Ehren non dovresti aggiungere il timezoneOffset per arrivare a gmt e quindi sottrarre l'offset centrale?
coder

15

Solo un altro approccio

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

Saluti!


2
Ciò potrebbe avere risultati imprevisti a causa dell'ora legale. Se il client si trova in un fuso orario che utilizza l'ora legale, il risultato dell'analisi potrebbe essere disattivato di un'ora (alcune zone utilizzano frazioni di ora). Ad esempio: l'utente è a New York e oggi è il 4 luglio. Ciò significa che l'utente è in GMT -0400 (fuso orario dell'ora legale orientale da quando è entrato in vigore l'ora legale); il timestamp passato è per il 30 gennaio, che è GMT-0500 (fuso orario standard orientale - nessun DST in quel periodo dell'anno). Il risultato sarà un'ora di pausa, perché getTimezoneOffset () ti dà l'offset ora, non quello che era a gennaio.
Dimitar Darazhanski

2
Per risolvere questo problema, è necessario prendere la differenza di orario della data in cui si sta passando (non il fuso orario corrente) : new Date().getTimezoneOffset()dovrebbe essere cambiato innew Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski

13

Ho il sospetto che la risposta non dia il risultato corretto. Nella domanda il richiedente vuole convertire il timestamp dal server all'ora corrente a Hellsinki ignorando il fuso orario corrente dell'utente.

È il fatto che il fuso orario dell'utente può essere qualsiasi cosa, quindi non possiamo fidarci di esso.

Se ad es. timestamp è 1270544790922 e abbiamo una funzione:

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

Quando un newyorkese visita la pagina, viene stampato l'avviso (_helsenkiTime):

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

E quando un finlandese visita la pagina, viene stampato l'avviso (_helsenkiTime):

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

Quindi la funzione è corretta solo se il visitatore della pagina ha il fuso orario di destinazione (Europa / Helsinki) nel suo computer, ma fallisce in quasi ogni altra parte del mondo. E poiché il timestamp del server è solitamente UNIX timestamp, che è per definizione in UTC, il numero di secondi dall'Epoch Unix (1 gennaio 1970 00:00:00 GMT), non possiamo determinare l'ora legale o non DST dal timestamp.

Quindi la soluzione è NON RICORDARE il fuso orario corrente dell'utente e implementare un modo per calcolare l'offset UTC indipendentemente dal fatto che la data sia in DST o meno. Javascript non ha un metodo nativo per determinare la cronologia delle transizioni dell'ora legale di un fuso orario diverso da quello corrente dell'utente. Possiamo ottenere questo risultato più semplicemente utilizzando uno script lato server, perché abbiamo un facile accesso al database del fuso orario del server con l'intera cronologia delle transizioni di tutti i fusi orari.

Ma se non hai accesso al database del fuso orario del server (o di qualsiasi altro server) E il timestamp è in UTC, puoi ottenere la funzionalità simile codificando le regole DST in Javascript.

Per coprire le date negli anni 1998-2099 in Europa / Helsinki è possibile utilizzare la seguente funzione ( 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;
}

Esempi di utilizzo:

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

E questo stampa quanto segue indipendentemente dal fuso orario dell'utente:

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

Ovviamente se puoi restituire il timestamp in una forma in cui l'offset (DST o non DST) è già aggiunto al timestamp sul server, non devi calcolarlo dal lato client e puoi semplificare molto la funzione. MA ricordati di NON usare timezoneOffset (), perché allora devi avere a che fare con il fuso orario dell'utente e questo non è il comportamento desiderato.


2
nb. Helsinki ha solo una "l". Questo errore sminuisce davvero questa risposta.
Ben McIntyre

@BenMcIntyre È un piccolo scherzo. O dovrebbe essere tale. :)
Timo Kähkönen

ah, non codificare mai la tua implementazione di ora / fuso orario ... ma sono troppo pigro per -1
mb21

3

Presumendo che tu ottenga il timestamp nell'ora di Helsinki, creerei un oggetto data impostato sulla mezzanotte del 1 gennaio 1970 UTC (per ignorare le impostazioni del fuso orario locale del browser). Quindi aggiungi semplicemente il numero necessario di millisecondi.

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

Fai attenzione in seguito, per chiedere sempre i valori UTC all'oggetto data. In questo modo gli utenti vedranno gli stessi valori di data indipendentemente dalle impostazioni locali. In caso contrario, i valori della data verranno spostati in base alle impostazioni dell'ora locale.


0

Potresti usare setUTCMilliseconds()

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

Questa risposta è sbagliata. setUTCMilliseconds aggiunge il numero di millisecondi specificati a Date.
Tibor

@Tibor: da MozDev: il setUTCMilliseconds()metodo imposta i millisecondi per una data specificata in base all'ora universale. [...] Se un parametro specificato è al di fuori dell'intervallo previsto, setUTCMilliseconds()tenta di aggiornare di conseguenza le informazioni sulla data nell'oggetto Date. In altre parole, crei un Dateoggetto con il timestamp Unix specificato, in UTC.
jimasun

Da w3schools: Il metodo setUTCMilliseconds () imposta i millisecondi (da 0 a 999), in base al tempo universale. Prova la risposta sopra e guarda tu stesso.
Tibor

Da MDN: Parametri millisecondsValue: un numero compreso tra 0 e 999, che rappresenta i millisecondi ...
Tibor

Il tuo esempio non funziona come previsto, perché new Date () crea un oggetto data con la data locale corrente. E poi aggiunge il numero di millisecondi specificato. Quindi, nel tuo esempio, se la data locale corrente è 2017-05-04, la data risultante sarà da qualche parte dopo l'anno 4027 ...
Tibor
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.