Le guide définitif de la manipulation DateTime

Publié: 2022-03-11

En tant que développeur de logiciels, vous ne pouvez pas fuir la manipulation des dates. Presque toutes les applications créées par un développeur auront un composant dans lequel la date et l'heure doivent être obtenues auprès de l'utilisateur, stockées dans une base de données et affichées à l'utilisateur.

Demandez à n'importe quel programmeur quelle est son expérience dans la gestion des dates et des fuseaux horaires et il partagera probablement des histoires de guerre. La gestion des champs de date et d'heure n'est certainement pas sorcier, mais peut souvent être fastidieuse et source d'erreurs.

Il existe des centaines d'articles sur le sujet, cependant, la plupart sont soit trop académiques, se concentrant sur des détails de fond, soit ils sont trop inégaux, fournissant de courts extraits de code sans beaucoup d'explications qui les accompagnent. Ce guide détaillé sur la manipulation DateTime devrait vous aider à comprendre les concepts de programmation et les meilleures pratiques relatives à l'heure et à la date sans avoir à parcourir une mer d'informations sur ce sujet.

Dans cet article, je vais vous aider à réfléchir clairement aux champs de date et d'heure et vous suggérer quelques bonnes pratiques qui peuvent vous aider à éviter l'enfer de la date et de l'heure. Ici, nous allons explorer certains des concepts clés nécessaires pour manipuler correctement les valeurs de date et d'heure, les formats pratiques pour stocker les valeurs DateTime et les transférer via des API, et plus encore.

Pour commencer, la bonne réponse pour le code de production est presque toujours d'utiliser une bibliothèque appropriée plutôt que de lancer la vôtre. Les difficultés potentielles avec le calcul DateTime discutées dans cet article ne sont que la pointe de l'iceberg, mais elles sont toujours utiles à connaître, avec ou sans bibliothèque.

Les bibliothèques DateTime aident si vous les comprenez correctement

Les bibliothèques de dates aident à bien des égards à vous faciliter la vie. Ils simplifient considérablement l'analyse de la date, les opérations arithmétiques et logiques de la date et le formatage de la date. Vous pouvez trouver une bibliothèque de dates fiable pour le front-end et le back-end pour faire le gros du travail à votre place.

Cependant, nous utilisons souvent des bibliothèques de dates sans penser au fonctionnement réel de la date/heure. La date/heure est un concept complexe. Les bogues qui surviennent en raison de sa mauvaise compréhension peuvent être extrêmement difficiles à comprendre et à corriger, même avec l'aide de bibliothèques de données. En tant que programmeur, vous devez comprendre les bases et être capable d'apprécier les problèmes que les bibliothèques de données résolvent pour en tirer le meilleur parti.

De plus, les bibliothèques de date/heure ne peuvent vous emmener que très loin. Toutes les bibliothèques de dates fonctionnent en vous donnant accès à des structures de données pratiques pour représenter un DateTime. Si vous envoyez et recevez des données via une API REST, vous devrez éventuellement convertir la date en chaîne et vice versa car JSON n'a pas de structure de données native pour représenter DateTime. Les concepts que j'ai décrits ici vous aideront à éviter certains des problèmes courants qui pourraient survenir lors de ces transformations date-chaîne et chaîne-date.

Remarque : même si j'ai utilisé JavaScript comme langage de programmation abordé dans cet article, ce sont des concepts généraux qui s'appliquent, dans une large mesure, à pratiquement tous les langages de programmation et leurs bibliothèques de dates. Donc, même si vous n'avez jamais écrit une ligne de JavaScript auparavant, n'hésitez pas à continuer à lire car je suppose à peine une connaissance préalable de JavaScript dans l'article.

Standardiser le temps

Un DateTime est un point très précis dans le temps. Réfléchissons à cela. Alors que je griffonne cet article, l'horloge de mon ordinateur portable indique le 21 juillet à 13h29. C'est ce que nous appelons «l'heure locale», l'heure que je vois sur les horloges murales autour de moi et sur ma montre-bracelet.

Donnez ou prenez quelques minutes, si je demande à mon amie de me rencontrer dans un café voisin à 15h00, je peux m'attendre à la voir là-bas à peu près à cette heure-là. De même, il n'y aurait pas de confusion si à la place je disais, par exemple, "rencontrons-nous dans une heure et demie". Nous parlons régulièrement du temps de cette façon avec des personnes vivant dans la même ville ou le même fuseau horaire.

Pensons à un scénario différent : je veux dire à un ami vivant à Uppsala, en Suède, que je veux lui parler à 17 h 00. Je lui envoie un message : « Hey Anton, parlons à 17 heures. J'obtiens immédiatement la réponse, "Votre temps ou mon temps?"

