Travail avancé avec les dates et l'heure en TypeScript : du typage aux opérations complexes
\n\nTravailler avec les dates et l'heure est l'une des tâches les plus fréquentes et en même temps les plus délicates en développement web. À un niveau avancé, la simple utilisation de new Date() ne suffit plus. TypeScript offre un système de types puissant, et les bibliothèques modernes (Luxon, Date-fns) permettent d'écrire un code sûr, prévisible et performant. Dans cet article, nous aborderons les techniques avancées : typage strict, gestion des fuseaux horaires, formatage et analyse complexes, ainsi que la création d'utilitaires personnalisés.
1. Typage strict des dates : types personnalisés et Branded Types
\n\nLe type Date en TypeScript ne protège pas contre la transmission d'une valeur incorrecte. Par exemple, une fonction qui attend une date de naissance peut recevoir un horodatage actuel. La solution consiste à utiliser les Branded Types (types marqués) ou le Nominal Typing.
// Branded Type pour la date de naissance\ntype BirthDate = Date & { __brand: 'BirthDate' };\n\nfunction createBirthDate(date: Date): BirthDate {\n // Validation : la date ne doit pas être dans le futur\n if (date.getTime() > Date.now()) {\n throw new Error('La date de naissance ne peut pas être dans le futur');\n }\n return date as BirthDate;\n}\n\nfunction calculateAge(birthDate: BirthDate): number {\n const diff = Date.now() - birthDate.getTime();\n return Math.floor(diff / (1000 * 60 * 60 * 24 * 365.25));\n}\n\n// Utilisation\nconst rawDate = new Date('1990-05-15');\nconst birth = createBirthDate(rawDate);\nconsole.log(calculateAge(birth)); // OK\n\n// Erreur : Argument of type 'Date' is not assignable to parameter of type 'BirthDate'\n// console.log(calculateAge(new Date())); \n\n\nCette approche garantit que seule une date de naissance validée sera transmise à la fonction calculateAge. Pour des scénarios plus complexes, vous pouvez créer des types pour les horodatages (UnixTimestamp), les intervalles (TimeInterval), etc.
2. Formatage et analyse avancés avec Intl.DateTimeFormat
\n\nL'objet natif Intl.DateTimeFormat permet de formater les dates de manière flexible sans bibliothèques externes. À un niveau avancé, nous pouvons créer des formateurs réutilisables et gérer des locales complexes.
// Création d'un formateur avec options\nconst formatter = new Intl.DateTimeFormat('fr-FR', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n weekday: 'long',\n hour: '2-digit',\n minute: '2-digit',\n timeZone: 'Europe/Paris',\n});\n\n// Formatage\nconsole.log(formatter.format(new Date())); \n// "vendredi 25 octobre 2024, 14:30"\n\n// Analyse (utilitaire personnalisé)\nfunction parseLocalizedDate(dateStr: string, locale: string): Date | null {\n const parts = dateStr.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (!parts) return null;\n \n const [, year, month, day] = parts;\n return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));\n}\n\n\nConseil : Pour des performances optimales, créez les formateurs une fois et réutilisez-les. Évitez de créer une nouvelle instance à chaque appel.
\n\n3. Gestion des fuseaux horaires avec la bibliothèque Luxon
\n\nL'objet natif Date gère mal les fuseaux horaires. Luxon est une bibliothèque moderne du créateur de Moment.js, qui s'intègre parfaitement avec TypeScript.
import { DateTime, Settings } from 'luxon';\n\n// Définition du fuseau horaire par défaut\nSettings.defaultZone = 'Europe/Paris';\n\n// Création d'une date avec indication explicite du fuseau\nconst meeting = DateTime.fromISO('2024-11-01T15:00:00', { zone: 'America/New_York' });\nconsole.log(meeting.toISO()); // 2024-11-01T15:00:00.000-04:00\n\n// Conversion vers un autre fuseau\nconst parisTime = meeting.setZone('Europe/Paris');\nconsole.log(parisTime.toFormat('dd.MM.yyyy HH:mm')); // 01.11.2024 21:00\n\n// Travail avec les intervalles\nconst start = DateTi