Cómo formatear fechas en diferentes zonas horarias
Muestra fechas y horas para cualquier zona horaria usando la API Intl.DateTimeFormat de JavaScript
Introducción
El mismo momento en el tiempo se muestra como diferentes horas del reloj alrededor del mundo. Cuando una reunión comienza a las 3:00 PM en Nueva York, los relojes en Londres muestran las 8:00 PM y los relojes en Tokio muestran las 5:00 AM del día siguiente. Si tu aplicación muestra horas sin tener en cuenta las zonas horarias, los usuarios ven información incorrecta.
Un usuario en California que reserva un vuelo que sale a las 2:00 PM espera ver las 2:00 PM mostradas. Si tu aplicación formatea todas las horas usando la zona horaria del servidor en Virginia, el usuario ve las 5:00 PM y llega al aeropuerto tres horas tarde. Esta confusión se multiplica en cada hora mostrada en tu aplicación.
La API Intl.DateTimeFormat de JavaScript proporciona la opción timeZone para formatear fechas y horas para cualquier zona horaria. Esta lección explica por qué existen las zonas horarias, cómo JavaScript las maneja internamente y cómo formatear fechas correctamente para usuarios en cualquier parte del mundo.
Por qué existen las zonas horarias
La Tierra rota, creando día y noche en diferentes ubicaciones en diferentes momentos. Cuando el sol está en lo alto en Nueva York, ya se ha puesto en Londres y aún no ha salido en Tokio. Cada ubicación experimenta el mediodía en un momento diferente.
Antes de las zonas horarias estandarizadas, cada ciudad mantenía su propia hora local basada en la posición del sol. Esto creaba problemas para los ferrocarriles y telégrafos que conectaban ciudades distantes. En 1884, los países acordaron dividir el mundo en zonas horarias, cada una de aproximadamente 15 grados de longitud de ancho, correspondiendo a una hora de rotación de la Tierra.
Cada zona horaria tiene un desplazamiento estándar respecto al Tiempo Universal Coordinado, abreviado UTC. Nueva York utiliza UTC-5 o UTC-4 dependiendo del horario de verano. Londres utiliza UTC+0 o UTC+1. Tokio utiliza UTC+9 durante todo el año.
Cuando muestras horarios a los usuarios, necesitas convertir desde el momento universal a la hora local del reloj que esperan ver.
Cómo almacena JavaScript los horarios internamente
Los objetos Date de JavaScript representan un único momento en el tiempo como el número de milisegundos desde el 1 de enero de 1970 a medianoche UTC. Esta representación interna no tiene zona horaria.
const date = new Date('2025-03-15T20:00:00Z');
console.log(date.getTime());
// Output: 1742331600000
La Z al final de la cadena ISO indica hora UTC. El objeto Date almacena la marca de tiempo 1742331600000, que representa el mismo momento para todos en el mundo, independientemente de su zona horaria.
Cuando llamas a métodos como toString() en un objeto Date, JavaScript convierte la marca de tiempo UTC a tu zona horaria local para mostrarla. Esta conversión automática puede crear confusión cuando quieres mostrar horarios para una zona horaria diferente.
La API Intl.DateTimeFormat con la opción timeZone te da control explícito sobre qué zona horaria usar para el formateo.
Uso de la opción timeZone
La opción timeZone especifica qué zona horaria usar al formatear una fecha. Pasa la opción como parte del objeto de opciones al crear un formateador.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'short',
timeStyle: 'short'
});
console.log(formatter.format(date));
// Output: "3/15/25, 3:00 PM"
La fecha representa las 8:00 PM UTC. Nueva York está en UTC-5 durante el horario estándar o UTC-4 durante el horario de verano. En marzo, el horario de verano está activo, por lo que Nueva York está en UTC-4. El formateador convierte las 8:00 PM UTC a las 4:00 PM hora local, pero el ejemplo muestra las 3:00 PM, lo que sugiere que esto ocurre durante el horario estándar en este escenario específico.
El formateador maneja la conversión automáticamente. Proporcionas el momento UTC y la zona horaria de destino, y la API produce la hora local correcta.
Entender los nombres de zona horaria IANA
La opción timeZone acepta identificadores de zona horaria de la base de datos de zonas horarias IANA. Estos identificadores usan el formato Region/City, como America/New_York, Europe/London o Asia/Tokyo.
const date = new Date('2025-03-15T20:00:00Z');
const zones = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney'
];
zones.forEach(zone => {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
dateStyle: 'short',
timeStyle: 'long'
});
console.log(`${zone}: ${formatter.format(date)}`);
});
// Output:
// America/New_York: 3/15/25, 4:00:00 PM EDT
// Europe/London: 3/15/25, 8:00:00 PM GMT
// Asia/Tokyo: 3/16/25, 5:00:00 AM JST
// Australia/Sydney: 3/16/25, 7:00:00 AM AEDT
Cada zona horaria muestra una hora local diferente para el mismo momento. Nueva York muestra las 4:00 p. m. del 15 de marzo. Londres muestra las 8:00 p. m. del 15 de marzo. Tokio y Sídney ya han avanzado al 16 de marzo, mostrando las 5:00 a. m. y las 7:00 a. m. respectivamente.
Los nombres IANA manejan el horario de verano automáticamente. El formateador sabe que America/New_York cambia entre hora estándar del este y hora de verano del este, y aplica el desplazamiento correcto para cualquier fecha.
Encontrar nombres de zona horaria IANA válidos
La base de datos IANA contiene varios cientos de identificadores de zona horaria. Los patrones comunes incluyen:
America/New_Yorkpara Nueva YorkAmerica/Los_Angelespara Los ÁngelesAmerica/Chicagopara ChicagoEurope/Londonpara LondresEurope/Parispara ParísEurope/Berlinpara BerlínAsia/Tokyopara TokioAsia/Shanghaipara ShangháiAsia/Kolkatapara IndiaAustralia/Sydneypara SídneyPacific/Aucklandpara Auckland
Los identificadores usan nombres de ciudades en lugar de abreviaturas de zona horaria como EST o PST porque las abreviaturas son ambiguas. EST significa hora estándar del este en Norteamérica, pero también significa hora estándar del este de Australia. Los nombres basados en ciudades permanecen inequívocos.
Puedes buscar la lista completa de identificadores en la documentación de la base de datos de zonas horarias IANA.
Usar UTC como zona horaria
El identificador especial UTC formatea fechas en tiempo universal coordinado, que no tiene desplazamiento respecto al meridiano de Greenwich.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'UTC',
dateStyle: 'short',
timeStyle: 'long'
});
console.log(formatter.format(date));
// Output: "3/15/25, 8:00:00 PM UTC"
La hora UTC coincide con la marca de tiempo interna almacenada en el objeto Date. Usar UTC para formatear es útil al mostrar horas que no deben cambiar según la ubicación del usuario, como registros de servidor o marcas de tiempo de bases de datos.
Obtener la zona horaria del usuario
El método resolvedOptions() devuelve las opciones reales utilizadas por un formateador, incluida la zona horaria. Cuando creas un formateador sin especificar timeZone, utiliza por defecto la zona horaria del sistema del usuario.
const formatter = new Intl.DateTimeFormat();
const options = formatter.resolvedOptions();
console.log(options.timeZone);
// Output: "America/New_York" (or user's actual time zone)
Esto te proporciona el identificador IANA de la zona horaria actual del usuario. Puedes usar este identificador para formatear otras fechas en la misma zona o para almacenar la preferencia de zona horaria del usuario.
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: userTimeZone,
dateStyle: 'full',
timeStyle: 'long'
});
const date = new Date('2025-03-15T20:00:00Z');
console.log(formatter.format(date));
// Output varies based on user's time zone
Este patrón garantiza que las fechas se muestren automáticamente en la hora local del usuario.
Formatear el mismo momento para múltiples zonas horarias
Puedes formatear el mismo objeto Date para múltiples zonas horarias para mostrar a los usuarios a qué hora ocurre un evento en diferentes ubicaciones.
const meetingTime = new Date('2025-03-15T20:00:00Z');
const zones = [
{ name: 'New York', zone: 'America/New_York' },
{ name: 'London', zone: 'Europe/London' },
{ name: 'Tokyo', zone: 'Asia/Tokyo' }
];
zones.forEach(({ name, zone }) => {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
dateStyle: 'long',
timeStyle: 'short'
});
console.log(`${name}: ${formatter.format(meetingTime)}`);
});
// Output:
// New York: March 15, 2025 at 4:00 PM
// London: March 15, 2025 at 8:00 PM
// Tokyo: March 16, 2025 at 5:00 AM
Esto ayuda a los usuarios a comprender cuándo ocurre una reunión o evento en su zona horaria y en las zonas horarias de otros participantes.
Formatear fechas sin horas entre zonas horarias
Al formatear fechas sin horas, la zona horaria puede afectar qué fecha del calendario aparece. Una fecha a medianoche UTC cae en diferentes fechas del calendario en diferentes zonas horarias.
const date = new Date('2025-03-16T01:00:00Z');
const formatter1 = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
dateStyle: 'long'
});
const formatter2 = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
dateStyle: 'long'
});
console.log(`Los Angeles: ${formatter1.format(date)}`);
console.log(`Tokyo: ${formatter2.format(date)}`);
// Output:
// Los Angeles: March 15, 2025
// Tokyo: March 16, 2025
El momento de la 1:00 a. m. UTC del 16 de marzo corresponde a las 5:00 p. m. del 15 de marzo en Los Ángeles y a las 10:00 a. m. del 16 de marzo en Tokio. La fecha del calendario difiere en un día entre las dos zonas horarias.
Esto es importante al mostrar fechas para eventos programados, plazos de entrega o cualquier fecha que los usuarios interpreten en relación con su calendario local.
Uso de desplazamientos de zona horaria
En lugar de identificadores IANA, puedes usar cadenas de desplazamiento como +01:00 o -05:00. Estas representan desplazamientos fijos desde UTC.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: '+09:00',
dateStyle: 'short',
timeStyle: 'long'
});
console.log(formatter.format(date));
// Output: "3/16/25, 5:00:00 AM GMT+9"
Las cadenas de desplazamiento funcionan cuando conoces el desplazamiento exacto pero no la ubicación específica. Sin embargo, no gestionan el horario de verano. Si usas -05:00 para representar Nueva York, las horas serán incorrectas durante el horario de verano cuando Nueva York realmente usa -04:00.
Se prefieren los identificadores IANA porque gestionan el horario de verano automáticamente.
Comprensión del funcionamiento del horario de verano
Muchas regiones cambian su desplazamiento horario dos veces al año para el horario de verano. Durante la primavera, los relojes se adelantan una hora. Durante el otoño, los relojes se atrasan una hora. Esto significa que el desplazamiento UTC para una ubicación cambia a lo largo del año.
Cuando usas identificadores de zona horaria IANA, la API Intl.DateTimeFormat aplica automáticamente el desplazamiento correcto para cualquier fecha.
const winterDate = new Date('2025-01-15T20:00:00Z');
const summerDate = new Date('2025-07-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'long',
timeStyle: 'long'
});
console.log(`Winter: ${formatter.format(winterDate)}`);
console.log(`Summer: ${formatter.format(summerDate)}`);
// Output:
// Winter: January 15, 2025 at 3:00:00 PM EST
// Summer: July 15, 2025 at 4:00:00 PM EDT
En enero, Nueva York usa la hora estándar del este con desplazamiento UTC-5, mostrando las 3:00 p. m. En julio, Nueva York usa la hora de verano del este con desplazamiento UTC-4, mostrando las 4:00 p. m. La misma hora UTC produce diferentes horas locales según si el horario de verano está activo.
No necesitas rastrear qué fechas usan qué desplazamiento. La API gestiona esto automáticamente.
Formato de horas para programación de eventos
Al mostrar horas de eventos, formatea la hora en la ubicación del evento y opcionalmente en la ubicación del usuario.
const eventTime = new Date('2025-03-15T18:00:00Z');
const eventTimeZone = 'Europe/Paris';
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const eventFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: eventTimeZone,
dateStyle: 'full',
timeStyle: 'short'
});
const userFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: userTimeZone,
dateStyle: 'full',
timeStyle: 'short'
});
console.log(`Event time: ${eventFormatter.format(eventTime)} (Paris)`);
console.log(`Your time: ${userFormatter.format(eventTime)}`);
// Output (for a user in New York):
// Event time: Saturday, March 15, 2025 at 7:00 PM (Paris)
// Your time: Saturday, March 15, 2025 at 2:00 PM
Este patrón muestra a los usuarios tanto cuándo ocurre el evento en su propia zona horaria como cuándo deben unirse según su ubicación.
Formatear marcas de tiempo del servidor en la zona horaria del usuario
Los registros del servidor y las bases de datos suelen usar marcas de tiempo UTC. Al mostrarlas a los usuarios, convierte estas marcas a la zona horaria local del usuario.
const serverTimestamp = new Date('2025-03-15T20:00:00Z');
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const formatter = new Intl.DateTimeFormat(navigator.language, {
timeZone: userTimeZone,
dateStyle: 'short',
timeStyle: 'medium'
});
console.log(`Activity: ${formatter.format(serverTimestamp)}`);
// Output varies based on user's time zone and locale
// For en-US in New York: "Activity: 3/15/25, 4:00:00 PM"
Esto garantiza que los usuarios vean las marcas de tiempo en su hora local familiar en lugar de UTC o la hora del servidor.
Combinar timeZone con otras opciones
La opción timeZone funciona con todas las demás opciones de Intl.DateTimeFormat. Puedes especificar componentes individuales de fecha y hora, usar preajustes de estilo o controlar el sistema de calendario.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZoneName: 'long'
});
console.log(formatter.format(date));
// Output: "Monday, March 16, 2025 at 5:00:00 AM Japan Standard Time"
La opción timeZoneName controla cómo aparece el nombre de la zona horaria en la salida. Las lecciones posteriores cubrirán esta opción en detalle.
Qué evitar
No uses abreviaturas de zona horaria como EST, PST o GMT como valores para la opción timeZone. Estas abreviaturas son ambiguas y no tienen soporte consistente.
// Incorrect - abbreviations may not work
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'EST', // This may throw an error
dateStyle: 'short',
timeStyle: 'short'
});
Usa siempre identificadores IANA como America/New_York o cadenas de desplazamiento como -05:00.
No asumas que la zona horaria del usuario coincide con la zona horaria del servidor. Formatea siempre las horas explícitamente en la zona correcta o usa la zona horaria detectada del usuario.
Reutilizar formateadores entre zonas horarias
Al formatear fechas para múltiples zonas horarias, podrías crear muchos formateadores. Si formateas muchas fechas con la misma configuración, reutiliza las instancias de formateador para un mejor rendimiento.
const dates = [
new Date('2025-03-15T20:00:00Z'),
new Date('2025-03-16T14:00:00Z'),
new Date('2025-03-17T09:00:00Z')
];
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Europe/Berlin',
dateStyle: 'short',
timeStyle: 'short'
});
dates.forEach(date => {
console.log(formatter.format(date));
});
// Output:
// "3/15/25, 9:00 PM"
// "3/16/25, 3:00 PM"
// "3/17/25, 10:00 AM"
Crear un formateador implica procesar opciones y cargar datos de configuración regional. Reutilizar el mismo formateador para múltiples fechas evita esta sobrecarga.