Come inizializzare una data JavaScript su un particolare fuso orario


232

Ho una data in un particolare fuso orario come stringa e voglio convertirla nell'ora locale. Ma non so come impostare il fuso orario nell'oggetto Date.

Ad esempio, Feb 28 2013 7:00 PM ET,allora posso

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

Per quanto ne so, posso impostare l'ora UTC o l'ora locale. Ma come posso impostare l'ora in un altro fuso orario?

Ho provato a utilizzare l'aggiunta / sottrazione dell'offset da UTC ma non so come contrastare l'ora legale. Non sono sicuro se sto andando nella direzione giusta.

Come posso fare per convertire l'ora da un fuso orario diverso all'ora locale in JavaScript?



1
Gli oggetti data non hanno un fuso orario, sono UTC.
RobG

Risposte:


352

sfondo

L' Dateoggetto JavaScript tiene traccia del tempo in UTC internamente, ma in genere accetta input e produce output nell'ora locale del computer su cui è in esecuzione. Ha pochissime strutture per lavorare con il tempo in altri fusi orari.

La rappresentazione interna di un Dateoggetto è un singolo numero, che rappresenta il numero di millisecondi trascorsi da allora 1970-01-01 00:00:00 UTC, senza considerare i secondi bisestili. Non esiste alcun fuso orario o formato stringa memorizzato nell'oggetto Date stesso. Quando Datevengono utilizzate varie funzioni dell'oggetto, il fuso orario locale del computer viene applicato alla rappresentazione interna. Se la funzione produce una stringa, è possibile prendere in considerazione le informazioni sulla locale del computer per determinare come produrre quella stringa. I dettagli variano in base alla funzione e alcuni sono specifici dell'implementazione.

Le uniche operazioni che l' Dateoggetto può fare con fusi orari non locali sono:

  • Può analizzare una stringa contenente un offset UTC numerico da qualsiasi fuso orario. Lo utilizza per regolare il valore analizzato e memorizza l'equivalente UTC. L'ora locale originale e l'offset non vengono mantenuti Datenell'oggetto risultante . Per esempio:

    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)
  • In ambienti che hanno implementato l' API di internazionalizzazione ECMASCript (aka "Intl"), un Dateoggetto può produrre una stringa specifica della locale adattata a un determinato identificatore di fuso orario. Ciò si ottiene tramite l' timeZoneopzione toLocaleStringe le sue variazioni. La maggior parte delle implementazioni supporterà gli identificatori di fuso orario IANA, come 'America/New_York'. Per esempio:

    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 maggior parte degli ambienti moderni supporta l'intero set di identificatori di fuso orario IANA ( vedere la tabella di compatibilità qui ). Tuttavia, tieni presente che l'unico identificativo richiesto per essere supportato da Intl è 'UTC', quindi dovresti controllare attentamente se è necessario supportare browser più vecchi o ambienti atipici (ad esempio, dispositivi IoT leggeri).

biblioteche

Esistono diverse librerie che possono essere utilizzate per lavorare con i fusi orari. Sebbene non riescano ancora a far Datecomportare diversamente l' oggetto, in genere implementano il database del fuso orario IANA standard e forniscono funzioni per utilizzarlo in JavaScript. Le librerie moderne utilizzano i dati del fuso orario forniti dall'API Intl, ma le librerie più vecchie in genere hanno un sovraccarico, soprattutto se si esegue in un browser Web, poiché il database può diventare un po 'grande. Alcune di queste librerie consentono anche di ridurre selettivamente il set di dati, in base al quale i fusi orari sono supportati e / o in base all'intervallo di date con cui è possibile lavorare.

Ecco le librerie da considerare:

Librerie basate su Intl

Il nuovo sviluppo dovrebbe scegliere tra una di queste implementazioni, che si basano sull'API Intl per i dati relativi al fuso orario:

Librerie non internazionali

Queste librerie sono mantenute, ma comportano l'onere di impacchettare i propri dati di fuso orario, che possono essere piuttosto grandi.

* Mentre Moment e Moment-Timezone erano stati precedentemente raccomandati, il team Moment ora preferisce che gli utenti abbiano scelto Luxon per un nuovo sviluppo.

Librerie fuori produzione

Queste librerie sono state ufficialmente interrotte e non dovrebbero più essere utilizzate.

Proposte future

La proposta temporale TC39 mira a fornire un nuovo set di oggetti standard per lavorare con date e orari nel linguaggio JavaScript stesso. Ciò includerà il supporto per un oggetto sensibile al fuso orario.


1
Si prega di cambiare "rappresentare" in "output / analisi", poiché i timestamp rappresentati sono indipendenti dal fuso orario
Bergi