Anton me dit qu'il vit dans le fuseau horaire d'Europe centrale qui est UTC+01:00. J'habite en UTC+05:45. Cela signifie que lorsqu'il est 17h00 là où j'habite, il est 17h00 - 05h45 = 11h15 UTC, ce qui se traduit par 11h15 UTC + 01h00 = 12h15 à Uppsala, parfait pour les deux de nous.

Soyez également conscient de la différence entre le fuseau horaire (heure d'Europe centrale) et le décalage horaire (UTC+05:45). Les pays peuvent également décider de modifier leurs décalages de fuseau horaire pour l'heure d'été pour des raisons politiques. Presque chaque année, les règles sont modifiées dans au moins un pays, ce qui signifie que tout code contenant ces règles doit être mis à jour. Cela vaut la peine de considérer de quoi dépend votre base de code pour chaque niveau de votre application.

C'est une autre bonne raison pour laquelle nous recommandons que seul le frontal traite les fuseaux horaires dans la plupart des cas. Si ce n'est pas le cas, que se passe-t-il lorsque les règles utilisées par votre moteur de base de données ne correspondent pas à celles de votre front-end ou de votre back-end ?

Ce problème de gestion de deux versions différentes de l'heure, par rapport à l'utilisateur et par rapport à une norme universellement acceptée, est difficile, d'autant plus dans le monde de la programmation où la précision est essentielle et où même une seconde peut faire une énorme différence. La première étape vers la résolution de ces problèmes consiste à stocker DateTime en UTC.

Normaliser le format

La normalisation de l'heure est merveilleuse car je n'ai besoin que de stocker l'heure UTC et tant que je connais le fuseau horaire de l'utilisateur, je peux toujours convertir son heure. Inversement, si je connais l'heure locale d'un utilisateur et son fuseau horaire, je peux le convertir en UTC.

Mais les dates et les heures peuvent être spécifiées dans de nombreux formats différents. Pour la date, vous pouvez écrire « 30 juillet » ou « 30 juillet » ou « 7/30 » (ou 30/7, selon l'endroit où vous vivez). Pour l'heure, vous pourriez écrire "21h30" ou "21h30".

Des scientifiques du monde entier se sont réunis pour résoudre ce problème et ont décidé d'un format pour décrire le temps que les programmeurs aiment vraiment parce qu'il est court et précis. Nous aimons l'appeler "format de date ISO", qui est une version simplifiée du format étendu ISO-8601 et il ressemble à ceci :

Une image montrant une version simplifiée du format étendu ISO-8601 appelé format de date ISO.

Pour 00:00 ou UTC, nous utilisons "Z" à la place, ce qui signifie l'heure zoulou, un autre nom pour UTC.

Manipulation de date et arithmétique en JavaScript

Avant de commencer avec les meilleures pratiques, nous allons en apprendre davantage sur la manipulation de dates à l'aide de JavaScript pour comprendre la syntaxe et les concepts généraux. Bien que nous utilisions JavaScript, vous pouvez facilement adapter ces informations à votre langage de programmation préféré.

Nous utiliserons l'arithmétique des dates pour résoudre les problèmes courants liés aux dates que la plupart des développeurs rencontrent.

Mon objectif est de vous mettre à l'aise pour créer un objet date à partir d'une chaîne et en extraire des composants. C'est quelque chose qu'une bibliothèque de dates peut vous aider, mais il est toujours préférable de comprendre comment cela se fait dans les coulisses.

Une fois que nous nous sommes sali les mains avec la date/l'heure, il est alors plus facile de réfléchir aux problèmes auxquels nous sommes confrontés, d'extraire les meilleures pratiques et d'aller de l'avant. Si vous souhaitez passer aux meilleures pratiques, n'hésitez pas à le faire, mais je vous recommande fortement de parcourir au moins la section arithmétique des dates ci-dessous.

L'objet de date JavaScript

Les langages de programmation contiennent des constructions utiles pour nous faciliter la vie. L'objet JavaScript Date est l'une de ces choses. Il propose des méthodes pratiques pour obtenir la date et l'heure actuelles, stocker une date dans une variable, effectuer une arithmétique de date et formater la date en fonction des paramètres régionaux de l'utilisateur.

En raison des différences entre les implémentations de navigateur et de la gestion incorrecte de l'heure d'été (DST), il n'est pas recommandé de dépendre de l'objet Date pour les applications critiques et vous devriez probablement utiliser une bibliothèque DateTime comme Luxon, date-fns ou dayjs. (Quoi que vous utilisiez, évitez le Moment.js autrefois populaire, souvent simplement appelé moment , tel qu'il apparaît dans le code, car il est désormais obsolète.)

Mais à des fins pédagogiques, nous utiliserons les méthodes fournies par l'objet Date() pour apprendre comment JavaScript gère DateTime.

Obtenir la date actuelle

 const currentDate = new Date();

Si vous ne transmettez rien au constructeur Date, l'objet date renvoyé contient la date et l'heure actuelles.

Vous pouvez ensuite le formater pour extraire uniquement la partie date comme suit :

 const currentDate = new Date(); const currentDayOfMonth = currentDate.getDate(); const currentMonth = currentDate.getMonth(); // Be careful! January is 0, not 1 const currentYear = currentDate.getFullYear(); const dateString = currentDayOfMonth + "-" + (currentMonth + 1) + "-" + currentYear; // "27-11-2020"

Remarque : L'écueil "Janvier est 0" est courant mais pas universel. Il vaut la peine de revérifier la documentation de n'importe quel langage (ou format de configuration : par exemple, cron est notamment basé sur 1) avant de commencer à l'utiliser.

Obtenir l'horodatage actuel

Si vous souhaitez plutôt obtenir l'horodatage actuel, vous pouvez créer un nouvel objet Date et utiliser la méthode getTime().

 const currentDate = new Date(); const timestamp = currentDate.getTime();

En JavaScript, un horodatage est le nombre de millisecondes qui se sont écoulées depuis le 1er janvier 1970.

Si vous n'avez pas l'intention de prendre en charge <IE8, vous pouvez utiliser Date.now() pour obtenir directement l'horodatage sans avoir à créer un nouvel objet Date.

Analyser une date

La conversion d'une chaîne en un objet de date JavaScript se fait de différentes manières.

Le constructeur de l'objet Date accepte une grande variété de formats de date :

 const date1 = new Date("Wed, 27 July 2016 13:30:00"); const date2 = new Date("Wed, 27 July 2016 07:45:00 UTC"); const date3 = new Date("27 July 2016 13:30:00 UTC+05:45");

Notez que vous n'avez pas besoin d'inclure le jour de la semaine car JS peut déterminer le jour de la semaine pour n'importe quelle date.

Vous pouvez également passer l'année, le mois, le jour, les heures, les minutes et les secondes comme arguments distincts :

 const date = new Date(2016, 6, 27, 13, 30, 0);

Bien sûr, vous pouvez toujours utiliser le format de date ISO :

 const date = new Date("2016-07-27T07:45:00Z");

Cependant, vous pouvez rencontrer des problèmes si vous ne fournissez pas explicitement le fuseau horaire !

 const date1 = new Date("25 July 2016"); const date2 = new Date("July 25, 2016");

L'un ou l'autre vous donnera le 25 juillet 2016 à 00:00:00 heure locale.

Si vous utilisez le format ISO, même si vous ne donnez que la date et non l'heure et le fuseau horaire, il acceptera automatiquement le fuseau horaire en UTC.

Cela signifie que:

 new Date("25 July 2016").getTime() !== new Date("2016-07-25").getTime() new Date("2016-07-25").getTime() === new Date("2016-07-25T00:00:00Z").getTime()

Formater une date

Heureusement, JavaScript moderne possède des fonctions d'internationalisation pratiques intégrées dans l'espace de noms Intl standard qui font du formatage de la date une opération simple.

Pour cela, nous aurons besoin de deux objets : un Date et un Intl.DateTimeFormat , initialisés avec nos préférences de sortie. Supposons que nous souhaitions utiliser le format américain (M/D/YYYY), cela ressemblerait à :

 const firstValentineOfTheDecade = new Date(2020, 1, 14); // 1 for February const enUSFormatter = new Intl.DateTimeFormat('en-US'); console.log(enUSFormatter.format(firstValentineOfTheDecade)); // 2/14/2020

Si à la place nous voulions le format néerlandais (D/M/YYYY), nous passerions simplement un code de culture différent au constructeur DateTimeFormat :

 const nlBEFormatter = new Intl.DateTimeFormat('nl-BE'); console.log(nlBEFormatter.format(firstValentineOfTheDecade)); // 14/2/2020

Ou une forme plus longue du format américain, avec le nom du mois en toutes lettres :

 const longEnUSFormatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); console.log(longEnUSFormatter.format(firstValentineOfTheDecade)); // February 14, 2020

Maintenant, si nous voulions un format ordinal approprié pour le jour du mois, c'est-à-dire « 14 » au lieu de simplement « 14 », cela nécessite malheureusement une solution de contournement, car les seules valeurs valides de day à ce jour sont "numeric" ou "2-digit" . En empruntant la version de Flavio Copes du code de Mathias Bynens pour tirer parti d'une autre partie d' Intl pour cela, nous pouvons personnaliser la sortie du jour du mois via formatToParts() :

 const pluralRules = new Intl.PluralRules('en-US', { type: 'ordinal' }) const suffixes = { 'one': 'st', 'two': 'nd', 'few': 'rd', 'other': 'th' } const convertToOrdinal = (number) => `${number}${suffixes[pluralRules.select(number)]}` // At this point: // convertToOrdinal("1") === "1st" // convertToOrdinal("2") === "2nd" // etc. const extractValueAndCustomizeDayOfMonth = (part) => { if (part.type === "day") { return convertToOrdinal(part.value); } return part.value; }; console.log( longEnUSFormatter.formatToParts(firstValentineOfTheDecade) .map(extractValueAndCustomizeDayOfMonth) .join("") ); // February 14th, 2020

Malheureusement, formatToParts n'est pas du tout pris en charge par Internet Explorer (IE) au moment de la rédaction de cet article, mais toutes les autres technologies de bureau, mobiles et back-end (c'est-à-dire Node.js) sont prises en charge. Pour ceux qui ont besoin de prendre en charge IE et qui ont absolument besoin d'ordinaux, la note ci-dessous (ou mieux, une bibliothèque de dates appropriée) fournit une réponse.

Si vous devez prendre en charge des navigateurs plus anciens comme IE avant la version 11, le formatage de la date en JavaScript est plus difficile car il n'y avait pas de fonctions de formatage de date standard comme strftime en Python ou PHP.

En PHP par exemple, la fonction strftime("Today is %b %d %Y %X", mktime(5,10,0,12,30,99)) vous donne Today is Dec 30 1999 05:10:00 .

Vous pouvez utiliser une combinaison différente de lettres précédées de % pour obtenir la date dans différents formats. (Attention, toutes les langues n'attribuent pas la même signification à chaque lettre, en particulier, « M » et « m » peuvent être échangés pendant des minutes et des mois.)

Si vous êtes sûr du format que vous souhaitez utiliser, il est préférable d'extraire des bits individuels à l'aide des fonctions JavaScript décrites ci-dessus et de créer une chaîne vous-même.

 var currentDate = new Date (); var date = currentDate.getDate(); var month = currentDate.getMonth(); var year = currentDate.getFullYear();

Nous pouvons obtenir la date au format MM/JJ/AAAA comme

 var monthDateYear = (month+ 1 ) + "/" + date + "/" + year;

Le problème avec cette solution est qu'elle peut donner une longueur incohérente aux dates car certains mois et jours du mois sont à un chiffre et d'autres à deux chiffres. Cela peut être problématique, par exemple, si vous affichez la date dans une colonne de tableau, car les dates ne s'alignent pas.

Nous pouvons résoudre ce problème en utilisant une fonction "pad" qui ajoute un 0 devant.

 function pad ( n ) { return n< 10 ? '0' +n : n; }

Maintenant, nous obtenons la date correcte au format MM/JJ/AAAA en utilisant :

 var mmddyyyy = pad(month + 1 ) + "/" + pad(date) + "/" + year;

Si nous voulons plutôt JJ-MM-AAAA, le processus est similaire :

 var ddmmyyyy = pad(date) + "-" + pad(month + 1 ) + "-" + year;

Montons la mise et essayons d'imprimer la date au format "Mois Date, Année". Nous aurons besoin d'un mappage des index de mois aux noms :

 var monthNames = [ "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" ]; var dateWithFullMonthName = monthNames[month] + " " + pad(date) + ", " + year;

Certaines personnes aiment afficher la date du 1er janvier 2013. Pas de problème, tout ce dont nous avons besoin est un ordinal de fonction d'assistance qui renvoie 1er pour 1, 12e pour 12 et 103e pour 103, etc., et le reste est simple :

 var ordinalDate = ordinal(date) + " " + monthNames[month] + ", " + year;

Il est facile de déterminer le jour de la semaine à partir de l'objet date, ajoutons donc cela dans :

 var daysOfWeek = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ]; ordinalDateWithDayOfWeek = daysOfWeek[currentDate.getDay()] + ", " + ordinalDate;

Le plus gros point ici est qu'une fois que vous avez extrait les nombres de la date, le formatage est principalement lié aux chaînes.

Modification du format de date

Une fois que vous savez comment analyser une date et la formater, changer une date d'un format à un autre consiste simplement à combiner les deux.

Par exemple, si vous avez une date au format 21 juillet 2013 et que vous souhaitez changer le format en 21-07-2013, cela peut être réalisé comme ceci :

 const myDate = new Date("Jul 21, 2013"); const dayOfMonth = myDate.getDate(); const month = myDate.getMonth(); const year = myDate.getFullYear(); function pad(n) { return n<10 ? '0'+n : n } const ddmmyyyy = pad(dayOfMonth) + "-" + pad(month + 1) + "-" + year; // "21-07-2013"

Utilisation des fonctions de localisation de l'objet de date JavaScript

Les méthodes de formatage de date dont nous avons discuté ci-dessus devraient fonctionner dans la plupart des applications, mais si vous voulez vraiment localiser le formatage de la date, je vous suggère d'utiliser la méthode toLocaleDateString() de l'objet Date :

 const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', });

… nous donne quelque chose comme 26 Jul 2016 .

Changer les paramètres régionaux en "en-US" donne "Jul 26, 2016" à la place. Remarquez comment la mise en forme a changé, mais les options d'affichage sont restées les mêmes, une fonctionnalité très utile. Comme indiqué dans la section précédente, la nouvelle technique basée sur Intl.DateTimeFormat fonctionne de manière très similaire, mais vous permet de réutiliser un objet formateur afin que vous n'ayez à définir les options qu'une seule fois.

Avec toLocaleDateString() , c'est une bonne habitude de toujours passer les options de formatage, même si la sortie semble correcte sur votre ordinateur. Cela peut empêcher l'interface utilisateur de se casser dans des paramètres régionaux inattendus avec des noms de mois très longs ou d'avoir l'air gênant à cause de noms courts.

Si je voulais plutôt le mois complet "juillet", tout ce que je fais est de changer le paramètre du mois dans les options en "long". JavaScript gère tout pour moi. Pour en-US, j'obtiens maintenant le 26 juillet 2016.

Remarque : Si vous souhaitez que le navigateur utilise automatiquement les paramètres régionaux de l'utilisateur, vous pouvez passer "undefined" comme premier paramètre.

Si vous souhaitez afficher la version numérique de la date et que vous ne voulez pas vous embêter avec MM/DD/YYYY vs. DD/MM/YYYY pour différents paramètres régionaux, je suggère la solution simple suivante :

 const today = new Date().toLocaleDateString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', });

Sur mon ordinateur, cela 7/26/2016 . Si vous voulez vous assurer que le mois et la date comportent deux chiffres, modifiez simplement les options :

 const today = new Date().toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', });

Cela 07/26/2016 . Juste ce que nous voulions!

Vous pouvez également utiliser d'autres fonctions connexes pour localiser l'affichage de l'heure et de la date :

Code Sortir La description
 now.toLocaleTimeString()
"04:21:38" Afficher la version localisée de l'heure uniquement
 now.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', });
"04:21:38" Afficher l'heure localisée en fonction des options fournies
 now.toLocaleString()
"22/07/2016, 04:21:38" Afficher la date et l'heure pour les paramètres régionaux de l'utilisateur
 now.toLocaleString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', });
"22/07/2016, 04:21" Afficher la date et l'heure localisées en fonction des options fournies

Calcul des dates et heures relatives

Voici un exemple d'ajout de 20 jours à une date JavaScript (c'est-à-dire, déterminer la date 20 jours après une date connue) :

 const myDate = new Date("July 20, 2016 15:00:00"); const nextDayOfMonth = myDate.getDate() + 20; myDate.setDate(nextDayOfMonth); const newDate = myDate.toLocaleString();

