Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
npm install date-fnsimport { format, parseISO, addDays, differenceInDays } from "date-fns";
// Formatea una fecha
format(new Date(), "MMMM d, yyyy"); // "April 6, 2026"
// Analiza cadena ISO
const date = parseISO("2026-04-06T12:00:00Z");
// Suma días
const nextWeek = addDays(new Date(), 7);
// Diferencia entre fechas
const days = differenceInDays(new Date("2026-12-31"), new Date()); // días hasta fin de añoCuándo usarlo: Necesitas formatear, analizar, comparar o manipular fechas con una biblioteca ligera y tree-shakable que use objetos Date nativos.
// app/components/ActivityFeed.tsx
"use client";
import {
format,
formatDistanceToNow,
isToday,
isYesterday,
isThisWeek,
parseISO,
} from "date-fns";
interface Activity {
id: string;
action: string;
timestamp: string; // ISO string
user: string;
}
const ACTIVITIES: Activity[] = [
{ id: "1", action: "pushed to main", timestamp: new Date().toISOString(), user: "Alice" },
{ id: "2", action: "opened PR #42", timestamp: new Date(Date.now() - 3600000).toISOString(), user: "Bob" },
{ id: "3", action: "merged PR #41", timestamp: new Date(Date.now() - 86400000).toISOString(), user: "Carol" },
{ id: "4", action: "created issue", timestamp: new Date(Date.now() - 259200000).toISOString(), user: "Dave" },
{ id: "5", action: "deployed v2.1", timestamp: "2026-03-20T10:30:00Z", user: "Eve" },
];
function formatTimestamp(isoString: string): string {
const date = parseISO(isoString);
if (isToday(date)) {
return formatDistanceToNow(date, { addSuffix: true }); // "2 hours ago"
}
if (isYesterday(date)) {
return `Yesterday at ${format(date, "h:mm a")}`; // "Yesterday at 3:30 PM"
}
if (isThisWeek(date)) {
return format(date, "EEEE 'at' h:mm a"); // "Monday at 10:30 AM"
}
return format(date, "MMM d, yyyy"); // "Mar 20, 2026"
}
export default function ActivityFeed() {
return (
<div className="max-w-lg mx-auto p-6">
<h2 className="text-xl font-bold mb-4">Actividad</h2>
<ul className="space-y-3">
{ACTIVITIES.map((activity) => (
<li key={activity.id} className="flex justify-between items-center border-b pb-2">
<div>
<span className="font-medium">{activity.user}</span>{" "}
<span className="text-gray-600">{activity.action}</span>
</div>
<time
dateTime={activity.timestamp}
className="text-sm text-gray-400 whitespace-nowrap"
>
{formatTimestamp(activity.timestamp)}
</time>
</li>
))}
</ul>
</div>
);
}Lo que esto demuestra:
parseISO para análisis seguro de cadenas ISOisToday, isYesterday, isThisWeek para formateo condicionalformatDistanceToNow para salida de estilo "hace X horas"<time> con atributo dateTimeformat usa tokens de Unicode Technical Standard #35: yyyy (año), MM (mes), dd (día), HH (24h), hh (12h), mm (minutos), ss (segundos)parseISO se prefiere sobre new Date(string) porque maneja cadenas ISO 8601 consistentemente entre navegadoresisBefore, isAfter, isEqual) comparan marcas de tiempo, no cadenas de fechaAritmética de fechas:
import {
addDays,
addMonths,
addHours,
subWeeks,
startOfMonth,
endOfMonth,
eachDayOfInterval,
} from "date-fns";
const tomorrow = addDays(new Date(), 1);
const nextMonth = addMonths(new Date(), 1);
const twoHoursLater = addHours(new Date(), 2);
const lastWeek = subWeeks(new Date(), 1);
// Obtén todos los días del mes actual (útil para calendarios)
const monthStart = startOfMonth(new Date());
const monthEnd = endOfMonth(new Date());
const daysInMonth = eachDayOfInterval({ start: monthStart, end: monthEnd });Soporte de locales:
import { format, formatDistanceToNow } from "date-fns";
import { fr } from "date-fns/locale";
import { ja } from "date-fns/locale";
format(new Date(), "EEEE d MMMM yyyy", { locale: fr });
// "lundi 6 avril 2026"
formatDistanceToNow(new Date(Date.now() - 7200000), {
addSuffix: true,
locale: ja,
});
// "約2時間前"Rango de fecha y comparación:
import {
isWithinInterval,
isBefore,
isAfter,
differenceInCalendarDays,
differenceInBusinessDays,
areIntervalsOverlapping,
} from "date-fns";
const eventStart = new Date("2026-04-10");
const eventEnd = new Date("2026-04-15");
// Comprueba si una fecha está en rango
isWithinInterval(new Date("2026-04-12"), {
start: eventStart,
end: eventEnd,
}); // true
// Días de negocio entre fechas (excluye fines de semana)
differenceInBusinessDays(eventEnd, eventStart); // 3
// Comprueba intervalos superpuestos
areIntervalsOverlapping(
{ start: eventStart, end: eventEnd },
{ start: new Date("2026-04-13"), end: new Date("2026-04-20") }
); // trueAnálisis de cadenas de fecha personalizadas:
import { parse, isValid } from "date-fns";
const date = parse("04/06/2026", "MM/dd/yyyy", new Date());
console.log(isValid(date)); // true
const invalid = parse("not-a-date", "MM/dd/yyyy", new Date());
console.log(isValid(invalid)); // falseDate o number (marca de tiempo) como argumentos de fechaformat devuelve string, addDays devuelve Date, differenceInDays devuelve numberdate-fns/locale@types adicional; los tipos están incluidos en date-fnsimport type { Locale } from "date-fns";
import { fr } from "date-fns/locale";
function formatDate(date: Date | number, locale: Locale = fr): string {
return format(date, "PPP", { locale });
}Confusión de zona horaria - Los objetos Date siempre están en la zona horaria local. format genera salida de hora local, no UTC. Solución: Usa date-fns-tz para formateo consciente de zona horaria, o usa formatInTimeZone desde date-fns-tz.
Sensibilidad de mayúsculas en tokens de formato - MM es mes, mm son minutos. DD no es válido (usa dd). Solución: Consulta la tabla de tokens de formato. Tokens comunes: yyyy-MM-dd HH:mm:ss.
parseISO vs new Date - new Date("2026-04-06") se analiza como UTC medianoche, pero se muestra en hora local, lo que puede cambiar la fecha. Solución: Siempre usa parseISO para cadenas ISO para obtener comportamiento consistente.
Trampa Date mutable - Aunque las funciones date-fns son puras, si pasas la misma instancia Date a varios lugares después de mutarla en otro lugar, obtendrás resultados inesperados. Solución: Crea nuevas instancias Date o usa los valores de retorno de funciones date-fns.
Tamaño del bundle con locales - Importar todos los locales agrega un tamaño de bundle significativo. Solución: Importa solo los locales que necesitas: import { fr } from "date-fns/locale", no import * as locales from "date-fns/locale".
Desincronización de fecha servidor/cliente - Las fechas renderizadas en el servidor usan la zona horaria del servidor, causando desincronizaciones de hidratación. Solución: Formatea fechas solo en el lado del cliente, o pasa cadenas pre-formateadas desde el servidor, o usa suppressHydrationWarning en el elemento <time>.
| Biblioteca | Mejor para | Compensación |
|---|---|---|
| date-fns | API funcional tree-shakable | Sin soporte de zona horaria sin date-fns-tz |
| dayjs | Remplazo drop-in de Moment.js (2KB) | API basada en plugins, mutable |
| Temporal (proposal) | API de fecha JS nativa futura | Aún no disponible en todos los runtimes |
| Luxon | Soporte completo de zona horaria e i18n | Bundle más grande (alrededor de 20KB) |
| Moment.js | Solo proyectos legacy | Deprecado, 67KB minificado, mutable |
new Date("2026-04-06") se analiza como UTC medianoche pero se muestra en hora local, lo que puede cambiar la fecha por un díaparseISO maneja cadenas ISO 8601 consistentemente entre todos los navegadoresimport { formatDistanceToNow } from "date-fns";
formatDistanceToNow(someDate, { addSuffix: true });
// "2 hours ago" or "in 3 days"La opción addSuffix antepone "in" o añade "ago" automáticamente.
MM = mes (01-12), mm = minutos (00-59)DD no es válido -- usa dd para día del mesyyyy-MM-dd HH:mm:ssimport { startOfMonth, endOfMonth, eachDayOfInterval } from "date-fns";
const start = startOfMonth(new Date());
const end = endOfMonth(new Date());
const days = eachDayOfInterval({ start, end });import { format } from "date-fns";
import { fr } from "date-fns/locale";
format(new Date(), "EEEE d MMMM yyyy", { locale: fr });
// "lundi 6 avril 2026"Importa solo los locales que necesitas para mantener el tamaño del bundle pequeño.
differenceInDays calcula basado en períodos completos de 24 horasdifferenceInCalendarDays cuenta límites de día de calendario cruzadosdifferenceInDays pero 1 con differenceInCalendarDaysimport { isWithinInterval } from "date-fns";
isWithinInterval(new Date("2026-04-12"), {
start: new Date("2026-04-10"),
end: new Date("2026-04-15"),
}); // truesuppressHydrationWarning en el elemento <time>date-fns-tz para formateo consciente de zona horariaformatInTimeZone para mostrar fechas en una zona horaria específicaimport type { Locale } from "date-fns";
import { format } from "date-fns";
function formatDate(
date: Date | number,
pattern: string = "PPP",
locale?: Locale
): string {
return format(date, pattern, { locale });
}Todas las funciones date-fns aceptan Date | number como argumentos de fecha.
import { parse, isValid } from "date-fns";
const date = parse("04/06/2026", "MM/dd/yyyy", new Date());
console.log(isValid(date)); // trueSiempre valida con isValid ya que parse devuelve un Date inválido para entrada incorrecta.
import { format } from "date-fns" incluye solo format y sus dependenciasimport * as dateFns from "date-fns" que anula el tree-shakingRevisado por Chris St. John·Última actualización: 19 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