Cómo inicializar una fecha de JavaScript en una zona horaria particular

232

Tengo una fecha y hora en una zona horaria particular como una cadena y quiero convertir esto a la hora local. Pero no sé cómo configurar la zona horaria en el objeto Fecha.

Por ejemplo, tengo Feb 28 2013 7:00 PM ET,entonces puedo

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

Hasta donde sé, puedo configurar la hora UTC o la hora local. Pero, ¿cómo configuro la hora en otra zona horaria?

Traté de usar la suma / resta del desplazamiento de UTC pero no sé cómo contrarrestar el horario de verano. No estoy seguro si me dirijo en la dirección correcta.

¿Cómo puedo convertir la hora de una zona horaria diferente a la hora local en javascript?

pavimentado
fuente
1
Los objetos de fecha no tienen una zona horaria, son UTC.
RobG

Respuestas:

352

Antecedentes

El Dateobjeto de JavaScript rastrea el tiempo en UTC internamente, pero generalmente acepta entradas y produce salidas en la hora local de la computadora en la que se está ejecutando. Tiene muy pocas instalaciones para trabajar con el tiempo en otras zonas horarias.

La representación interna de un Dateobjeto es un número único, que representa el número de milisegundos que han transcurrido desde entonces 1970-01-01 00:00:00 UTC, independientemente de los segundos bisiestos. No hay zona horaria o formato de cadena almacenado en el objeto Fecha en sí. Cuando Datese utilizan varias funciones del objeto, la zona horaria local de la computadora se aplica a la representación interna. Si la función produce una cadena, entonces la información de la ubicación de la computadora puede tomarse en consideración para determinar cómo producir esa cadena. Los detalles varían según la función, y algunos son específicos de la implementación.

Las únicas operaciones que el Dateobjeto puede hacer con zonas horarias no locales son:

  • Puede analizar una cadena que contiene un desplazamiento numérico UTC de cualquier zona horaria. Utiliza esto para ajustar el valor que se analiza y almacena el equivalente UTC. La hora local original y el desplazamiento no se conservan en el Dateobjeto resultante . Por ejemplo:

    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)
  • En entornos que han implementado la API de internacionalización ECMASCript (también conocida como "Intl"), un Dateobjeto puede producir una cadena específica de la localidad ajustada a un identificador de zona horaria dado. Esto se logra a través de la timeZoneopción toLocaleStringay sus variaciones. La mayoría de las implementaciones admitirán identificadores de zona horaria de IANA, como 'America/New_York'. Por ejemplo:

    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 mayoría de los entornos modernos admiten el conjunto completo de identificadores de zona horaria de IANA ( consulte la tabla de compatibilidad aquí ). Sin embargo, tenga en cuenta que el único identificador requiere ser apoyada por Intl es 'UTC', por lo tanto usted debe comprobar cuidadosamente si es necesario apoyar los navegadores antiguos o entornos atípicos (por ejemplo, dispositivos IO ligeros).

Bibliotecas

Hay varias bibliotecas que se pueden usar para trabajar con zonas horarias. Aunque todavía no pueden hacer que el Dateobjeto se comporte de manera diferente, generalmente implementan la base de datos estándar de zonas horarias de la IANA y proporcionan funciones para usarlo en JavaScript. Las bibliotecas modernas usan los datos de zona horaria proporcionados por la API Intl, pero las bibliotecas más antiguas suelen tener gastos generales, especialmente si se ejecuta en un navegador web, ya que la base de datos puede ser un poco grande. Algunas de estas bibliotecas también le permiten reducir selectivamente el conjunto de datos, ya sea por qué zonas horarias son compatibles y / o por el rango de fechas con las que puede trabajar.

Aquí están las bibliotecas a considerar:

Bibliotecas basadas en Intl

El nuevo desarrollo debe elegir una de estas implementaciones, que dependen de la API Intl para sus datos de zona horaria:

Bibliotecas no internacionales

Estas bibliotecas se mantienen, pero conllevan la carga de empaquetar sus propios datos de zona horaria, que pueden ser bastante grandes.

* Si bien Moment y Moment-Timezone se recomendaron anteriormente, el equipo de Moment ahora prefiere que los usuarios elijan Luxon para un nuevo desarrollo.

Bibliotecas descontinuadas

Estas bibliotecas se han descontinuado oficialmente y ya no se deben usar.

Propuestas Futuras

La propuesta temporal TC39 tiene como objetivo proporcionar un nuevo conjunto de objetos estándar para trabajar con fechas y horas en el lenguaje JavaScript. Esto incluirá soporte para un objeto con reconocimiento de zona horaria.

