Como inicializar uma data JavaScript para um fuso horário específico

232

Tenho data e hora em um fuso horário específico como uma sequência e quero convertê-la para a hora local. Mas não sei como definir o fuso horário no objeto Data.

Por exemplo, eu tenho, Feb 28 2013 7:00 PM ET,então eu posso

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

Tanto quanto sei, posso definir a hora UTC ou a hora local. Mas como faço para definir a hora em outro fuso horário?

Tentei usar a adição / subtração do deslocamento do UTC, mas não sei como combater o horário de verão. Não tenho certeza se estou indo na direção certa.

Como posso converter a hora de um fuso horário diferente para a hora local em javascript?

pavanred
fonte
1
Os objetos de data não possuem um fuso horário, são UTC.
RobG

Respostas:

352

fundo

O Dateobjeto JavaScript rastreia o tempo no UTC internamente, mas normalmente aceita entrada e produz saída no horário local do computador em que está sendo executado. Possui muito poucas facilidades para trabalhar com o horário em outros fusos horários.

A representação interna de um Dateobjeto é um número único, representando o número de milissegundos decorridos desde então 1970-01-01 00:00:00 UTC, sem considerar os segundos bissextos. Não há fuso horário ou formato de string armazenado no próprio objeto Date. Quando várias funções do Dateobjeto são usadas, o fuso horário local do computador é aplicado à representação interna. Se a função produzir uma sequência, as informações de localidade do computador poderão ser levadas em consideração para determinar como produzir essa sequência. Os detalhes variam de acordo com a função e alguns são específicos da implementação.

As únicas operações que o Dateobjeto pode realizar com fusos horários não locais são:

  • Ele pode analisar uma string contendo um deslocamento numérico do UTC de qualquer fuso horário. Ele usa isso para ajustar o valor que está sendo analisado e armazena o equivalente ao UTC. A hora local original e o deslocamento não são mantidos no Dateobjeto resultante . Por exemplo:

    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)
  • Em ambientes que implementaram a API de Internacionalização do ECMASCript (também conhecida como "Intl"), um Dateobjeto pode produzir uma sequência específica de localidade ajustada para um determinado identificador de fuso horário. Isso é realizado através da timeZoneopção toLocaleStringe suas variações. A maioria das implementações suporta identificadores de fuso horário da IANA, como 'America/New_York'. Por exemplo:

    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)

    A maioria dos ambientes modernos oferece suporte ao conjunto completo de identificadores de fuso horário da IANA ( consulte a tabela de compatibilidade aqui ). No entanto, lembre-se de que o único identificador necessário para ser suportado pelo Intl é 'UTC', portanto, você deve verificar cuidadosamente se precisa oferecer suporte a navegadores mais antigos ou ambientes atípicos (por exemplo, dispositivos IoT leves).

Bibliotecas

Existem várias bibliotecas que podem ser usadas para trabalhar com fusos horários. Embora eles ainda não possam fazer com que o Dateobjeto se comporte de maneira diferente, eles normalmente implementam o banco de dados de fuso horário da IANA padrão e fornecem funções para usá-lo em JavaScript. Bibliotecas modernas usam os dados de fuso horário fornecidos pela API Intl, mas as bibliotecas mais antigas geralmente têm sobrecarga, especialmente se você estiver executando em um navegador da Web, pois o banco de dados pode ficar um pouco grande. Algumas dessas bibliotecas também permitem reduzir seletivamente o conjunto de dados, pelo horário em que os fusos horários são suportados e / ou pelo intervalo de datas com o qual você pode trabalhar.

Aqui estão as bibliotecas a serem consideradas:

Bibliotecas baseadas em Intl

O novo desenvolvimento deve escolher uma dessas implementações, que dependem da API Intl para seus dados de fuso horário:

Bibliotecas Não Intl

Essas bibliotecas são mantidas, mas carregam o fardo de empacotar seus próprios dados de fuso horário, que podem ser bastante grandes.

* Embora Moment e Moment-Timezone tenham sido recomendados anteriormente, a equipe do Moment agora prefere que os usuários escolham o Luxon para um novo desenvolvimento.

Bibliotecas descontinuadas

Essas bibliotecas foram oficialmente descontinuadas e não devem mais ser usadas.

Propostas futuras

A proposta temporal do TC39 visa fornecer um novo conjunto de objetos padrão para trabalhar com datas e horas no próprio idioma JavaScript. Isso incluirá suporte para um objeto com reconhecimento de fuso horário.

