Cómo gestionar zonas horarias en React Router v7
Mostrar horarios en la zona horaria local del usuario
Problema
Cuando las aplicaciones muestran horarios sin considerar la ubicación del usuario, surgen confusión y errores. Un servidor puede almacenar la hora de un evento como "2024-03-15T20:00:00Z" y renderizar "8:00 PM" directamente desde ese valor UTC. Los usuarios en diferentes zonas horarias ven la misma hora del reloj pero la interpretan como local, lo que genera citas perdidas y conflictos de programación. El problema fundamental es que un único momento en el tiempo tiene diferentes representaciones según la ubicación geográfica, y mostrar horarios en la zona incorrecta los hace inútiles o engañosos.
Este problema se agrava cuando los usuarios colaboran entre regiones. Un evento programado para las 3:00 PM en el servidor se convierte en 3:00 PM en todas partes en la interfaz, aunque debería aparecer como 6:00 AM en Tokio, 2:00 PM en Londres y 9:00 AM en Nueva York para el mismo momento real.
Solución
Almacena todas las marcas de tiempo en un formato universal como cadenas ISO 8601 con indicadores UTC o marcas de tiempo Unix. Al mostrar estos horarios a los usuarios, conviértelos en objetos Date de JavaScript y formatéalos usando APIs de internacionalización que respeten la zona horaria del navegador. Los navegadores modernos detectan automáticamente la zona horaria local del usuario, y las bibliotecas de formateo aprovechan esto para convertir marcas de tiempo UTC en la representación local correcta sin cálculos manuales de desplazamiento.
Este enfoque separa el almacenamiento de la presentación. El servidor envía una marca de tiempo canónica, y cada cliente la renderiza según su propia zona horaria, asegurando que todos vean la hora local correcta para el mismo momento global.
Pasos
1. Envía marcas de tiempo en formato ISO 8601 desde tu fuente de datos
Asegúrate de que tu API o cargador de datos devuelva marcas de tiempo como cadenas ISO 8601 con indicadores UTC o como marcas de tiempo Unix en milisegundos.
export async function loader() {
const event = await fetchEvent();
return {
title: event.title,
startTime: "2024-03-15T20:00:00Z",
endTime: "2024-03-15T22:00:00Z",
};
}
El sufijo Z indica hora UTC. Cuando esta cadena se analiza en un objeto Date de JavaScript, el navegador la almacenará internamente como una marca de tiempo UTC.
2. Crea un componente que formatee horas usando react-intl
Importa componentes de formateo desde react-intl y pasa objetos Date creados a partir de tus cadenas ISO.
import { FormattedDate, FormattedTime } from "react-intl";
import type { Route } from "./+types/event";
export default function EventDetails({ loaderData }: Route.ComponentProps) {
const startTime = new Date(loaderData.startTime);
const endTime = new Date(loaderData.endTime);
return (
<div>
<h1>{loaderData.title}</h1>
<p>
<FormattedDate
value={startTime}
year="numeric"
month="long"
day="numeric"
/>
{" at "}
<FormattedTime value={startTime} />
{" to "}
<FormattedTime value={endTime} />
</p>
</div>
);
}
Los componentes FormattedDate y FormattedTime utilizan automáticamente la zona horaria detectada del navegador para mostrar la hora local correcta. Un usuario en Nueva York ve "15 de marzo de 2024 a las 3:00 p. m." mientras que un usuario en Tokio ve "16 de marzo de 2024 a las 5:00 a. m." para la misma marca de tiempo UTC.
3. Usa la API imperativa para necesidades de formateo dinámico
Cuando necesites horas formateadas en atributos, valores calculados o contextos que no sean JSX, usa el hook useIntl.
import { useIntl } from "react-intl";
import type { Route } from "./+types/event";
export default function EventCard({ loaderData }: Route.ComponentProps) {
const intl = useIntl();
const startTime = new Date(loaderData.startTime);
const formattedDate = intl.formatDate(startTime, {
year: "numeric",
month: "short",
day: "numeric",
});
const formattedTime = intl.formatTime(startTime, {
hour: "numeric",
minute: "2-digit",
});
return (
<div>
<h2>{loaderData.title}</h2>
<time dateTime={loaderData.startTime}>
{formattedDate} at {formattedTime}
</time>
</div>
);
}
Los métodos formatDate y formatTime devuelven cadenas formateadas según la configuración regional y la zona horaria del usuario. El atributo dateTime conserva la cadena ISO original para legibilidad automática mientras que el texto mostrado presenta la hora local en formato amigable para el usuario.