Matt Johnson-Pint
fuente
1
Cambie "represent" a "output / parse", ya que las marcas de tiempo representadas son independientes de la zona horaria
Bergi
1
@ Bergi: he repensado esto y estoy de acuerdo contigo. Actualicé mi respuesta en consecuencia.
Matt Johnson-Pint
1
Cuando haces esto en la consola Firebug:, var date_time = new Date()el valor de date_timees (para mí en AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}y, por lo tanto, parece que ha almacenado una zona horaria. ¿Cómo puedo asegurar que date_timees puramente la hora UTC, sin incluir ninguna zona horaria?
user1063287
Hmm, de acuerdo con esta respuesta ( stackoverflow.com/a/8047885/1063287 ), es esto: new Date().getTime();
user1063287
1
@ user1063287: el método toString utiliza el desplazamiento de la zona horaria del host para generar una fecha y hora en la zona horaria "local". El objeto de fecha en sí tiene un valor de tiempo que es un desplazamiento desde 1970-01-01T00: 00: 00Z, por lo que efectivamente UTC. Para ver la fecha y hora UTC, use toISOString .
RobG
69

Como dijo Matt Johnson

Si puede limitar su uso a los navegadores web modernos, ahora puede hacer lo siguiente sin ninguna biblioteca especial:

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

Esta no es una solución integral, pero funciona para muchos escenarios que requieren solo la conversión de salida (de UTC u hora local a una zona horaria específica, pero no en la otra dirección).

Entonces, aunque el navegador no puede leer las zonas horarias de la IANA al crear una fecha, o tiene algún método para cambiar las zonas horarias en un objeto Date existente, parece que hay un truco a su alrededor:

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

Pico común
fuente
66
Me pregunto por qué esta respuesta no está recibiendo más amor. Para mis propósitos, es absolutamente la mejor solución. En mi aplicación, todas las fechas son UTC, pero deben ingresarse o enviarse en zonas horarias explícitas de IANA en la interfaz de usuario. Para este propósito, uso esta solución y funciona perfectamente. Simple, efectivo, estándar.
logidelic
2
@logidelic es una publicación bastante nueva. para ser honesto, me sorprendí cuando me di cuenta de que podrías usar toLocaleStringpara lograr el efecto inverso, y sí, ¡esto debería ser de conocimiento común! ir a escribir un artículo sobre ello y me mencionar :-)
commonpike
1
Para un entorno de nodo que aún no está en GMT, el código anterior debe cambiar de var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); en var invdate = nueva Fecha ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.
55
Los navegadores no están obligados a analizar la salida de toLocaleString , por lo que se basa en una premisa defectuosa.
RobG
3
"... ¿por qué esta respuesta no está recibiendo más amor?" - porque el Dateobjeto que devuelve es una mentira. Incluso en el ejemplo que se muestra, la salida de cadena incluye la zona horaria de la máquina local. En mi computadora, ambos resultados dicen "GMT-0700 (hora de verano del Pacífico)", donde eso solo es correcto para el primero (Toronto está realmente en hora del este). Más allá de la salida de la cadena, la marca de tiempo contenida dentro del Dateobjeto ha sido cambiado a un punto diferente en el tiempo. Esta puede ser una técnica útil, pero viene con muchas advertencias, principalmente que las Datefunciones del objeto no tendrán una salida normal.
Matt Johnson-Pint
30

Puede especificar un desplazamiento de zona horaria en new Date(), por ejemplo:

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

o

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

Dado que Datealmacena la hora UTC (es decir, getTimeregresa en UTC), javascript convertirá la hora en UTC, y cuando llame a cosas como toStringjavascript convertirá la hora UTC en la zona horaria local del navegador y devolverá la cadena en la zona horaria local, es decir, si estoy usando UTC+8:

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

También puedes usar el getHours/Minute/Secondmétodo normal :

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

(Esto 8significa que después de que la hora se convierta en mi hora local UTC+8, el número de horas es 8).

maowtm
fuente
16
Analizar cualquier formato que no sea el formato extendido ISO 8601 depende de la implementación y no se debe confiar en él. No hay un estándar para las abreviaturas de zona horaria, por ejemplo, "EST" podría representar cualquiera de las 3 zonas diferentes.
RobG
1
Los ejemplos en este comentario a menudo no funcionan en Internet Explorer. Como se mencionó en el comentario anterior a esta publicación, ISO 8601 es importante. Confirmé leyendo la especificación del lenguaje de la edición ECMA-262 (Javascript 5ta. Edición).
Dakusan
12

Esto debería resolver su problema, no dude en ofrecer soluciones. Este método también tendrá en cuenta el horario de verano para la fecha indicada.

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

Cómo usar con zona horaria y hora local:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
pollos
fuente
Esta solución fue adecuada para mi necesidad. No estaba seguro de cómo puedo explicar exactamente el horario de verano.
Hossein Amin
¿Por qué no simplemente regresar tzDatedirectamente?
Levi
8