L'objet date d'origine représente désormais une date 20 jours après le 20 juillet et newDate contient une chaîne localisée représentant cette date. Sur mon navigateur, newDate contient "09/08/2016, 15:00:00".

Pour calculer des horodatages relatifs avec une différence plus précise que des jours entiers, vous pouvez utiliser Date.getTime() et Date.setTime() pour travailler avec des nombres entiers représentant le nombre de millisecondes depuis une certaine époque, à savoir le 1er janvier 1970. Pour exemple, si vous voulez savoir quand il est 17 heures après maintenant :

 const msSinceEpoch = (new Date()).getTime(); const seventeenHoursLater = new Date(msSinceEpoch + 17 * 60 * 60 * 1000);

Comparer des dates

Comme pour tout ce qui concerne la date, la comparaison des dates a ses propres pièges.

Tout d'abord, nous devons créer des objets de date. Heureusement, <, >, <= et >= fonctionnent tous. Donc, comparer le 19 juillet 2014 et le 18 juillet 2014 est aussi simple que :

 const date1 = new Date("July 19, 2014"); const date2 = new Date("July 28, 2014"); if(date1 > date2) { console.log("First date is more recent"); } else { console.log("Second date is more recent"); }

La vérification de l'égalité est plus délicate, car deux objets date représentant la même date sont toujours deux objets date différents et ne seront pas égaux. Comparer les chaînes de date est une mauvaise idée car, par exemple, "20 juillet 2014" et "20 juillet 2014" représentent la même date mais ont des représentations de chaîne différentes. L'extrait ci-dessous illustre le premier point :

 const date1 = new Date("June 10, 2003"); const date2 = new Date(date1); const equalOrNot = date1 == date2 ? "equal" : "not equal"; console.log(equalOrNot);

