Comment formater des plages de dates comme 1 janv. - 5 janv.

Utiliser JavaScript pour afficher des plages de dates avec un formatage adapté à la locale et une suppression intelligente des redondances

Introduction

Les plages de dates apparaissent dans de nombreuses applications web. Les systèmes de réservation affichent la disponibilité du 1er janvier au 5 janvier, les calendriers d'événements présentent des conférences de plusieurs jours, les tableaux de bord d'analyse montrent des données pour des périodes spécifiques, et les rapports couvrent des trimestres fiscaux ou des périodes personnalisées. Ces plages indiquent qu'un élément s'applique à toutes les dates comprises entre deux points d'extrémité.

Lorsque vous formatez manuellement des plages de dates en concaténant deux dates formatées avec un tiret, vous créez un résultat inutilement verbeux. Une plage du 1er janvier 2024 au 5 janvier 2024 n'a pas besoin de répéter le mois et l'année dans la seconde date. Le résultat "1 - 5 janvier 2024" transmet la même information de manière plus concise en omettant les composants redondants.

JavaScript fournit la méthode formatRange() sur Intl.DateTimeFormat pour gérer automatiquement le formatage des plages de dates. Cette méthode détermine quels composants de date inclure dans chaque partie de la plage, en appliquant les conventions spécifiques à la locale pour les séparateurs et le formatage tout en supprimant les répétitions inutiles.

Pourquoi les plages de dates nécessitent un formatage intelligent

Différentes plages de dates nécessitent différents niveaux de détail. Une plage au sein du même mois nécessite moins d'informations qu'une plage s'étendant sur plusieurs années. Le format optimal dépend des composants de date qui diffèrent entre les dates de début et de fin.

Pour une plage dans la même journée, vous n'avez besoin que de montrer la différence d'heure : "10h00 - 14h00". Répéter la date complète pour les deux heures n'ajoute aucune information.

Pour une plage dans le même mois, vous montrez le mois une fois et listez les deux numéros de jour : "1-5 janvier 2024". Inclure "janvier" deux fois rend le résultat plus difficile à lire sans ajouter de clarté.

Pour une plage couvrant différents mois de la même année, vous montrez les deux mois mais pouvez omettre l'année de la première date : "25 décembre 2024 - 2 janvier 2025" nécessite l'information complète, mais "15 janvier - 20 février 2024" peut omettre l'année de la première date dans certaines locales.

Pour les plages s'étendant sur plusieurs années, vous devez inclure l'année pour les deux dates : "1er décembre 2023 - 15 mars 2024".

L'implémentation manuelle de ces règles nécessite de vérifier quels composants de date diffèrent et de construire des chaînes de format en conséquence. Différentes locales appliquent également ces règles différemment, en utilisant différents séparateurs et conventions d'ordre. La méthode formatRange() encapsule cette logique.

Utilisation de formatRange pour formater des plages de dates

La méthode formatRange() accepte deux objets Date et renvoie une chaîne formatée. Créez une instance Intl.DateTimeFormat avec la locale et les options souhaitées, puis appelez formatRange() avec les dates de début et de 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));
// Résultat : "1/1/24 – 1/5/24"

Le formateur applique le format de date par défaut en anglais américain aux deux dates et les relie par un tiret demi-cadratin. Pour les dates du même mois, le résultat affiche toujours les deux dates complètes car le format par défaut est assez court.

Vous pouvez spécifier quels composants de date inclure en utilisant les mêmes options disponibles pour le formatage de date standard.

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));
// Résultat : "January 1 – 5, 2024"

Maintenant, le formateur omet intelligemment le mois et l'année de la seconde date car ils correspondent à la première date. Le résultat n'affiche que le numéro du jour pour la date de fin, rendant la plage plus lisible.

Comment formatRange optimise l'affichage des plages de dates

La méthode formatRange() examine les deux dates et détermine quels composants diffèrent. Elle n'inclut que les composants nécessaires dans chaque partie de la plage.

Pour les dates du même mois et de la même année, seul le jour de fin apparaît dans la seconde partie.

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)
));
// Résultat : "January 1 – 5, 2024"

console.log(formatter.formatRange(
  new Date(2024, 0, 15),
  new Date(2024, 0, 20)
));
// Résultat : "January 15 – 20, 2024"

Les deux plages affichent le mois et l'année une seule fois, avec juste les numéros de jour reliés par le séparateur de plage.

Pour les dates de différents mois de la même année, les deux mois apparaissent mais l'année n'apparaît qu'une seule fois.

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)
));
// Résultat : "January 15 – February 20, 2024"

