Cómo formatear rangos de fechas como 1 ene - 5 ene
Usa JavaScript para mostrar rangos de fechas con formato apropiado al idioma y eliminación inteligente de redundancias
Introducción
Los rangos de fechas aparecen en todas las aplicaciones web. Los sistemas de reservas muestran disponibilidad del 1 de enero al 5 de enero, los calendarios de eventos muestran conferencias de varios días, los paneles de análisis muestran datos para períodos de tiempo específicos y los informes cubren trimestres fiscales o intervalos de fechas personalizados. Estos rangos comunican que algo aplica a todas las fechas entre dos puntos finales.
Cuando formateas rangos de fechas manualmente concatenando dos fechas formateadas con un guion, creas una salida innecesariamente verbosa. Un rango del 1 de enero de 2024 al 5 de enero de 2024 no necesita repetir el mes y el año en la segunda fecha. La salida "1 - 5 de enero de 2024" transmite la misma información de forma más concisa al omitir componentes redundantes.
JavaScript proporciona el método formatRange() en Intl.DateTimeFormat para manejar el formateo de rangos de fechas automáticamente. Este método determina qué componentes de fecha incluir en cada parte del rango, aplicando convenciones específicas del idioma para separadores y formato mientras elimina repeticiones innecesarias.
Por qué los rangos de fechas necesitan formato inteligente
Diferentes rangos de fechas requieren diferentes niveles de detalle. Un rango dentro del mismo mes necesita menos información que un rango que abarca varios años. El formato óptimo depende de qué componentes de fecha difieren entre las fechas de inicio y fin.
Para un rango dentro del mismo día, solo necesitas mostrar la diferencia de tiempo: "10:00 a. m. - 2:00 p. m.". Repetir la fecha completa para ambas horas no añade información.
Para un rango dentro del mismo mes, muestras el mes una vez y enumeras ambos números de día: "1-5 de enero de 2024". Incluir "enero" dos veces hace que la salida sea más difícil de leer sin añadir claridad.
Para un rango que abarca diferentes meses en el mismo año, se muestran ambos meses pero se puede omitir el año de la primera fecha: "December 25, 2024 - January 2, 2025" necesita la información completa, pero "January 15 - February 20, 2024" puede omitir el año de la primera fecha en algunas configuraciones regionales.
Para rangos que abarcan múltiples años, debe incluir el año para ambas fechas: "December 1, 2023 - March 15, 2024".
La implementación manual de estas reglas requiere verificar qué componentes de fecha difieren y construir cadenas de formato en consecuencia. Diferentes configuraciones regionales también aplican estas reglas de manera diferente, utilizando diferentes separadores y convenciones de orden. El método formatRange() encapsula esta lógica.
Usar formatRange para formatear rangos de fechas
El método formatRange() acepta dos objetos Date y devuelve una cadena formateada. Cree una instancia de Intl.DateTimeFormat con la configuración regional y las opciones deseadas, luego llame a formatRange() con las fechas de inicio y fin.
const formatter = new Intl.DateTimeFormat("en-US");
const start = new Date(2024, 0, 1);
const end = new Date(2024, 0, 5);
console.log(formatter.formatRange(start, end));
// Output: "1/1/24 – 1/5/24"
El formateador aplica el formato de fecha predeterminado del inglés estadounidense a ambas fechas y las conecta con una raya. Para fechas dentro del mismo mes, la salida aún muestra ambas fechas completas porque el formato predeterminado es bastante corto.
Puede especificar qué componentes de fecha incluir utilizando las mismas opciones disponibles para el formateo de fechas regular.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const start = new Date(2024, 0, 1);
const end = new Date(2024, 0, 5);
console.log(formatter.formatRange(start, end));
// Output: "January 1 – 5, 2024"
Ahora el formateador omite inteligentemente el mes y el año de la segunda fecha porque coinciden con la primera fecha. La salida muestra solo el número de día para la fecha de fin, haciendo el rango más legible.
Cómo formatRange optimiza la salida de rangos de fechas
El método formatRange() examina ambas fechas y determina qué componentes difieren. Incluye solo los componentes necesarios en cada parte del rango.
Para fechas en el mismo mes y año, solo aparece el día final en la segunda parte.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "January 1 – 5, 2024"
console.log(formatter.formatRange(
new Date(2024, 0, 15),
new Date(2024, 0, 20)
));
// Output: "January 15 – 20, 2024"
Ambos rangos muestran el mes y el año una vez, con solo los números de día conectados por el separador de rango.
Para fechas en diferentes meses del mismo año, aparecen ambos meses pero el año aparece solo una vez.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.formatRange(
new Date(2024, 0, 15),
new Date(2024, 1, 20)
));
// Output: "January 15 – February 20, 2024"
El formateador incluye ambos nombres de mes pero coloca el año al final, aplicándolo a todo el rango.
Para fechas que abarcan diferentes años, aparecen ambas fechas completas.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.formatRange(
new Date(2023, 11, 25),
new Date(2024, 0, 5)
));
// Output: "December 25, 2023 – January 5, 2024"
Cada fecha incluye su año completo porque los años difieren. El formateador no puede omitir ningún año sin crear ambigüedad.
Formateo de rangos de fecha y hora
Cuando tu formato incluye componentes de hora, el método formatRange() aplica la misma omisión inteligente a los campos de hora.
Para horas en el mismo día, solo los componentes de hora difieren en la salida.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
const start = new Date(2024, 0, 1, 10, 0);
const end = new Date(2024, 0, 1, 14, 30);
console.log(formatter.formatRange(start, end));
// Output: "January 1, 2024, 10:00 AM – 2:30 PM"
La fecha aparece una vez, seguida del rango de hora. El formateador reconoce que repetir la fecha y hora completas para el final no añadiría información útil.
Para horas en diferentes días, aparecen ambos valores completos de fecha y hora.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
const start = new Date(2024, 0, 1, 10, 0);
const end = new Date(2024, 0, 2, 14, 30);
console.log(formatter.formatRange(start, end));
// Output: "January 1, 2024, 10:00 AM – January 2, 2024, 2:30 PM"
Aparecen ambas fechas y horas porque representan días diferentes. El formateador no puede omitir ningún componente de forma segura.
Formateo de rangos de fechas en diferentes locales
El formateo de rangos se adapta a las convenciones de cada locale para el orden de componentes de fecha, separadores y qué componentes omitir.
const enFormatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(enFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "January 1 – 5, 2024"
const deFormatter = new Intl.DateTimeFormat("de-DE", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(deFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "1.–5. Januar 2024"
const jaFormatter = new Intl.DateTimeFormat("ja-JP", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(jaFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "2024年1月1日~5日"
El inglés coloca el mes primero y muestra el año al final. El alemán coloca los números de día primero con puntos, luego el nombre del mes, luego el año. El japonés usa el orden año-mes-día y el guion de onda (~) como separador de rango. Cada locale aplica sus propias convenciones sobre qué componentes mostrar una vez versus dos veces.
Estas diferencias se extienden a rangos entre meses.
const enFormatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(enFormatter.formatRange(
new Date(2024, 0, 15),
new Date(2024, 1, 20)
));
// Output: "January 15 – February 20, 2024"
const deFormatter = new Intl.DateTimeFormat("de-DE", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(deFormatter.formatRange(
new Date(2024, 0, 15),
new Date(2024, 1, 20)
));
// Output: "15. Januar – 20. Februar 2024"
const frFormatter = new Intl.DateTimeFormat("fr-FR", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(frFormatter.formatRange(
new Date(2024, 0, 15),
new Date(2024, 1, 20)
));
// Output: "15 janvier – 20 février 2024"
Los tres locales muestran ambos nombres de mes pero los posicionan de manera diferente en relación con los números de día. Los formateadores manejan estas variaciones automáticamente.
Formateo de rangos de fecha con diferentes estilos
Puedes usar la opción dateStyle para controlar la longitud general del formato, igual que con el formateo de una sola fecha.
const shortFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "short"
});
console.log(shortFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "1/1/24 – 1/5/24"
const mediumFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium"
});
console.log(mediumFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "Jan 1 – 5, 2024"
const longFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "long"
});
console.log(longFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "January 1 – 5, 2024"
const fullFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "full"
});
console.log(fullFormatter.formatRange(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
));
// Output: "Monday, January 1 – Friday, January 5, 2024"
El estilo short produce fechas numéricas y no aplica omisión inteligente porque el formato ya es compacto. Los estilos medium y long abrevian o escriben completo el mes y omiten componentes redundantes. El estilo full incluye nombres de días de la semana para ambas fechas.
Uso de formatRangeToParts para estilo personalizado
El método formatRangeToParts() devuelve un array de objetos que representan los componentes del rango formateado. Esto te permite aplicar estilos o manipular partes individuales de la salida del rango.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const parts = formatter.formatRangeToParts(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
);
console.log(parts);
La salida es un array de objetos, cada uno con las propiedades type, value y source.
[
{ type: "month", value: "January", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "day", value: "1", source: "startRange" },
{ type: "literal", value: " – ", source: "shared" },
{ type: "day", value: "5", source: "endRange" },
{ type: "literal", value: ", ", source: "shared" },
{ type: "year", value: "2024", source: "shared" }
]
La propiedad type identifica el componente: mes, día, año o texto literal. La propiedad value contiene el texto formateado. La propiedad source indica si el componente pertenece a la fecha de inicio, fecha de fin, o es compartido entre ambas.
Puedes usar estas partes para crear HTML personalizado con estilos para diferentes componentes.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const parts = formatter.formatRangeToParts(
new Date(2024, 0, 1),
new Date(2024, 0, 5)
);
let html = "";
parts.forEach(part => {
if (part.type === "month") {
html += `<span class="month">${part.value}</span>`;
} else if (part.type === "day") {
html += `<span class="day">${part.value}</span>`;
} else if (part.type === "year") {
html += `<span class="year">${part.value}</span>`;
} else if (part.type === "literal" && part.source === "shared" && part.value.includes("–")) {
html += `<span class="separator">${part.value}</span>`;
} else {
html += part.value;
}
});
console.log(html);
// Output: <span class="month">January</span> <span class="day">1</span><span class="separator"> – </span><span class="day">5</span>, <span class="year">2024</span>
Esta técnica preserva el formateo específico del locale mientras permite un estilo visual personalizado.
Qué sucede cuando las fechas son iguales
Cuando pasas la misma fecha para ambos parámetros de inicio y fin, formatRange() genera una sola fecha formateada en lugar de un rango.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2024, 0, 1);
console.log(formatter.formatRange(date, date));
// Output: "January 1, 2024"
El formateador reconoce que un rango con puntos finales idénticos no es realmente un rango y lo formatea como una sola fecha. Este comportamiento se aplica incluso cuando los objetos Date son instancias diferentes con el mismo valor.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const start = new Date(2024, 0, 1, 10, 0);
const end = new Date(2024, 0, 1, 10, 0);
console.log(formatter.formatRange(start, end));
// Output: "January 1, 2024"
Aunque estos son objetos Date separados, representan la misma fecha y hora. El formateador muestra una única fecha porque el rango tiene una duración cero al nivel de precisión de las opciones de formato. Dado que el formato no incluye componentes de tiempo, las horas son irrelevantes y las fechas se consideran iguales.
Cuándo usar formatRange vs formateo manual
Usa formatRange() al mostrar rangos de fechas a los usuarios. Esto se aplica a rangos de fechas de reserva, duración de eventos, períodos de informes, ventanas de disponibilidad o cualquier otro intervalo de tiempo. El método garantiza el formateo correcto específico del idioma y la omisión óptima de componentes.
Evita formatRange() cuando necesites mostrar múltiples fechas no relacionadas. Una lista de fechas límite como "1 de enero, 15 de enero, 1 de febrero" debe usar llamadas regulares a format() para cada fecha en lugar de tratarlas como rangos.
También evita formatRange() al mostrar comparaciones o diferencias entre fechas. Si estás mostrando cuánto antes o después es una fecha en comparación con otra, eso representa un cálculo de tiempo relativo en lugar de un rango.