Cela ne sortira not equal .

Ce cas particulier peut être résolu en comparant les équivalents entiers des dates (leurs horodatages) comme suit :

 date1.getTime() == date2.getTime()

J'ai vu cet exemple dans de nombreux endroits, mais je ne l'aime pas parce que vous ne créez généralement pas d'objet date à partir d'un autre objet date. Je pense donc que l'exemple n'est important que d'un point de vue académique. De plus, cela nécessite que les deux objets Date se réfèrent exactement à la même seconde, alors que vous voudrez peut-être seulement savoir s'ils se réfèrent au même jour, heure ou minute.

Prenons un exemple plus pratique. Vous essayez de comparer si l'anniversaire que l'utilisateur a entré est le même que la date chanceuse que vous obtenez d'une API.

 const userEnteredString = "12/20/1989"; // MM/DD/YYYY format const dateStringFromAPI = "1989-12-20T00:00:00Z"; const dateFromUserEnteredString = new Date(userEnteredString) const dateFromAPIString = new Date(dateStringFromAPI); if (dateFromUserEnteredString.getTime() == dateFromAPIString.getTime()) { transferOneMillionDollarsToUserAccount(); } else { doNothing(); }

Les deux représentaient la même date, mais malheureusement, votre utilisateur n'obtiendra pas le million de dollars.