Matt Johnson-Pint
fonte
1
Por favor, altere "representar" a "saída / parse", já que os representados timestamps são-fuso horário independente
Bergi
1
@ Bergi - repensei isso e concordo com você. Atualizei minha resposta de acordo.
Matt Johnson-Pint
1
Quando você fizer isso no console Firebug: var date_time = new Date(), o valor de date_timeé (para mim em Brasília) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}e assim, portanto, parece que tem armazenado um fuso horário. Como posso garantir que date_timea hora UTC é puramente, sem nenhum fuso horário incluído?
user1063287
Hmm, de acordo com esta resposta ( stackoverflow.com/a/8047885/1063287 ), é a seguinte: new Date().getTime();
user1063287
1
@ user1063287 - o método toString usa o deslocamento do fuso horário do host para produzir uma data e hora no fuso horário "local". O objeto de data em si tem um valor de tempo que é um deslocamento de 1970-01-01T00: 00: 00Z, portanto, efetivamente, UTC. Para ver a data e hora do UTC, use toISOString .
RobG
69

Como Matt Johnson disse

Se você pode limitar seu uso a navegadores modernos, agora pode fazer o seguinte sem nenhuma biblioteca especial:

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

Esta não é uma solução abrangente, mas funciona em muitos cenários que requerem apenas conversão de saída (do UTC ou da hora local para um fuso horário específico, mas não na outra direção).

Portanto, embora o navegador não consiga ler os fusos horários da IANA ao criar uma data ou tenha métodos para alterar os fusos horários em um objeto Date existente, parece haver uma invasão:

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

commonpike
fonte
6
Estou me perguntando por que essa resposta não está recebendo mais amor. Para meus propósitos, é absolutamente a melhor solução. No meu aplicativo, todas as datas são UTC, mas precisam ser inseridas ou exibidas em fusos horários explícitos da IANA na interface do usuário. Para esse fim, uso esta solução e ela funciona perfeitamente. Simples, eficaz, padrão.
logidelic 11/02/19
2
@logidelic é um post bastante novo. para ser honesto, fiquei impressionado quando me ocorreu que você poderia usar toLocaleStringpara obter o efeito inverso, e sim, isso deve ser do conhecimento geral! vá escrever um artigo sobre isso e me mencionar :-)
commonpike
1
Para um ambiente de nó que ainda não esteja no GMT, o código acima precisa mudar de var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); em var invdate = new Date ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.
5
Os navegadores não são obrigados a analisar a saída de toLocaleString , por isso é baseada em uma premissa defeituosa.
RobG 8/09/19
3
"... por que esta resposta não está ficando mais amor?" - porque o Dateobjeto que ele retorna é uma mentira. Até o exemplo mostrado, a saída da string inclui o fuso horário da máquina local. No meu computador, ambos os resultados dizem "GMT-0700 (Horário de verão do Pacífico)", onde isso é correto apenas para o primeiro (Toronto está realmente no horário do leste). Além da saída da string, o registro de data e hora contido no Dateobjeto foi mudou para um ponto diferente no tempo. Essa pode ser uma técnica útil, mas vem com muitas ressalvas - principalmente porque as Datefunções do objeto não terão saída normal.
Matt Johnson-Pint
30

Você pode especificar um deslocamento de fuso horário new Date(), por exemplo:

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

ou

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

Desde a Datehora UTC da loja (ou seja, getTimeretorna em UTC), o javascript os converterá em UTC, e quando você chamar coisas como toStringjavascript converterá o horário UTC no fuso horário local do navegador e retornará a string no fuso horário local, ou seja, se eu estiver usando UTC+8:

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

Além disso, você pode usar o getHours/Minute/Secondmétodo normal :

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

(Isso 8significa que após o horário ser convertido em meu horário local - UTC+8, o número de horas é 8.)

maowtm
fonte
16
A análise de qualquer formato que não seja o formato estendido ISO 8601 depende da implementação e não deve ser considerada. Não há padrão para abreviações de fuso horário, por exemplo, "EST" pode representar qualquer uma das 3 zonas diferentes.
RobG
1
Os exemplos neste comentário geralmente não funcionam no Internet Explorer. Conforme mencionado no comentário anterior deste post, a ISO 8601 é importante. Confirmei lendo a especificação da linguagem de edição ECMA-262 (Javascript 5).
Dakusan
12

Isso deve resolver seu problema, fique à vontade para oferecer correções. Este método também considerará o horário de verão para a data especificada.

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

Como usar com fuso horário e hora local:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
galinhas
fonte
Esta solução foi adequada para a minha necessidade. Eu não tinha certeza de como exatamente posso explicar o horário de verão.
Hossein Amin
Por que não apenas retornar tzDatediretamente?
levi
8

