Cómo formatear tiempo relativo en Next.js (Enrutador de Páginas) v16
Formatear marcas de tiempo como frases '2 días atrás'
Problema
Mostrar marcas de tiempo como frases de tiempo relativo como "hace 2 días" o "en 3 horas" hace que el contenido se sienta inmediato y contextual. Sin embargo, estas frases siguen patrones gramaticales complejos que varían dramáticamente entre idiomas. El inglés coloca "ago" después de las cantidades pasadas y "in" antes de las futuras, pero otros idiomas pueden usar diferentes órdenes de palabras, flexionar unidades de tiempo basadas en la cantidad, o emplear estructuras gramaticales completamente diferentes. Construir manualmente estas frases con concatenación de cadenas produce resultados incorrectos en todos los idiomas excepto en el que fue escrito, rompiendo la experiencia de usuario para audiencias internacionales.
Solución
Utiliza el formato de tiempo relativo de react-intl para convertir diferencias numéricas de tiempo en frases gramaticalmente correctas para el idioma del usuario. Calcula la diferencia de tiempo entre una marca de tiempo y el momento actual, determina la unidad apropiada (segundos, minutos, horas, días), y pasa ambos valores a las APIs de formateo de react-intl. La API subyacente Intl.RelativeTimeFormat maneja automáticamente las reglas gramaticales específicas del idioma, el orden de las palabras y las reglas de flexión.
Pasos
1. Crear un helper para calcular valores de tiempo relativos
La diferencia de tiempo debe convertirse en un valor numérico y una unidad apropiada. Construye una función que compare una marca de tiempo con el tiempo actual y seleccione la mejor unidad.
export function getRelativeTimeValue(date: Date | number) {
const now = Date.now();
const timestamp = typeof date === "number" ? date : date.getTime();
const diffInSeconds = (timestamp - now) / 1000;
const minute = 60;
const hour = minute * 60;
const day = hour * 24;
const week = day * 7;
const month = day * 30;
const year = day * 365;
const absDiff = Math.abs(diffInSeconds);
if (absDiff < minute) {
return { value: Math.round(diffInSeconds), unit: "second" as const };
} else if (absDiff < hour) {
return {
value: Math.round(diffInSeconds / minute),
unit: "minute" as const,
};
} else if (absDiff < day) {
return { value: Math.round(diffInSeconds / hour), unit: "hour" as const };
} else if (absDiff < week) {
return { value: Math.round(diffInSeconds / day), unit: "day" as const };
} else if (absDiff < month) {
return { value: Math.round(diffInSeconds / week), unit: "week" as const };
} else if (absDiff < year) {
return { value: Math.round(diffInSeconds / month), unit: "month" as const };
} else {
return { value: Math.round(diffInSeconds / year), unit: "year" as const };
}
}
Esta función devuelve un valor numérico con signo (negativo para el pasado, positivo para el futuro) y la unidad más apropiada basada en la magnitud de la diferencia.
2. Formatear tiempo relativo con el hook useIntl
La función formatRelativeTime devuelve una cadena de tiempo relativo formateada y espera un valor numérico, una unidad y opciones que se ajusten a Intl.RelativeTimeFormatOptions. Utiliza el helper del paso 1 para obtener el valor y la unidad, luego formatéalo.
import { useIntl } from "react-intl";
import { getRelativeTimeValue } from "@/lib/relative-time";
export default function CommentTimestamp({ date }: { date: Date }) {
const intl = useIntl();
const { value, unit } = getRelativeTimeValue(date);
return (
<time dateTime={date.toISOString()}>
{intl.formatRelativeTime(value, unit)}
</time>
);
}
El método formatRelativeTime produce frases apropiadas según la localización como "hace 2 días" en español o "il y a 2 jours" en francés.
3. Usar FormattedRelativeTime para formateo declarativo
FormattedRelativeTime implementa características avanzadas como actualización a lo largo del tiempo y utiliza la API formatRelativeTime con propiedades para valor, unidad y opciones de formateo relativo. Para componentes que necesitan actualizaciones automáticas, utiliza la forma de componente.
import { FormattedRelativeTime } from "react-intl";
import { getRelativeTimeValue } from "@/lib/relative-time";
export default function LiveTimestamp({ date }: { date: Date }) {
const { value, unit } = getRelativeTimeValue(date);
return (
<time dateTime={date.toISOString()}>
<FormattedRelativeTime
value={value}
unit={unit}
numeric="auto"
updateIntervalInSeconds={10}
/>
</time>
);
}
La propiedad updateIntervalInSeconds controla con qué frecuencia se vuelve a renderizar el componente, manteniendo actualizada la frase mostrada a medida que pasa el tiempo.
4. Personalizar el estilo de formateo
El componente acepta RelativeTimeFormatOptions incluyendo propiedades numeric y style. Ajusta estas opciones para controlar el formato de salida.
import { FormattedRelativeTime } from "react-intl";
import { getRelativeTimeValue } from "@/lib/relative-time";
export default function CompactTimestamp({ date }: { date: Date }) {
const { value, unit } = getRelativeTimeValue(date);
return (
<FormattedRelativeTime
value={value}
unit={unit}
numeric="auto"
style="narrow"
/>
);
}
Establecer numeric="auto" produce frases como "ayer" en lugar de "hace 1 día" cuando es apropiado, y style="narrow" crea formas más cortas como "hace 2 m." en lugar de "hace 2 meses".