Cómo construir un reloj mundial o convertidor de zonas horarias
Un reloj mundial muestra la hora actual en varias ciudades simultáneamente. Un convertidor de zonas horarias toma una fecha y hora concretas y muestra qué hora es en otras zonas. Ambos se construyen sobre la misma base: un timestamp UTC y la API Intl.DateTimeFormat.
El núcleo: un mismo momento, múltiples representaciones
const now = new Date(); // instante UTC
const cities = [
{ name: "New York", zone: "America/New_York" },
{ name: "London", zone: "Europe/London" },
{ name: "Singapore", zone: "Asia/Singapore" },
{ name: "Tokyo", zone: "Asia/Tokyo" },
];
cities.forEach(({ name, zone }) => {
const time = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
hour: "2-digit",
minute: "2-digit",
hour12: false,
}).format(now);
console.log(`${name}: ${time}`);
});
// New York: 15:00
// London: 20:00
// Singapore: 03:00 (día siguiente)
// Tokyo: 04:00 (día siguiente)
El horario de verano se gestiona automáticamente — Intl.DateTimeFormat usa las reglas IANA de cada fecha.
Añadir un reloj en tiempo real
Actualiza cada segundo con setInterval. Siempre actualiza desde new Date() en lugar de incrementar un valor almacenado — así evitas la deriva y el reloj se mantiene correcto durante los cambios de horario.
function updateClocks() {
const now = new Date();
cities.forEach(({ name, zone, elementId }) => {
document.getElementById(elementId).textContent = new Intl.DateTimeFormat(
"en-US",
{
timeZone: zone,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
},
).format(now);
});
}
setInterval(updateClocks, 1000);
updateClocks(); // ejecutar inmediatamente al cargar
Construir un convertidor de zonas horarias
Un convertidor toma una fecha y hora especificada por el usuario y una zona de origen, convierte a UTC y luego muestra el resultado en las zonas destino.
import { fromZonedTime, toZonedTime, format } from "date-fns-tz";
function convert(localDateTimeString, sourceZone, targetZones) {
// 1. Parsear la hora local en la zona de origen → UTC
const utcDate = fromZonedTime(localDateTimeString, sourceZone);
// 2. Mostrar UTC en cada zona destino
return targetZones.map((zone) => ({
zone,
display: format(toZonedTime(utcDate, zone), "yyyy-MM-dd HH:mm zzz", {
timeZone: zone,
}),
}));
}
convert("2026-06-20 15:00", "America/New_York", [
"Europe/London",
"Asia/Tokyo",
"Australia/Sydney",
]);
// [
// { zone: 'Europe/London', display: '2026-06-20 20:00 BST' },
// { zone: 'Asia/Tokyo', display: '2026-06-21 04:00 JST' },
// { zone: 'Australia/Sydney', display: '2026-06-21 05:00 AEST' },
// ]
Cómo obtener la lista de zonas IANA
La lista completa de zonas IANA está disponible en Intl.supportedValuesOf('timeZone') en navegadores modernos y Node:
const allZones = Intl.supportedValuesOf("timeZone");
// → ['Africa/Abidjan', 'Africa/Accra', ... 'Pacific/Wake']
Para una interfaz de reloj mundial, no querrás 600 zonas en un desplegable. Usa una lista curada de las ciudades principales, o agrupa por región y muestra una zona representativa por región.
Gestionar el “día siguiente” en la visualización
Cuando un timestamp UTC es el 20 de junio pero se muestra como el 21 de junio en Tokio, tu interfaz necesita hacerlo visible. Muestra la fecha junto a la hora para cualquier zona que cruce la medianoche:
new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Tokyo",
weekday: "short",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
}).format(now);
// → "Sun, Jun 21, 04:00"
Testing
Los casos con más probabilidad de revelar bugs:
- Una hora durante la transición del cambio de horario (1–2am en el día de transición de la zona destino)
- Una hora que cae en días de calendario distintos según la zona (por ejemplo, las 11pm UTC el 31 de diciembre)
- Zonas con offset fraccionario:
Asia/Kolkata(UTC+5:30),Asia/Kathmandu(UTC+5:45) - Zonas sin cambio de horario:
Asia/Tokyo,Asia/Singapore— verifica que permanecen fijas cuando otras zonas cambian