Temps de lecture : 4 min.

La manipulation native des dates en Javascript n’a jamais été chose facile. Tout cela a changé avec l’arrivé de Moment.js vers 2010 qui a été la première librairie populaire permettant la manipulation simple des dates.

Cependant, l’écosystème Javascript à fortement évolué. avec les optimisations (webpack, tree shaking, typescript), et l’équipe a annoncé la fin de l’évolution du projet Moment.js au profit d’alternative plus optimisé.

Avec l’arrêt de l’évolution de moment.js, cet article à pour but de lister différentes façon de manipuler les dates en 2021.

L’objet Date n’est pas pratique à utiliser

Depuis ses débuts, manipuler les dates Javascript est contraignant. L’objet Date comporte de nombreux défauts, listé dans cette article:

https://maggiepint.com/2017/04/09/fixing-javascript-date-getting-started/

  • Pas de support des timezone autre que le local time et UTC, 
  • Le comportement du parser de date instable, 
  • Les objets date sont mutable, 
  • La mauvaise gestion des heures d’hiver et d’été, 
  • Computation APIs are unwieldy
  • Pas de support du calendrier grégorien

Ajoutons à cela la manipulation des dates qui n’est pas natif:

  • additioner/soustraire des unités de temps (date + 3 mois)
  • comparer des dates
  • formater des distances (il y a un 3 heures…)

En 2011, Moment.js a contribué à faciliter la manipulation des dates, en fournissant une API complète et une documentation claire couvrant de nombreux cas. Elle a aussi permis à utiliser les timezone.

Moment.js a été construit sur l’écosystème web d’il y a dix ans. Le monde Javascript évoluant très vite (webpack, structures immuables, tree shaking), ainsin que l’émergence de nouvelle librairie utilisant les optimisations actuelle, Moment.js a été déclaré par ses contributeurs comme un projet legacy et n’est donc plus conseillé pour de nouveau projet.

Des comparatifs sont disponible pour permettre de choisir quelles libraries date utiliser selon ses préférences:

https://github.com/you-dont-need/You-Dont-Need-Momentjs

Nous allons donc voir quelques façons de manipuler les dates en Javascript en 2021.

Date-fns

Date-fns, créé en 2014, propose un ensemble de fonctions (plus de 180 fonctions), a la particularité de manipuler l’objet Date, d’utiliser le paradigme fonctionnel, de supporter le tree-shaking, d’être immutable par nature, et d’être un projet bien maintenu avec de nombreuse étoile github.

https://bestofjs.org/projects/date-fns

Le tree-shaking permet de supprimer les fonctions inutiles à la compilation du projet. Si on n’utilise que quelques fonctions, tout le reste est supprimé, permettant de maîtriser le poid du projet (à la différence de moment.js où le tree shaking n’est pas possible car toutes les fonctions sont groupé dans une instance de l’objet Moment)

La doc date-fns est complète avec de nombreux exemple 

https://date-fns.org/docs/Getting-Started

Voici quelque exemple de manipulation de date avec date-fns

Parsing date
parseISO('2020-06-11T09:30:30')
//=> Thu Jun 11 2020 09:30:30

Format date
// Code example
import { format, formatDistance, formatRelative, subDays } from 'date-fns'

format(new Date(2020, 6, 11), 'MM/dd/yyyy')
//=> '11/07/2014'

formatDistance(subDays(new Date(), 3), new Date())
//=> "3 days ago"

formatRelative(subDays(new Date(), 3), new Date())
//=> "last Friday at 7:26 p.m."

formatISO(new Date(2019, 8, 18, 19, 0, 52))
//=> '2019-09-18T19:00:52Z'

// Represent 18 September 2019 in ISO 8601 format, date only:
formatISO(new Date(2019, 8, 18, 19, 0, 52), { representation: 'date' })
//=> '2019-09-18'

// Represent 18 September 2019 in ISO 8601 format, time only (UTC):
formatISO(new Date(2019, 8, 18, 19, 0, 52), { representation: 'time' })
//=> '19:00:52Z'

Manipulate date
// Code example
addDays(new Date(2014, 8, 1), 10)
//=> Thu Sep 11 2014 00:00:00

const result = addBusinessDays(new Date(2014, 8, 1), 10)
//=> Mon Sep 15 2014 00:00:00 (skipped weekend days)


Date-fns also contains tons of conditional helpers well named (isFuture, isSunday, isSameWeek)
// Code example
isBefore(new Date(1989, 6, 10), new Date(1987, 1, 11))
//=> false

isSunday(new Date(2014, 8, 21))
//=> true

var result = isSameWeek(new Date(2014, 7, 31), new Date(2014, 8, 4))
//=> true

isFuture(new Date(2014, 11, 31))
//=> false

Manipuler les dates sans dépendances externes