Le formateur inclut les deux noms de mois mais place l'année à la fin, l'appliquant à toute la plage.

Pour les dates couvrant différentes années, les deux dates complètes apparaissent.

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)
));
// Résultat : "December 25, 2023 – January 5, 2024"

Chaque date inclut son année complète car les années diffèrent. Le formateur ne peut omettre aucune année sans créer d'ambiguïté.

Formatage des plages de dates et d'heures

Lorsque votre format inclut des composants temporels, la méthode formatRange() applique la même omission intelligente aux champs de temps.

Pour les heures d'une même journée, seuls les composants temporels diffèrent dans le résultat.

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 date apparaît une seule fois, suivie de la plage horaire. Le formateur reconnaît que répéter la date et l'heure complètes pour la fin n'ajouterait aucune information utile.

Pour les heures sur différents jours, les valeurs complètes de date et d'heure apparaissent toutes les deux.

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"

Les dates et les heures apparaissent toutes les deux car elles représentent des jours différents. Le formateur ne peut pas omettre de composants en toute sécurité.

Formatage des plages de dates dans différentes locales

Le formatage des plages s'adapte aux conventions de chaque locale pour l'ordre des composants de date, les séparateurs et les composants à omettre.

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日"

L'anglais place le mois en premier et affiche l'année à la fin. L'allemand place les numéros de jour en premier avec des points, puis le nom du mois, puis l'année. Le japonais utilise l'ordre année-mois-jour et le tiret ondulé (~) comme séparateur de plage. Chaque locale applique ses propres conventions pour déterminer quels composants afficher une fois ou deux fois.

Ces différences s'étendent aux plages inter-mensuelles.

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"

Les trois locales affichent les deux noms de mois mais les positionnent différemment par rapport aux numéros de jour. Les formateurs gèrent automatiquement ces variations.

Formatage des plages de dates avec différents styles

Vous pouvez utiliser l'option dateStyle pour contrôler la longueur globale du format, tout comme avec le formatage d'une date unique.

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"

Le style short produit des dates numériques et n'applique pas d'omission intelligente car le format est déjà compact. Les styles medium et long abrègent ou écrivent le mois en toutes lettres et omettent les composants redondants. Le style full inclut les noms des jours de la semaine pour les deux dates.

Utilisation de formatRangeToParts pour un style personnalisé

La méthode formatRangeToParts() renvoie un tableau d'objets représentant les composants de la plage formatée. Cela vous permet de styliser ou de manipuler individuellement les différentes parties de la sortie de la plage.

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 sortie est un tableau d'objets, chacun avec les propriétés type, value et 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 propriété type identifie le composant : mois, jour, année ou texte littéral. La propriété value contient le texte formaté. La propriété source indique si le composant appartient à la date de début, à la date de fin, ou s'il est partagé entre les deux.

Vous pouvez utiliser ces parties pour créer du HTML personnalisé avec un style pour différents composants.

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>

Cette technique préserve le formatage spécifique à la locale tout en permettant un style visuel personnalisé.

Que se passe-t-il lorsque les dates sont identiques

Lorsque vous passez la même date pour les paramètres de début et de fin, formatRange() affiche une seule date formatée plutôt qu'une plage.

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));
// Résultat : "January 1, 2024"

Le formateur reconnaît qu'une plage avec des points de début et de fin identiques n'est pas vraiment une plage et la formate comme une date unique. Ce comportement s'applique même lorsque les objets Date sont des instances différentes avec la même valeur.

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));
// Résultat : "January 1, 2024"

Même s'il s'agit d'objets Date distincts, ils représentent la même date et heure. Le formateur affiche une seule date car la plage a une durée nulle au niveau de précision des options de format. Comme le format n'inclut pas de composants temporels, les heures sont non pertinentes et les dates sont considérées comme égales.

Quand utiliser formatRange vs formatage manuel

Utilisez formatRange() lors de l'affichage de plages de dates aux utilisateurs. Cela s'applique aux plages de dates de réservation, à la durée des événements, aux périodes de rapport, aux fenêtres de disponibilité ou à tout autre intervalle de temps. La méthode assure un formatage correct spécifique à la locale et une omission optimale des composants.

Évitez formatRange() lorsque vous devez afficher plusieurs dates sans rapport entre elles. Une liste d'échéances comme "1er janvier, 15 janvier, 1er février" devrait utiliser des appels réguliers à format() pour chaque date plutôt que de les traiter comme des plages.

Évitez également formatRange() lors de l'affichage de comparaisons ou de différences entre dates. Si vous affichez combien une date est plus tôt ou plus tard par rapport à une autre, cela représente un calcul de temps relatif plutôt qu'une plage.