Encontré que la forma más compatible de hacer esto, sin preocuparme por una biblioteca de terceros, era usar getTimezoneOffsetpara calcular la marca de tiempo adecuada o actualizar la hora y luego usar los métodos normales para obtener la fecha y hora necesarias.

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

Anteriormente estaba usando UTCmétodos al realizar las transformaciones de fecha, que era incorrecta. Al agregar el desplazamiento al tiempo, el uso de las getfunciones locales devolverá los resultados deseados.

Shaun Cockerill
fuente
¿Dónde calculaste timezone_offset?
Anand Somani
1
Vaya, etiqueté mal una de mis variables. timezone_offsetdebería haber sido offset, o viceversa. He editado mi respuesta para mostrar el nombre correcto de la variable.
Shaun Cockerill
3

Me encontré con un problema similar con las pruebas unitarias (específicamente en broma cuando las pruebas unitarias se ejecutan localmente para crear las instantáneas y luego el servidor CI se ejecuta (potencialmente) en una zona horaria diferente, lo que hace que falle la comparación de instantáneas). Me burlé de nuestros Datey algunos de los métodos de apoyo de esta manera:

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

Intente usar ctoc de npm. https://www.npmjs.com/package/ctoc_timezone

Tiene una funcionalidad simple para cambiar las zonas horarias (la mayoría de las zonas horarias alrededor de 400) y todos los formatos personalizados que desea que se muestren.

Chowdary Sandeep
fuente
2

Sobre la base de las respuestas anteriores, estoy usando este forro nativo para convertir la cadena larga de zona horaria en la cadena de tres letras:

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

Esto le dará PDT o PST dependiendo de la fecha provista. En mi caso de uso particular, desarrollando en Salesforce (Aura / Lightning), podemos obtener la zona horaria del usuario en formato largo desde el back-end.

Shane
fuente
Solo porque me gusta jugar al pedante de vez en cuando, 'America / Los_Angeles' no es una zona horaria, es una "ubicación representativa" para una compensación particular y reglas de horario de verano y el historial de esos cambios (según la base de datos de zonas horarias de la IANA ) . ;-)
RobG
Jaja, tienes que publicar ese comentario en múltiples respuestas aquí: P
Shane
2

Sé que son 3 años demasiado tarde, pero tal vez pueda ayudar a alguien más porque no he encontrado algo así, excepto la biblioteca de zona horaria, que no es exactamente lo mismo que está pidiendo aquí.

He hecho algo similar para la zona horaria alemana, esto es un poco complejo debido al horario de verano y a los años bisiestos donde tienes 366 días.

Es posible que necesite un poco de trabajo con la función "isDaylightSavingTimeInGermany", mientras que las diferentes zonas horarias cambian en diferentes momentos del horario de verano.

de todos modos, echa un vistazo a esta página: https://github.com/zerkotin/german-timezone-converter/wiki

Los métodos principales son: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Me he esforzado por documentarlo, por lo que no será tan confuso.

zerkotin
fuente
Esto debería ser un comentario, no es una respuesta.
RobG
1

Pruebe: date-from-timezone , resuelve la fecha esperada con la ayuda de disponible de forma nativa Intl.DateTimeFormat.

Utilicé ese método en uno de mis proyectos durante algunos años, pero ahora decidí publicarlo como un pequeño proyecto de SO :)

Mariusz Nowak
fuente
1

Quizás esto te ayude

/**
 * 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
fuente
0

Para los usuarios de Ionic, tuve un infierno con esto porque .toISOString()tiene que usarse con la plantilla html.

Esto tomará la fecha actual, pero por supuesto se puede agregar a las respuestas anteriores para una fecha seleccionada.

Lo solucioné usando esto:

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

El * 60000 indica el UTC -6, que es CST, por lo que se necesita cualquier TimeZone, se puede cambiar el número y la diferencia.

Stephen Romero
fuente
Esta no es la respuesta que busca el interlocutor. Estaba buscando obtener la fecha para la zona horaria específica.
Richard Vergis
@RichardVergis La pregunta establece claramente que el usuario desea cambiar a la hora local, pero no indica en qué zona horaria se encuentra. Indiqué mi zona horaria como ejemplo y el usuario puede leer claramente según mi ejemplo, pueden ingresar su zona horaria compensada.
Stephen Romero
-2

Estaba enfrentando el mismo problema, usé este

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

Devolverá milisegundos a los que puede verificar tener +100 timzone intialize British time ¡Espero que ayude!

Sandeep Kumar
fuente
3
(Esta publicación no parece proporcionar una respuesta de calidad a la pregunta. Edite su respuesta o simplemente publíquela como un comentario a la pregunta).
sɐunıɔ ןɐ qɐp