Voici le problème : JavaScript suppose toujours que le fuseau horaire est celui que le navigateur lui fournit, sauf indication contraire explicite.

Cela signifie, pour moi, new Date ("12/20/1989") créera une date 1989-12-20T00:00:00+5:45 ou 1989-12-19T18:15:00Z qui n'est pas la même que 1989-12-20T00:00:00Z en termes d'horodatage.

Il n'est pas possible de changer uniquement le fuseau horaire d'un objet date existant, donc notre objectif est maintenant de créer un nouvel objet date mais avec UTC au lieu du fuseau horaire local.

Nous allons ignorer le fuseau horaire de l'utilisateur et utiliser UTC lors de la création de l'objet date. Il y a deux façons de le faire :

  1. Créez une chaîne de date au format ISO à partir de la date saisie par l'utilisateur et utilisez-la pour créer un objet Date. Utilisation d'un format de date ISO valide pour créer un objet Date tout en rendant très clair l'intention de UTC par rapport à local.
 const userEnteredDate = "12/20/1989"; const parts = userEnteredDate.split("/"); const userEnteredDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const userEnteredDateObj = new Date(userEnteredDateISO + "T00:00:00Z"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateObj.getTime() == dateFromAPI.getTime(); // true

Cela fonctionne également si vous ne spécifiez pas l'heure car celle-ci sera par défaut à minuit (c'est-à-dire 00:00:00Z):

 const userEnteredDate = new Date("1989-12-20"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true

N'oubliez pas : si le constructeur de date reçoit une chaîne au format de date ISO correct de AAAA-MM-JJ, il suppose automatiquement UTC.

  1. JavaScript fournit une fonction Date.UTC() intéressante que vous pouvez utiliser pour obtenir l'horodatage UTC d'une date. Nous extrayons les composants de la date et les transmettons à la fonction.
 const userEnteredDate = new Date("12/20/1989"); const userEnteredDateTimeStamp = Date.UTC(userEnteredDate.getFullYear(), userEnteredDate.getMonth(), userEnteredDate.getDate(), 0, 0, 0); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateTimeStamp == dateFromAPI.getTime(); // true ...

Trouver la différence entre deux dates

Un scénario courant que vous rencontrerez consiste à trouver la différence entre deux dates.

Nous abordons deux cas d'utilisation :

Trouver le nombre de jours entre deux dates

Convertissez les deux dates en horodatage UTC, trouvez la différence en millisecondes et trouvez les jours équivalents.

 const dateFromAPI = "2016-02-10T00:00:00Z"; const now = new Date(); const datefromAPITimeStamp = (new Date(dateFromAPI)).getTime(); const nowTimeStamp = now.getTime(); const microSecondsDiff = Math.abs(datefromAPITimeStamp - nowTimeStamp); // Math.round is used instead of Math.floor to account for certain DST cases // Number of milliseconds per day = // 24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second const daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60 * 24)); console.log(daysDiff);

Recherche de l'âge de l'utilisateur à partir de sa date de naissance

 const birthDateFromAPI = "12/10/1989";

Remarque : Nous avons un format non standard. Lisez la documentation de l'API pour déterminer si cela signifie 12 octobre ou 10 décembre. Passez au format ISO en conséquence.

 const parts = birthDateFromAPI.split("/"); const birthDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const birthDate = new Date(birthDateISO); const today = new Date(); let age = today.getFullYear() - birthDate.getFullYear(); if(today.getMonth() < birthDate.getMonth()) { age--; } if(today.getMonth() == birthDate.getMonth() && today.getDate() < birthDate.getDate()) { age--; }

Je sais qu'il existe des façons plus concises d'écrire ce code, mais j'aime l'écrire de cette façon en raison de la clarté de la logique.

Suggestions pour éviter l'enfer des rendez-vous

Maintenant que nous sommes à l'aise avec l'arithmétique des dates, nous sommes en mesure de comprendre les meilleures pratiques à suivre et les raisons de les suivre.

Obtenir DateTime de l'utilisateur

Si vous obtenez la date et l'heure de l'utilisateur, vous recherchez probablement son DateTime local. Nous avons vu dans la section arithmétique des dates que le constructeur Date peut accepter la date de différentes manières.

Pour éliminer toute confusion, je suggère toujours de créer une date en utilisant le new Date(year, month, day, hours, minutes, seconds, milliseconds) même si vous avez déjà la date dans un format analysable valide. Si tous les programmeurs de votre équipe suivent cette règle simple, il sera extrêmement facile de maintenir le code à long terme car il est aussi explicite que possible avec le constructeur Date .

La partie intéressante est que vous pouvez utiliser les variations qui vous permettent d'omettre l'un des quatre derniers paramètres s'ils sont nuls ; c'est-à-dire que new Date(2012, 10, 12) est identique à new Date(2012, 10, 12, 0, 0, 0, 0) car les paramètres non spécifiés sont par défaut à zéro.

Par exemple, si vous utilisez un sélecteur de date et d'heure qui vous donne la date 2012-10-12 et l'heure 12h30, vous pouvez extraire les parties et créer un nouvel objet Date comme suit :

 const dateFromPicker = "2012-10-12"; const timeFromPicker = "12:30"; const dateParts = dateFromPicker.split("-"); const timeParts = timeFromPicker.split(":"); const localDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2], timeParts[0], timeParts[1]);