Eu achei a maneira mais suportada de fazer isso, sem me preocupar com uma biblioteca de terceiros, usando getTimezoneOffsetpara calcular o carimbo de data / hora apropriado ou atualizar a hora e, em seguida, usar os métodos normais para obter a data e hora necessárias.

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

EDITAR

Eu estava usando UTCmétodos anteriormente ao executar as transformações de data, que estavam incorretas. Ao adicionar o deslocamento à hora, o uso das getfunções locais retornará os resultados desejados.

Shaun Cockerill
fonte
Onde você calculou timezone_offset?
Anand Somani
1
Ops, coloquei uma etiqueta incorreta em uma das minhas variáveis. timezone_offsetdeveria ter sido offset, ou vice-versa. Editei minha resposta para mostrar o nome correto da variável.
Shaun Cockerill
3

Eu tive um problema semelhante com testes de unidade (especificamente em tom de brincadeira quando os testes de unidade são executados localmente para criar os instantâneos e, em seguida, o servidor de CI executa (potencialmente) em um fuso horário diferente, causando falha na comparação de instantâneos. Eu zombei do nosso Datee de alguns dos métodos de suporte como:

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
fonte
2

Tente usar o ctoc da npm. https://www.npmjs.com/package/ctoc_timezone

Ele tem uma funcionalidade simples para alterar os fusos horários (a maioria dos fusos horários é de cerca de 400) e todos os formatos personalizados que você deseja exibir.

Sandeep Chowdary
fonte
2

Com base nas respostas acima, estou usando esse forro nativo para converter a cadeia longa de fuso horário na cadeia de três letras:

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

Isso fornecerá PDT ou PST, dependendo da data fornecida. No meu caso de uso específico, desenvolvendo no Salesforce (Aura / Lightning), podemos obter o fuso horário do usuário no formato longo a partir do back-end.

Shane
fonte
Só porque eu gosto de interpretar pedantes de tempos em tempos, 'America / Los_Angeles' não é um fuso horário, é um "local representativo" para regras específicas de deslocamento e horário de verão e o histórico dessas alterações (de acordo com o banco de dados de fusos horários da IANA ) . ;-)
RobG 23/09/19
Haha, você tem que postar o comentário em várias respostas aqui: P
Shane
2

Sei que são 3 anos tarde demais, mas talvez isso possa ajudar outra pessoa, porque não encontrei nada assim, exceto a biblioteca de fuso horário, que não é exatamente a mesma que ele está pedindo aqui.

Eu fiz algo semelhante para o fuso horário alemão, isso é um pouco complexo devido ao horário de verão e aos anos bissextos em que você tem 366 dias.

pode ser necessário um pouco de trabalho com a função "isDaylightSavingTimeInGermany", enquanto diferentes fusos horários mudam em horários diferentes do horário de verão.

de qualquer forma, confira esta página: https://github.com/zerkotin/german-timezone-converter/wiki

os principais métodos são: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Eu me esforcei para documentá-lo, para que não seja tão confuso.

zerkotin
fonte
Isso deve ser um comentário, não é uma resposta.
`` #
1

Experimente: date-from-timezone , resolve a data esperada com a ajuda de nativamente disponíveisIntl.DateTimeFormat .

Eu já usei esse método em um de meus projetos há alguns anos, mas agora decidi publicá-lo como um pequeno projeto de sistema operacional :)

Mariusz Nowak
fonte
0

Para usuários Ionic, eu tive um problema com isso, porque .toISOString()tem que ser usado com o modelo html.

Isso agarra a data atual, mas é claro que pode ser adicionado às respostas anteriores para uma data selecionada.

Eu o consertei usando isso:

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

O * 60000 indica o UTC -6, que é CST, portanto, seja qual for o fuso horário, o número e a diferença podem ser alterados.

Stephen Romero
fonte
Esta não é a resposta que o questionador está procurando. Ele estava procurando obter a data para o fuso horário específico.
Richard Vergis 29/04
@RichardVergis A pergunta afirma claramente que o usuário deseja alterar para a hora local, mas não indica em qual fuso horário está. Indiquei meu fuso horário como exemplo e o usuário pode ler claramente com base no meu exemplo, pode inserir deslocamento de fuso horário.
Stephen Romero
0

Talvez isso ajude você

/**
 * 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
fonte
-2

Estava enfrentando o mesmo problema, usou este

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

Ele retornará milissegundos aos quais você pode verificar, ter +100 fuso horário inicializando o horário britânico Espero que ajude !!

sandeep kumar
fonte
3
(Esta postagem parece não fornecer uma resposta de qualidade à pergunta. Edite sua resposta ou publique-a como um comentário na pergunta).
sɐunıɔ ןɐ qɐp