Cómo gestionar zonas horarias en TanStack Start v1
Mostrar horas en la zona horaria local del usuario
Problema
Cuando las aplicaciones muestran horas, representan un momento único que aparece de manera diferente según la ubicación del usuario. Si un servidor renderiza "8:00 PM" en su propia zona horaria o UTC, los usuarios en diferentes zonas horarias verán una hora local incorrecta, lo que genera confusión sobre cuándo ocurren realmente los eventos. Los usuarios esperan que las horas reflejen automáticamente su zona horaria local, pero sin un manejo adecuado, ven las horas en la zona horaria que el servidor utilizó durante el renderizado. Esta discrepancia hace que los usuarios pierdan citas, malinterpreten horarios y pierdan confianza en la fiabilidad de la aplicación.
Solución
Pasa una opción timeZone a los métodos de formateo de fechas para controlar cómo se interpretan y muestran las fechas. La función formatDate acepta Intl.DateTimeFormatOptions que incluyen configuración de zona horaria. Almacena las marcas de tiempo en un formato universal como cadenas ISO 8601 o marcas de tiempo Unix, luego formatéalas en el cliente donde la zona horaria del navegador está disponible automáticamente. La API Intl.DateTimeFormat utiliza la zona horaria predeterminada del usuario cuando no se especifica ninguna opción timeZone, asegurando que las horas se muestren correctamente para la ubicación de cada usuario sin detección manual de zona horaria.
Pasos
1. Crear un componente de formateo de fechas con zona horaria
Utiliza el hook useIntl para acceder al objeto intl y su método formatDate. Este componente acepta una marca de tiempo ISO y la formatea para la zona horaria local del usuario.
import { useIntl } from "react-intl";
interface LocalTimeProps {
timestamp: string;
showDate?: boolean;
showTime?: boolean;
}
export function LocalTime({
timestamp,
showDate = true,
showTime = true,
}: LocalTimeProps) {
const intl = useIntl();
const date = new Date(timestamp);
const formatted = intl.formatDate(date, {
...(showDate && {
year: "numeric",
month: "short",
day: "numeric",
}),
...(showTime && {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
}),
});
return <time dateTime={timestamp}>{formatted}</time>;
}
El componente FormattedDate y el método formatDate utilizan las API Intl.DateTimeFormat con DateTimeFormatOptions. El navegador aplica automáticamente la zona horaria del usuario cuando no se proporciona un timeZone explícito, convirtiendo la marca de tiempo UTC a hora local.
2. Usa el componente para mostrar las horas de los eventos
Importa y usa el componente LocalTime donde necesites mostrar marcas de tiempo. El componente gestiona la conversión de zona horaria automáticamente según la configuración del navegador de cada usuario.
import { createFileRoute } from "@tanstack/react-router";
import { LocalTime } from "~/components/LocalTime";
export const Route = createFileRoute("/events/$eventId")({
component: EventDetail,
});
function EventDetail() {
const event = Route.useLoaderData();
return (
<div>
<h1>{event.title}</h1>
<p>
Starts: <LocalTime timestamp={event.startTime} />
</p>
<p>
Ends: <LocalTime timestamp={event.endTime} />
</p>
</div>
);
}
Cada usuario ve las horas de los eventos convertidas a su zona horaria local. La marca de tiempo ISO del servidor se analiza y formatea según las preferencias de configuración regional y zona horaria del usuario.
3. Formatea las horas con visualización explícita de zona horaria
Cuando los usuarios necesiten saber qué zona horaria representa una hora, incluye el nombre de la zona horaria en las opciones de formato.
import { useIntl } from "react-intl";
interface ExplicitTimezoneProps {
timestamp: string;
timezone?: string;
}
export function ExplicitTimezone({
timestamp,
timezone,
}: ExplicitTimezoneProps) {
const intl = useIntl();
const date = new Date(timestamp);
const formatted = intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: timezone,
timeZoneName: "long",
});
return <time dateTime={timestamp}>{formatted}</time>;
}
Especificar una opción timeZone obliga al formateador a mostrar la hora en esa zona horaria específica. Cuando timezone es undefined, el navegador usa la zona horaria local del usuario. Esto es útil para mostrar horas en una zona horaria específica independientemente de dónde se encuentre el usuario.
4. Crea un helper para la visualización de tiempo relativo
Para eventos recientes, muestra tiempos relativos como "hace 2 horas" mientras preservas el tiempo absoluto en un tooltip.
import { useIntl } from "react-intl";
interface RelativeTimeProps {
timestamp: string;
}
export function RelativeTime({ timestamp }: RelativeTimeProps) {
const intl = useIntl();
const date = new Date(timestamp);
const now = Date.now();
const diffInSeconds = Math.floor((now - date.getTime()) / 1000);
const absoluteTime = intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
let value: number;
let unit: Intl.RelativeTimeFormatUnit;
if (diffInSeconds < 60) {
value = -diffInSeconds;
unit = "second";
} else if (diffInSeconds < 3600) {
value = -Math.floor(diffInSeconds / 60);
unit = "minute";
} else if (diffInSeconds < 86400) {
value = -Math.floor(diffInSeconds / 3600);
unit = "hour";
} else {
value = -Math.floor(diffInSeconds / 86400);
unit = "day";
}
const relativeTime = intl.formatRelativeTime(value, unit);
return (
<time dateTime={timestamp} title={absoluteTime}>
{relativeTime}
</time>
);
}
El tiempo relativo actualiza la visualización para mostrar duraciones amigables para el usuario mientras el atributo title preserva la hora local exacta para los usuarios que pasan el cursor por encima. Ambos formatos respetan automáticamente la zona horaria y la configuración regional del usuario.