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 verán información incorrecta.
Un usuario en California que reserva un vuelo que sale a las 2:00 PM espera ver 2:00 PM en pantalla. Si tu aplicación formatea todas las horas usando la zona horaria del servidor en Virginia, el usuario verá 5:00 PM y llegará 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 el día y la 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 creó 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, correspondiente a una hora de rotación de la Tierra.
Cada zona horaria tiene un desplazamiento estándar desde el Tiempo Universal Coordinado, abreviado UTC. Nueva York usa UTC-5 o UTC-4 dependiendo del horario de verano. Londres usa UTC+0 o UTC+1. Tokio usa UTC+9 durante todo el año.
Cuando muestras horas a los usuarios, necesitas convertir desde el momento universal a la hora local del reloj que esperan ver.
Cómo JavaScript almacena internamente los tiempos
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 tiempo 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 tiempos 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.
Usando 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 es 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 es 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 es durante el horario estándar en este escenario específico.
El formateador maneja la conversión automáticamente. Tú proporcionas el momento UTC y la zona horaria objetivo, y la API produce la hora local correcta.
Comprendiendo los nombres de zonas horarias IANA
La opción timeZone acepta identificadores de zona horaria de la Base de Datos de Zonas Horarias IANA. Estos identificadores utilizan el formato Región/Ciudad, 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 PM del 15 de marzo. Londres muestra las 8:00 PM del 15 de marzo. Tokio y Sídney ya han avanzado al 16 de marzo, mostrando las 5:00 AM y 7:00 AM respectivamente.
Los nombres IANA manejan el horario de verano automáticamente. El formateador sabe que America/New_York cambia entre el Horario Estándar del Este y el Horario de Verano del Este y aplica el desplazamiento correcto para cualquier fecha.
Encontrando nombres válidos de zonas horarias IANA
La base de datos IANA contiene varios cientos de identificadores de zonas horarias. Los patrones comunes incluyen:
America/New_Yorkpara la ciudad de 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 utilizan nombres de ciudades en lugar de abreviaturas de zonas horarias como EST o PST porque las abreviaturas son ambiguas. EST significa Horario Estándar del Este en Norteamérica, pero también significa Horario Estándar del Este Australiano. 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.
Uso de UTC como zona horaria
El identificador especial UTC formatea las fechas en Tiempo Universal Coordinado, que no tiene desplazamiento desde el meridiano principal.
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"
El tiempo UTC coincide con la marca de tiempo interna almacenada en el objeto Date. Usar UTC para el formateo es útil cuando se muestran tiempos que no deberían cambiar según la ubicación del usuario, como registros de servidor o marcas de tiempo de bases de datos.
Obtención de la zona horaria del usuario
El método resolvedOptions() devuelve las opciones reales utilizadas por un formateador, incluyendo la zona horaria. Cuando creas un formateador sin especificar timeZone, por defecto utiliza la zona horaria del sistema del usuario.
const formatter = new Intl.DateTimeFormat();
const options = formatter.resolvedOptions();
console.log(options.timeZone);
// Output: "America/New_York" (o la zona horaria real del usuario)
Esto te proporciona el identificador IANA para 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));
// La salida varía según la zona horaria del usuario
Este patrón asegura que las fechas se muestren automáticamente en la hora local del usuario.
Formateo del 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 entender cuándo ocurre una reunión o evento en su zona horaria y en las zonas horarias de otros participantes.
Formateo de fechas sin horas a través de 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 1:00 AM UTC del 16 de marzo corresponde a las 5:00 PM del 15 de marzo en Los Ángeles y a las 10:00 AM del 16 de marzo en Tokio. La fecha del calendario difiere en un día entre las dos zonas horarias.
Esto es importante cuando se muestran fechas para eventos programados, plazos 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 manejan 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 manejan automáticamente el horario de verano.
Entendiendo cómo funciona el horario de verano
Muchas regiones cambian su compensación horaria 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 la compensación UTC para una ubicación cambia a lo largo del año.
Cuando utilizas identificadores de zona horaria IANA, la API Intl.DateTimeFormat aplica automáticamente la compensación correcta 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 utiliza el horario estándar del este con compensación UTC-5, mostrando las 3:00 PM. En julio, Nueva York utiliza el horario de verano del este con compensación UTC-4, mostrando las 4:00 PM. El mismo tiempo UTC produce diferentes horas locales según si el horario de verano está activo.
No necesitas rastrear qué fechas utilizan qué compensación. La API maneja esto automáticamente.
Formateo de horas para programación de eventos
Al mostrar las horas de los 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 deberían unirse según su ubicación.
Formateo de marcas de tiempo del servidor en la zona horaria del usuario
Los registros del servidor y los registros de base de datos a menudo utilizan marcas de tiempo UTC. Al mostrarlas a los usuarios, conviértelas 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)}`);
// La salida varía según la zona horaria y la configuración regional del usuario
// Para en-US en Nueva York: "Activity: 3/15/25, 4:00:00 PM"
Esto asegura que los usuarios vean las marcas de tiempo en hora local familiar en lugar de UTC o la hora del servidor.
Combinación de 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));
// Salida: "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.
// Incorrecto - las abreviaturas pueden no funcionar
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'EST', // Esto puede generar un error
dateStyle: 'short',
timeStyle: 'short'
});
Siempre usa 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. Siempre formatea las horas explícitamente en la zona correcta o usa la zona horaria detectada del usuario.
Reutilización de formateadores entre zonas horarias
Cuando formateas fechas para múltiples zonas horarias, podrías crear muchos formateadores. Si formateas muchas fechas con la misma configuración, reutiliza las instancias del 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 localización. Reutilizar el mismo formateador para múltiples fechas evita esta sobrecarga.