Essayez d'éviter de créer une date à partir d'une chaîne à moins qu'elle ne soit au format de date ISO. Utilisez plutôt la méthode Date(année, mois, date, heures, minutes, secondes, microsecondes).

Obtenir uniquement la date

Si vous n'obtenez que la date, la date de naissance d'un utilisateur par exemple, il est préférable de convertir le format au format de date ISO valide pour éliminer toute information de fuseau horaire qui peut faire avancer ou reculer la date lors de la conversion en UTC. Par exemple:

 const dateFromPicker = "12/20/2012"; const dateParts = dateFromPicker.split("/"); const ISODate = dateParts[2] + "-" + dateParts[0] + "-" + dateParts[1]; const birthDate = new Date(ISODate).toISOString();

Au cas où vous l'auriez oublié, si vous créez un objet Date avec l'entrée dans un format de date ISO valide (AAAA-MM-JJ), il sera par défaut UTC au lieu d'utiliser par défaut le fuseau horaire du navigateur.

Stockage de la date

Stockez toujours le DateTime en UTC. Envoyez toujours une chaîne de date ISO ou un horodatage au back-end.

Des générations de programmeurs informatiques ont réalisé cette simple vérité après des expériences amères en essayant de montrer l'heure locale correcte à l'utilisateur. Stocker l'heure locale dans le back-end est une mauvaise idée, il est préférable de laisser le navigateur gérer la conversion en heure locale dans le front-end.

