Cómo manejar zonas horarias en React Router v7
Mostrar horas en la zona horaria local del usuario
Problema
Cuando las aplicaciones muestran horas sin considerar la ubicación del usuario, surgen confusiones y errores. Un servidor puede almacenar la hora de un evento como "2024-03-15T20:00:00Z" y mostrar "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 para ellos, lo que lleva a citas perdidas y conflictos de programación. El problema fundamental es que un solo momento en el tiempo tiene diferentes representaciones dependiendo de la ubicación geográfica, y mostrar las horas en la zona incorrecta las hace sin sentido o engañosas.
Este problema se agrava cuando los usuarios colaboran entre regiones. Un evento programado para las 3:00 PM en el servidor se muestra como 3:00 PM en todas partes en la interfaz de usuario, 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 timestamps Unix. Al mostrar estas horas a los usuarios, conviértelas en objetos Date de JavaScript y formátealas 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 las marcas de tiempo UTC en la representación local correcta sin cálculos manuales de compensación.
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 tiempo 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 tiempos 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"
/>
{" a las "}
<FormattedTime value={startTime} />
{" hasta "}
<FormattedTime value={endTime} />
</p>
</div>
);
}
Los componentes FormattedDate y FormattedTime utilizan automáticamente la zona horaria detectada por el navegador para mostrar la hora local correcta. Un usuario en Nueva York ve "15 de marzo de 2024 a las 3:00 PM" mientras que un usuario en Tokio ve "16 de marzo de 2024 a las 5:00 AM" para la misma marca de tiempo UTC.
3. Usa la API imperativa para necesidades de formateo dinámicas
Cuando necesites tiempos formateados en atributos, valores calculados o contextos que no sean JSX, utiliza 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} a las {formattedTime}
</time>
</div>
);
}
Los métodos formatDate y formatTime devuelven cadenas formateadas según la configuración regional y zona horaria del usuario. El atributo dateTime conserva la cadena ISO original para legibilidad por máquina mientras que el texto mostrado presenta la hora local de manera amigable para el usuario.