Cómo gestionar zonas horarias en Next.js (Pages Router) v16
Mostrar horarios en la zona horaria local del usuario
Problema
Cuando las aplicaciones muestran horarios sin tener en cuenta la ubicación del usuario, surge confusión. Una reunión programada para "3:00 PM" significa momentos diferentes dependiendo de si el espectador está en Nueva York, Londres o Tokio. Si el servidor renderiza un horario en UTC o en su propia zona horaria, los usuarios en otras regiones ven horarios locales incorrectos, lo que lleva a citas perdidas y errores de programación. La misma marca de tiempo debe interpretarse de manera diferente para cada usuario según su zona horaria.
Solución
Almacena las marcas de tiempo en un formato universal como ISO 8601 o marcas de tiempo Unix, luego formatéalas para mostrarlas en la zona horaria local del usuario en el cliente. Detecta la zona horaria del usuario usando la API de internacionalización del navegador y pásala a las funciones de formateo de react-intl. Esto garantiza que cada usuario vea los horarios ajustados a su propia zona horaria, eliminando la ambigüedad sobre cuándo ocurren los eventos.
Pasos
1. Detectar la zona horaria del usuario en el cliente
El método Intl.DateTimeFormat().resolvedOptions().timeZone del navegador devuelve el identificador de zona horaria IANA del usuario, como America/New_York o Europe/London. Crea un hook personalizado para acceder a este valor.
import { useState, useEffect } from "react";
export function useUserTimeZone() {
const [timeZone, setTimeZone] = useState<string>("UTC");
useEffect(() => {
const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
setTimeZone(detected);
}, []);
return timeZone;
}
Este hook se ejecuta solo en el cliente, evitando desajustes de hidratación, y por defecto usa UTC hasta que se detecte la zona horaria.
2. Formatear fechas con la zona horaria del usuario usando FormattedDate
Pasa la opción timeZone a los métodos de formateo de react-intl para controlar qué zona horaria se usa para la visualización. Usa el componente <FormattedDate> con la zona horaria detectada.
import { FormattedDate } from "react-intl";
import { useUserTimeZone } from "../hooks/useUserTimeZone";
interface EventDateProps {
timestamp: string;
}
export function EventDate({ timestamp }: EventDateProps) {
const userTimeZone = useUserTimeZone();
return (
<FormattedDate
value={new Date(timestamp)}
timeZone={userTimeZone}
year="numeric"
month="long"
day="numeric"
hour="numeric"
minute="2-digit"
timeZoneName="short"
/>
);
}
La prop timeZone garantiza que la fecha se formatee en la zona horaria local del usuario en lugar de la del servidor o UTC.
3. Formatear tiempos imperativamente con useIntl
Para escenarios donde necesitas una cadena formateada en lugar de un componente, usa el hook useIntl para acceder a la API de formateo imperativa.
import { useIntl } from "react-intl";
import { useUserTimeZone } from "../hooks/useUserTimeZone";
interface MeetingTimeProps {
startTime: string;
}
export function MeetingTime({ startTime }: MeetingTimeProps) {
const intl = useIntl();
const userTimeZone = useUserTimeZone();
const formattedTime = intl.formatDate(new Date(startTime), {
timeZone: userTimeZone,
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
});
return <span title={startTime}>{formattedTime}</span>;
}
El método formatDate acepta un segundo argumento con DateTimeFormatOptions, incluyendo timeZone, permitiendo control total sobre cómo se muestra la marca de tiempo.
4. Pasar marcas de tiempo del servidor como cadenas ISO
En getServerSideProps, obtén datos en tiempo de solicitud y pásalos al componente de página como props. Serializa las fechas como cadenas ISO 8601 para preservar la información de zona horaria.
import { GetServerSideProps } from "next";
interface Event {
id: string;
title: string;
startTime: string;
}
interface EventPageProps {
event: Event;
}
export const getServerSideProps: GetServerSideProps<
EventPageProps
> = async () => {
const event = {
id: "1",
title: "Team Meeting",
startTime: new Date("2025-02-15T15:00:00Z").toISOString(),
};
return {
props: { event },
};
};
export default function EventPage({ event }: EventPageProps) {
return (
<div>
<h1>{event.title}</h1>
<EventDate timestamp={event.startTime} />
</div>
);
}
Las cadenas ISO preservan el momento exacto en el tiempo, permitiendo que el formateo del lado del cliente las convierta con precisión a cualquier zona horaria.