← Todos los artículos

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

Two daylight-saving transitions a year break the assumption that "wall-clock time + timezone = a unique moment". Both are encoded in the IANA timezone database (still often called the Olson database) — the authoritative source of timezone rules used by every modern OS, browser, and language runtime.

  • DST gap: 02:00 to 02:59 local time does not exist on the spring-forward day
  • DST overlap: 01:00 to 01:59 local time occurs twice on the fall-back day
  • Ambiguous local time: in the overlap window you must pick "earlier" or "later" explicitly
  • Python uses a fold flag; JS Temporal uses a disambiguation option; java.time defaults to the earlier offset
  • Olson database / IANA tzdata / zoneinfo are three names for the same dataset, updated several times a year
  • Each IANA name (America/Los_Angeles, Europe/Berlin) maps to a list of (transition, offset, abbreviation) tuples
  • Stale tzdata on a device produces silently-wrong local times after any DST or zone change
  • Server best practice: do all date arithmetic in UTC, convert to local only at display

FAQ

Can JavaScript format a date in another timezone without a library?
Yes. Use Intl.DateTimeFormat or toLocaleString with a timeZone option, such as America/New_York or Asia/Tokyo.
Does Intl.DateTimeFormat handle daylight saving time?
Yes. When you use an IANA timezone identifier, the runtime applies the correct offset for that timezone and date.
Should I store timezone offsets or timezone names?
Store UTC for event instants. If you need the user's local context, store an IANA timezone name such as America/New_York, not only a numeric offset.
How do I get the user's timezone in JavaScript?
Use Intl.DateTimeFormat().resolvedOptions().timeZone, which returns an IANA name like America/New_York. Store that name, not a numeric offset, when you need to reconstruct the user's local time later.
What is the DST gap (spring-forward)?
The DST gap is the wall-clock hour that is skipped when daylight saving time begins. In most US zones, when the clock would tick from 02:00 to 02:59 it instead jumps to 03:00. Local times in that window do not exist on the transition day. Unix timestamps themselves never skip; only the wall-clock interpretation does.
What is the DST overlap (fall-back)?
The DST overlap is the wall-clock hour that occurs twice when daylight saving time ends. In most US zones, the clock ticks from 01:59 back to 01:00, so every minute from 01:00 to 01:59 happens twice. A wall-clock time in that window maps to two Unix timestamps; converters and schedulers must specify "earlier" or "later".