Formato de fechas correcto por zona horaria en JavaScript sin librerías
El Intl.DateTimeFormat integrado de JavaScript maneja el formato de zonas horarias IANA sin moment.js ni date-fns. Aprende las opciones explícitas de timeZone, la visualización segura ante el horario de verano, formatToParts, los límites de la conversión de reloj y cuándo Temporal o una librería de zonas horarias sigue siendo útil.
Por qué Date no tiene propiedad de zona horaria
Un Date de JavaScript es siempre un recuento de milisegundos UTC. No hay ninguna zona horaria guardada dentro del objeto. Cuando llamas a .toString() o .toLocaleString() sin opciones, JavaScript usa la zona horaria local del entorno desde el sistema operativo. El mismo código produce salidas distintas en un servidor de Nueva York frente a un portátil en Tokio, aunque el timestamp subyacente sea idéntico.
Formatear con Intl.DateTimeFormat
Intl.DateTimeFormat es la API integrada para formatear según la configuración regional y la zona horaria. Admite identificadores de zona horaria IANA, gestiona las transiciones de horario de verano automáticamente y está disponible en navegadores modernos y Node.js. La clave es pasar la opción timeZone explícitamente en lugar de confiar en el valor por defecto del entorno.
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
- new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)
Extraer partes individuales con formatToParts
Usa formatToParts() para obtener los componentes individuales de la fecha como objetos {type, value} y construir cadenas de fecha personalizadas. Esto es mejor que dividir una cadena de fecha localizada porque la puntuación, el orden y los sistemas de escritura difieren según la configuración regional.
- const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
- parts.find(p => p.type === 'year').value → '2023'
- parts.find(p => p.type === 'month').value → '11'
- Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }
Convertir hora de reloj a UTC (la dirección difícil)
Ir de una hora de reloj en una zona horaria dada a UTC es más difícil con la API Date clásica. Formatear un instante en America/New_York es fácil; construir el instante representado por 2026-03-08 02:30 en America/New_York no lo es, porque esa hora local puede saltarse o repetirse durante las transiciones de horario de verano.
- Temporal alcanzó la Etapa 4 en 2026, pero el soporte nativo en los navegadores aún no es universal
- Para uso en producción en todos los navegadores hoy, el polyfill de Temporal o date-fns-tz toDate() siguen siendo opciones prácticas
- Evita la aritmética manual de offset UTC: las transiciones de horario de verano ocurren a horas locales distintas en años distintos
- El zonedToEpochMs() de este sitio usa corrección de offset de una iteración; consulta src/timeUtils.ts
Elegir el identificador de zona horaria correcto
Usa nombres de zona horaria IANA como America/New_York, Europe/London, Asia/Tokyo o UTC. Evita los offsets fijos como UTC-5 para la hora de cara al usuario porque los offsets cambian con el horario de verano. America/New_York puede ser UTC-5 en enero y UTC-4 en julio; el nombre IANA permite a la plataforma aplicar la regla histórica correcta para la fecha seleccionada.
- Bien: America/Los_Angeles — incluye reglas de horario de verano históricas y futuras
- Bien: Europe/Berlin — gestiona la hora de verano de Europa Central automáticamente
- Bien: Asia/Shanghai — visualización estable UTC+8 sin horario de verano
- Usa UTC para logs, almacenamiento de API, timestamps de base de datos y comparación entre regiones
- Usa la zona horaria IANA del usuario para la visualización final en la interfaz del producto
Preguntas frecuentes sobre formato de zona horaria
- ¿Puede JavaScript formatear una fecha en otra zona horaria sin una librería?
- Sí. Usa Intl.DateTimeFormat o toLocaleString con una opción timeZone, como America/New_York o Asia/Tokyo.
- ¿Intl.DateTimeFormat gestiona el horario de verano?
- Sí. Cuando usas un identificador de zona horaria IANA, el entorno aplica el offset correcto para esa zona horaria y fecha.
- ¿Debo guardar offsets de zona horaria o nombres de zona horaria?
- Guarda UTC para los instantes de los eventos. Si necesitas el contexto local del usuario, guarda un nombre de zona horaria IANA como America/New_York, no solo un offset numérico.
- ¿Cómo obtengo la zona horaria del usuario en JavaScript?
- Usa Intl.DateTimeFormat().resolvedOptions().timeZone, que devuelve un nombre IANA como America/New_York. Guarda ese nombre, no un offset numérico, cuando necesites reconstruir más tarde la hora local del usuario.