1
@Bergi - Ci ho ripensato e sono d'accordo con te. Aggiornato di conseguenza la mia risposta.
Matt Johnson-Pint,

1
Quando lo fai nella console Firebug:, var date_time = new Date()il valore di date_timeè (per me in AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}e quindi quindi sembra che abbia memorizzato un fuso orario. Come posso assicurarmi che date_timesia puramente l'ora UTC, senza alcun fuso orario incluso?
user1063287

Hmm, secondo questa risposta ( stackoverflow.com/a/8047885/1063287 ), è questo: new Date().getTime();
user1063287

1
@ user1063287: il metodo toString utilizza l'offset del fuso orario dell'host per produrre una data e un'ora nel fuso orario "locale". L'oggetto data stesso ha un valore di ora che è un offset da 1970-01-01T00: 00: 00Z, quindi efficacemente UTC. Per visualizzare la data e l'ora UTC, utilizzare toISOString .
RobG

69

Come ha detto Matt Johnson

Se è possibile limitare l'utilizzo ai moderni browser Web, è ora possibile effettuare le seguenti operazioni senza librerie speciali:

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

Questa non è una soluzione completa, ma funziona per molti scenari che richiedono solo la conversione dell'output (dall'ora locale o UTC a un fuso orario specifico, ma non nell'altra direzione).