En outre, il devrait être évident que vous ne devez jamais envoyer une chaîne DateTime telle que "20 juillet 1989 12h10" au serveur principal. Même si vous envoyez également le fuseau horaire, vous augmentez l'effort des autres programmeurs pour comprendre vos intentions et analyser et stocker la date correctement.

Utilisez les toISOString() ou toJSON() de l'objet Date pour convertir le DateTime local en UTC.

 const dateFromUI = "12-13-2012"; const timeFromUI = "10:20"; const dateParts = dateFromUI.split("-"); const timeParts = timeFromUI.split(":"); const date = new Date(dateParts[2], dateParts[0]-1, dateParts[1], timeParts[0], timeParts[1]); const dateISO = date.toISOString(); $.post("http://example.com/", {date: dateISO}, ...)

Affichage de la date et de l'heure

  1. Obtenez l'horodatage ou la date au format ISO à partir d'une API REST.
  2. Créez un objet Date .
  3. Utilisez les toLocaleString() ou toLocaleDateString() et toLocaleTimeString() ou une bibliothèque de dates pour afficher l'heure locale.
 const dateFromAPI = "2016-01-02T12:30:00Z"; const localDate = new Date(dateFromAPI); const localDateString = localDate.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric', }); const localTimeString = localDate.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', });

Quand devez-vous également stocker l'heure locale ?

« Parfois, il est important de connaître le fuseau horaire dans lequel un événement s'est produit, et la conversion en un seul fuseau horaire efface irrévocablement cette information.

"Si vous faites une promotion marketing et que vous voulez savoir quels clients ont passé des commandes vers l'heure du déjeuner, une commande qui semble avoir été passée à midi GMT n'est pas très utile alors qu'elle a été passée au petit-déjeuner à New York."