Si on ne souhaite pas utiliser de librairie externe, on peut définir son propre fichier utilitaire de date.

Le projet 30 second of code propose des snippets de fonctions communes. Sans devoir réinventer la logique, on peut consulter ce site et copier les fonctions intéressante afin de les intégrer dans projet Javascript. https://www.30secondsofcode.org/js/t/date/p/1

Source: https://www.30secondsofcode.org/js/s/format-duration
const formatDuration = ms => {
  if (ms < 0) ms = -ms;
  const time = {
    day: Math.floor(ms / 86400000),
    hour: Math.floor(ms / 3600000) % 24,
    minute: Math.floor(ms / 60000) % 60,
    second: Math.floor(ms / 1000) % 60,
    millisecond: Math.floor(ms) % 1000
  };
  return Object.entries(time)
    .filter(val => val[1] !== 0)
    .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)
    .join(', ');
};

// EXAMPLES
formatDuration(1001); // '1 second, 1 millisecond'
formatDuration(34325055574);
// '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'


Source: https://www.30secondsofcode.org/js/s/is-between-dates
const isBetweenDates = (dateStart, dateEnd, date) =>
  date > dateStart && date < dateEnd;

// EXAMPLES
isBetweenDates(
  new Date(2010, 11, 20),
  new Date(2010, 11, 30),
  new Date(2010, 11, 19)
); // false
isBetweenDates(
  new Date(2010, 11, 20),
  new Date(2010, 11, 30),
  new Date(2010, 11, 25)
); // true

Temporal, la future solution natif

Afin de moderniser la manipulation natives des dates, l’équipe de TC39 formalise une nouvelle spécification, sous le nom de Temporal, qui sera intégré nativement dans les moteurs Javascript, sans librairie externe.

https://tc39.es/proposal-temporal/docs/index.html

// On peut sélectionner la date, l'heure ou les deux
 const date = Temporal.now.plainDateISO().toString();
 => "2020-04-25"
 const time = Temporal.now.plainTimeISO().toString();
 => "12:52:48.389968386"
 const dateTime = Temporal.now.plainDateTimeISO().toString();
 => "2020-04-25T12:52:48.389968386"

Temporal sera un objet global, comme l’objet Math. Comme pour Moment et Date fns, on va retrouver de nombreuses fonctions de création, manipulation et formattage de date.

De plus, Temporal cherche à combler les défauts de Date, en particulier en donnant plus de flexibilité sur la manipulation des timezone. Pour cela, il existe plusieurs type pour représenter des dates: sans zone (PlainDate, PlainTime, PlainDateTime…) et avec zone (Instant, ZonedDateTime).

La convertion entre les deux systèmes de date doit se faire avec attention, l’API Temporal proposant différentes manière pour convertir d’une date sans zone vers une date avec zone.

// Représente une dateTime sans zone
 const dateTime = Temporal.PlainDateTime.from({
   year: 2020,
   month: 04,
   day: 25,
   hour: 12
 });
 => 2020-04-25T12:00:00
 // Représente une dateTime avec une zone
 const zonedDateTime = Temporal.ZonedDateTime.from({
   // IANA Zone 
   // https://www.iana.org/time-zones
   timeZone: 'America/Los_Angeles',
   year: 2020,
   month: 04,
   day: 25,
   hour: 12
 });
 => 2020-04-25T12:00:00-07:00[America/Los_Angeles]
 // Représente un Instant zoné dans le temps
 Temporal.Instant.from("2020-04-25T12:52:48Z")
 => 2020-04-25T12:52:48Z
 // Convertir une date sans zone à une date zoné
 dateTime.toZonedDateTime('Asia/Seoul')
 => 2020-04-25T12:00:00+09:00[Asia/Seoul]
 // Convertir une date zoné vers une date sans zone
 Temporal.PlainDateTime.from(zonedDateTime)
 => 2020-04-25T12:00:00

A ce jour (Mai 2021), Temporal est en Stage 3, en cours d’implémentation dans les différents moteurs Javascript (Navigateur web, nodejs…)

De nombreux autres exemples sont disponible sur le cookbook (sur Chrome ou Edge, vous pouvez ouvrir Dev tools et utiliser l’objet Temporal directement depuis la console)

https://tc39.es/proposal-temporal/docs/cookbook.html

Conclusion

Moment.js est maintenant considéré comme obsolète pour les nouveaux projets. En remplacement, on peut utiliser date-fns pour le côté fonctionnel, et il existe d’autre librairie (luxon, dayjs) selon le style de programmation préféré. Il est également possible d’utiliser des sites de snippets pour copier les fonctions de manipulation de date sans dépendre d’une librairie externe. Mais tout cela sera rendu obsolète avec l’arrivé de Temporal proposé par TC39, actuellement en stage 3. Temporal sera donc nativement supporté par les moteurs Javascript (navigateur et node.js)