Quindi, sebbene il browser non sia in grado di leggere i fusi orari IANA durante la creazione di una data o abbia dei metodi per cambiare i fusi orari su un oggetto Date esistente, sembra esserci un hack attorno:

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()}`);


6
Mi chiedo perché questa risposta non ottenga più amore. Per i miei scopi è assolutamente la soluzione migliore. Nella mia app, tutte le date sono UTC, ma devono essere immesse o emesse in fusi orari IANA espliciti nell'interfaccia utente. A tale scopo utilizzo questa soluzione e funziona perfettamente. Semplice, efficace, standard.
logidelic,

2
@logidelic è un post piuttosto nuovo. ad essere sincero, mi sono stupito quando mi ha colpito che si poteva usare toLocaleStringper ottenere l'effetto contrario, e sì, questa dovrebbe essere conoscenza comune! vai a scrivere un articolo a riguardo e menzionami :-)
commonpike,

1
Per un ambiente nodo che non è già in GMT, il codice sopra deve cambiare da var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); in var invdate = new Date ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.

5
I browser non sono tenuti ad analizzare l'output di toLocaleString , quindi si basa su una premessa errata.
RobG

3
"... perché questa risposta non sta ottenendo più amore?" - perché l' Dateoggetto che restituisce è una bugia. Anche nell'esempio mostrato, l'output della stringa include il fuso orario della macchina locale. Sul mio computer, entrambi i risultati dicono "GMT-0700 (Pacific Daylight Time)", dove questo è corretto solo per il primo (Toronto è in realtà nell'ora orientale.) Oltre all'output della stringa, il timestamp all'interno Datedell'oggetto è stato spostato in un altro momento. Questa può essere una tecnica utile, ma viene fornita con molti avvertimenti, principalmente che Datele funzioni degli oggetti non avranno un output normale.
Matt Johnson-Pint,

30

È possibile specificare un fuso orario attivato new Date() , ad esempio:

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

o

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

Poiché Datememorizza l'ora UTC (ovvero getTimerestituisce UTC), javascript convertirà l'ora in UTC e quando chiami cose cometoString javascript convertirà l'ora UTC nel fuso orario locale del browser e restituirà la stringa nel fuso orario locale, cioè se sto usando UTC+8:

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

Inoltre puoi usare normale getHours/Minute/Second metodo:

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

(Ciò 8significa che dopo che l'ora è stata convertita nella mia ora locale UTC+8, il numero delle ore è 8.)


16
L'analisi di qualsiasi formato diverso dal formato esteso ISO 8601 dipende dall'implementazione e non deve essere invocata. Non esiste uno standard per le abbreviazioni del fuso orario, ad esempio "EST" potrebbe rappresentare una delle 3 zone diverse.
RobG,

1
Gli esempi in questo commento spesso non funzionano in Internet Explorer. Come menzionato nel precedente commento a questo post, ISO 8601 è importante. Ho confermato leggendo la specifica del linguaggio ECMA-262 (Javascript 5th) edition.
Dakusan,

12

Questo dovrebbe risolvere il tuo problema, non esitare a offrire soluzioni. Questo metodo terrà conto anche dell'ora legale per la data specificata.

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

Come utilizzare con fuso orario e ora locale:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)

Questa soluzione era adatta alle mie esigenze. Non ero sicuro di come esattamente posso tenere conto dell'ora legale.
Hossein Amin,

Perché non tornare tzDatedirettamente?
levi

8

Ho trovato il modo più supportato per farlo, senza preoccuparmi di una libreria di terze parti, utilizzando il getTimezoneOffsetcalcolo del timestamp appropriato o l'aggiornamento dell'ora, quindi utilizzare i metodi normali per ottenere la data e l'ora necessarie.

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

MODIFICARE

In precedenza utilizzavo UTCmetodi quando eseguivo le trasformazioni di data, il che era errato. Con l'aggiunta dell'offset all'ora, l'utilizzo delle getfunzioni locali restituirà i risultati desiderati.


Dove hai calcolato timezone_offset?
Anand Somani,

1
Oops, ho etichettato erroneamente una delle mie variabili. timezone_offsetavrebbe dovuto essere offset, o viceversa. Ho modificato la mia risposta per mostrare il nome della variabile corretto.
Shaun Cockerill,

3

Mi sono imbattuto in un problema simile con i test unitari (in particolare per scherzo quando i test unitari vengono eseguiti localmente per creare gli snapshot e quindi il server CI esegue (potenzialmente) un fuso orario diverso causando il fallimento del confronto degli snapshot). Ho deriso il nostro Datee alcuni dei metodi di supporto in questo modo:

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

});


2

Sulla base delle risposte di cui sopra, sto usando questo liner nativo per convertire la stringa del fuso orario lungo nella stringa di tre lettere:

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

Questo darà PDT o PST a seconda della data fornita. Nel mio caso d'uso specifico, sviluppando su Salesforce (Aura / Lightning), siamo in grado di ottenere il fuso orario dell'utente nel formato lungo dal back-end.


Solo perché mi piace giocare a pedante di tanto in tanto, "America / Los_Angeles" non è un fuso orario, è una "posizione rappresentativa" per un particolare offset e le regole dell'ora legale e la cronologia di tali cambiamenti (per il database dei fusi orari IANA ) . ;-)
RobG

Haha, devi pubblicare quel commento su più risposte qui: P
Shane

2

Conosco i suoi 3 anni troppo tardi, ma forse può aiutare qualcun altro perché non ho trovato nulla del genere tranne la biblioteca del fuso orario momento, che non è esattamente la stessa cosa che sta chiedendo qui.

Ho fatto qualcosa di simile per il fuso orario tedesco, questo è un po 'complesso a causa dell'ora legale e degli anni bisestili in cui hai 366 giorni.

potrebbe essere necessario un po 'di lavoro con la funzione "isDaylightSavingTimeInGermany" mentre fusi orari diversi cambiano in tempi diversi rispetto all'ora legale.

comunque, controlla questa pagina: https://github.com/zerkotin/german-timezone-converter/wiki

i metodi principali sono: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Ho fatto uno sforzo per documentarlo, quindi non sarà così confuso.


Questo dovrebbe essere un commento, non è una risposta.
RobG

1

Prova: data-da-fuso orario , risolve la data prevista con l'aiuto di nativamente disponibiliIntl.DateTimeFormat .

Ho usato quel metodo in uno dei miei progetti già da alcuni anni, ma ora ho deciso di pubblicarlo come piccolo progetto OS :)


1

Forse questo ti aiuterà

/**
 * 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')
);


0

Per gli utenti ionici, ho avuto un inferno con questo perché .toISOString() deve essere usato con il modello html.

Questo prenderà la data corrente, ma ovviamente può essere aggiunto alle risposte precedenti per una data selezionata.

Ho risolto usando questo:

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

* 60000 indica UTC -6 che è CST, quindi qualunque sia il fuso orario necessario, il numero e la differenza possono essere modificati.


Questa non è la risposta che l'interrogante sta cercando. Stava cercando di ottenere la data per il fuso orario specifico.
Richard Vergis,

@RichardVergis La domanda indica chiaramente che l'utente vuole passare all'ora locale, ma non indica in quale fuso orario si trova. Ho indicato il mio fuso orario come esempio e l'utente può leggere chiaramente in base al mio esempio, possono inserire il loro fuso orario offset.
Stephen Romero,

-2

Stava affrontando lo stesso problema, usato questo

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

Restituirà millisecondi a cui puoi verificare che +100 timzone inizializzino il tempo britannico Spero che sia d'aiuto !!


3
(Questo post non sembra fornire una risposta di qualità alla domanda. Modifica la tua risposta o semplicemente pubblicala come commento alla domanda).
sɐunıɔ ןɐ qɐp
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.