Si vous rencontrez ce genre de situation, il serait plus judicieux de sauvegarder également l'heure locale. Comme d'habitude, nous aimerions créer la date au format ISO, mais nous devons d'abord trouver le décalage horaire.

La fonction getTimeZoneOffset() de l'objet Date nous indique le nombre de minutes qui, ajoutées à une heure locale donnée, donnent l'heure UTC équivalente. Je suggère de le convertir au format (+-)hh:mm car cela rend plus évident qu'il s'agit d'un décalage de fuseau horaire.

 const now = new Date(); const tz = now.gettime zoneOffset();

Pour mon fuseau horaire +05:45, j'obtiens -345, ce n'est pas seulement le signe opposé, mais un nombre comme -345 peut être complètement déroutant pour un développeur back-end. Nous convertissons donc cela en +05:45.

 const sign = tz > 0 ? "-" : "+"; const hours = pad(Math.floor(Math.abs(tz)/60)); const minutes = pad(Math.abs(tz)%60); const tzOffset = sign + hours + ":" + minutes;

Maintenant, nous obtenons le reste des valeurs et créons une chaîne ISO valide qui représente le DateTime local.

 const localDateTime = now.getFullYear() + "-" + pad(now.getMonth()+1) + "-" + pad(now.getDate()) + "T" + pad(now.getHours()) + ":" + pad(now.getMinutes()) + ":" + pad(now.getSeconds());

Si vous le souhaitez, vous pouvez envelopper les dates UTC et locales dans un objet.

 const eventDate = { utc: now.toISOString(), local: localDateTime, tzOffset: tzOffset, }

Maintenant, dans le backend, si vous voulez savoir si l'événement s'est produit avant midi, heure locale, vous pouvez analyser la date et simplement utiliser la fonction getHours() .

 const localDateString = eventDate.local; const localDate = new Date(localDateString); if(localDate.getHours() < 12) { console.log("Event happened before noon local time"); }

Nous n'avons pas utilisé le tzOffset ici, mais nous le stockons toujours car nous pourrions en avoir besoin à l'avenir à des fins de débogage. Vous pouvez en fait simplement envoyer le décalage du fuseau horaire et l'heure UTC uniquement. Mais j'aime aussi stocker l'heure locale car vous devrez éventuellement stocker la date dans une base de données et avoir l'heure locale stockée séparément vous permet d'interroger directement en fonction d'un champ plutôt que d'avoir à effectuer des calculs pour obtenir la date locale.

Parfois, même avec le fuseau horaire local enregistré, vous souhaiterez afficher les dates dans un fuseau horaire particulier. Par exemple, les heures des événements peuvent avoir plus de sens dans le fuseau horaire de l'utilisateur actuel s'ils sont virtuels, ou dans le fuseau horaire où ils auront physiquement lieu, s'ils ne le sont pas. Dans tous les cas, il vaut la peine d'examiner au préalable les solutions établies pour le formatage avec des noms de fuseaux horaires explicites.

Configuration du serveur et de la base de données

Configurez toujours vos serveurs et bases de données pour utiliser le fuseau horaire UTC. (Notez que UTC et GMT ne sont pas la même chose - GMT, par exemple, pourrait impliquer un passage à BST pendant l'été, alors que UTC ne le fera jamais.)

Nous avons déjà vu à quel point les conversions de fuseaux horaires peuvent être pénibles, surtout lorsqu'elles ne sont pas intentionnelles. Toujours envoyer UTC DateTime et configurer vos serveurs pour qu'ils soient dans le fuseau horaire UTC peuvent vous faciliter la vie. Votre code back-end sera beaucoup plus simple et plus propre car il n'aura pas à faire de conversions de fuseau horaire. DateTime data coming in from servers across the world can be compared and sorted effortlessly.

Code in the back end should be able to assume the time zone of the server to be UTC (but should still have a check in place to be sure). A simple configuration check saves having to think about and code for conversions every time new DateTime code is written.

It's Time for Better Date Handling

Date manipulation is a hard problem. The concepts behind the practical examples in this article apply beyond JavaScript, and are just the beginning when it comes to properly handling DateTime data and calculations. Plus, every helper library will come with its own set of nuances—which is even true of the eventual official standard support{target=”_blank”} for these types of operations.

The bottom line is: Use ISO on the back end, and leave the front end to format things properly for the user. Professional programmers will be aware of some of the nuances, and will (all the more decidedly) use well-supported DateTime libraries on both the back end and the front end. Built-in functions on the database side are another story, but hopefully this article gives enough background to make wiser decisions in that context, too.

Related: Buggy JavaScript Code: The 10 Most Common Mistakes JavaScript Developers Make