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

Utilisez 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 partout dans les applications web. Les systèmes de réservation affichent les disponibilités du 1er janvier au 5 janvier, les calendriers d'événements présentent des conférences de plusieurs jours, les tableaux de bord analytiques 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 communiquent qu'une information s'applique à toutes les dates entre deux points de départ et d'arrivée.

Lorsque vous formatez manuellement des plages de dates en concaténant deux dates formatées avec un tiret, vous créez une sortie inutilement verbeuse. 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. La sortie "1er - 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 couvrant 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 au sein du même jour, vous devez uniquement afficher la différence horaire : "10:00 - 14:00". Répéter la date complète pour les deux heures n'ajoute aucune information.

Pour une plage de dates au sein du même mois, vous affichez le mois une seule 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 apporter de clarté supplémentaire.

Pour une plage de dates couvrant différents mois de la même année, vous affichez 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 de dates couvrant 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 les chaînes de format en conséquence. Différentes locales appliquent également ces règles différemment, en utilisant des séparateurs et des conventions d'ordre différents. La méthode formatRange() encapsule cette logique.

Utiliser formatRange pour formater des plages de dates

La méthode formatRange() accepte deux objets Date et retourne 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));
// Output: "1/1/24 – 1/5/24"

Le formateur applique le format de date anglais américain par défaut aux deux dates et les relie avec un tiret demi-cadratin. Pour les dates au sein 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));
// Output: "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 affiche uniquement le numéro de 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 deuxième 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)
));
// Output: "January 1 – 5, 2024"

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

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

Pour les dates de mois différents 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)
));
// Output: "January 15 – February 20, 2024"

Le formateur inclut les deux noms de mois mais place l'année à la fin, en l'appliquant à l'ensemble de 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)
));
// Output: "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 horaires.

Pour les heures du même jour, seuls les composants temporels diffèrent dans la sortie.

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 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 de jours différents, les deux valeurs complètes de date et d'heure apparaissent.

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 deux dates et heures apparaissent 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 d'abord les chiffres du jour 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 couvrant plusieurs mois.

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 ces variations automatiquement.

Formatage des plages de dates avec différents styles

Vous pouvez utiliser l'option dateStyle pour contrôler la longueur globale du format, comme pour 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 entier 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 des parties individuelles de la sortie de 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 elles.

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 égales

Lorsque vous passez la même date pour les paramètres de début et de fin, formatRange() génère 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));
// Output: "January 1, 2024"

Le formateur reconnaît qu'une plage avec des extrémités identiques n'est pas vraiment une plage et la formate comme une seule date. 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));
// Output: "January 1, 2024"

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

Quand utiliser formatRange plutôt que le 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 d'événements, aux périodes de rapports, aux fenêtres de disponibilité ou à toute autre période. La méthode garantit un formatage correct spécifique à la locale et une omission optimale des composants.

Évitez formatRange() lorsque vous devez afficher plusieurs dates non liées. Une liste d'échéances comme « 1er janvier, 15 janvier, 1er février » doit utiliser des appels format() réguliers 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 à quel point 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.