FlowKit

Manipuler les dates et les heures dans n8n avec Luxon : le guide pratique

Publié le 28 juillet 2026 · 7 min de lecture

Presque tous les workflows n8n finissent par toucher à une date : horodater un enregistrement, déclencher une relance trois jours après un email, vérifier qu'on est bien dans les heures ouvrées avant d'envoyer une notification, ou afficher « le 28 juillet 2026 à 9h00 » dans un rapport. Bonne nouvelle : n8n embarque nativement Luxon, une bibliothèque JavaScript moderne de manipulation de dates, et l'expose directement dans les expressions via deux variables prêtes à l'emploi : $now et $today. Maîtriser une dizaine de méthodes Luxon suffit à couvrir 95 % des besoins — ce guide les passe en revue avec des exemples directement copiables.

$now et $today : vos deux points de départ

Dans n'importe quelle expression n8n, $now renvoie un objet DateTime Luxon représentant l'instant présent, et $today la même chose avec l'heure remise à minuit (00:00:00). Ce ne sont pas des chaînes de caractères : ce sont des objets riches, sur lesquels on enchaîne des méthodes.

{{ $now }}                      → l'instant présent
{{ $today }}                    → aujourd'hui à 00:00:00
{{ $now.toFormat('yyyy-MM-dd') }} → "2026-07-28"

La distinction compte : pour comparer la date d'un dossier avec « aujourd'hui », $today évite les faux négatifs causés par les heures et minutes ; pour horodater précisément un événement, $now s'impose. Si vous préférez travailler dans un node Code, les mêmes objets sont disponibles, ainsi que la classe DateTime de Luxon — notre guide des expressions JavaScript dans n8n détaille ce que l'on peut faire dans l'un et dans l'autre.

Formater une date : toFormat et les formats prédéfinis

La méthode .toFormat() accepte une chaîne de tokens (documentés par Luxon) qui décrit le rendu souhaité :

{{ $now.toFormat('yyyy-MM-dd') }}        → "2026-07-28"
{{ $now.toFormat('dd/MM/yyyy HH:mm') }}  → "28/07/2026 09:15"
{{ $now.toFormat('cccc dd LLLL yyyy') }} → "Tuesday 28 July 2026"

Attention à la casse des tokens : MM désigne le mois, mm les minutes, HH l'heure sur 24 heures. Pour des rendus localisés en français dans un email (« mardi 28 juillet 2026 »), passez une locale : {{ $now.setLocale('fr').toFormat('cccc dd LLLL yyyy') }}. Et pour échanger avec une API, préférez le format ISO standard : {{ $now.toISO() }} produit une chaîne complète et non ambiguë, fuseau inclus.

Ajouter, soustraire, caler : plus, minus et set

L'arithmétique de dates se fait avec .plus() et .minus(), qui acceptent un objet de durées :

{{ $now.plus({days: 3}) }}               → dans 3 jours
{{ $now.minus({hours: 2, minutes: 30}) }} → il y a 2h30
{{ $today.plus({months: 1}) }}            → dans un mois, à minuit

La méthode .set() complète le duo en fixant une composante précise : {{ $today.plus({days: 1}).set({hour: 9}) }} donne « demain à 9h00 », l'expression type à passer à un node Wait en mode « At Specified Time » — nous détaillons ce fonctionnement dans le guide du node Wait. Luxon gère correctement les cas limites : ajouter un mois au 31 janvier donne le 28 (ou 29) février, pas une date invalide.

Comparer des dates et calculer des durées

Les objets DateTime se comparent directement avec les opérateurs habituels :

{{ $json.deadline_dt < $now }}   → l'échéance est-elle dépassée ?
{{ $json.created >= $today }}    → créé aujourd'hui ?

Pour mesurer un écart, .diff() renvoie un objet Duration dont on extrait l'unité voulue :

{{ $now.diff(DateTime.fromISO($json.created_at), 'days').days }}

Cette expression renvoie l'ancienneté d'un enregistrement en jours (avec décimales — arrondissez avec Math.floor() si besoin). C'est exactement le calcul au cœur d'une logique de relance : « ce dossier a-t-il plus de 3 jours sans réponse ? ». Un node IF avec la condition {{ $now.diff(DateTime.fromISO($json.last_contact), 'days').days >= 3 }} route les dossiers à relancer, pattern que nous appliquons de bout en bout dans notre article sur les relances de dossiers incomplets.

Parser une chaîne : fromISO et fromFormat

Les dates arrivent rarement sous forme d'objets DateTime : une API renvoie une chaîne ISO, un fichier CSV une date au format français, un formulaire un texte libre. Deux méthodes couvrent l'essentiel :

{{ DateTime.fromISO('2026-07-28T09:00:00') }}
{{ DateTime.fromFormat('28/07/2026', 'dd/MM/yyyy') }}

fromISO traite les chaînes au format ISO 8601 (le cas de la majorité des API) ; fromFormat prend en second argument la description exacte du format d'entrée, avec les mêmes tokens que toFormat. Point crucial : une chaîne qui ne correspond pas au format attendu ne lève pas d'erreur, elle produit un DateTime invalide qui se propage silencieusement. Testez .isValid juste après le parsing quand la donnée vient de l'extérieur.

Fuseaux horaires : le sujet qui fait dérailler les workflows

C'est la source numéro un de bugs temporels dans n8n. Trois niveaux de configuration entrent en jeu :

  • L'heure du serveur : la plupart des instances Docker tournent en UTC. $now reflète le fuseau configuré côté n8n, pas celui de votre navigateur.
  • La variable d'environnement GENERIC_TIMEZONE : définissez-la (par exemple Europe/Paris) pour fixer le fuseau par défaut de toute l'instance — c'est elle que lisent les nodes sensibles au temps, comme le Schedule Trigger.
  • La timezone du workflow : dans les paramètres de chaque workflow, une timezone spécifique peut surcharger le défaut de l'instance.

Pour une conversion ponctuelle dans une expression, .setZone() fait le travail : {{ $now.setZone('Europe/Paris').toFormat('HH:mm') }} affiche l'heure de Paris quel que soit le fuseau du serveur. Utilisez toujours les identifiants IANA (Europe/Paris, America/New_York), jamais des décalages fixes comme « UTC+1 » : seuls les identifiants IANA suivent automatiquement les changements d'heure été/hiver.

Ces changements d'heure ne sont d'ailleurs pas un détail folklorique : une étude de Barnes et Wagner publiée en 2009 dans le Journal of Applied Psychology (« Changing to daylight saving time cuts into sleep and increases workplace injuries » — voir sur Google Scholar) montre que le seul passage à l'heure d'été raccourcit mesurablement le sommeil des travailleurs et augmente les accidents du travail. Si une heure de décalage suffit à produire des effets physiques mesurables sur des humains, imaginez ce qu'elle fait à un workflow qui envoie ses relances « à 9h » : les règles de temps civil (DST, fuseaux) sont des pièges bien réels, à modéliser explicitement plutôt qu'à découvrir en production. Pour les déclenchements planifiés, notre guide du Schedule Trigger et des fuseaux horaires creuse spécifiquement ce sujet.

Cas concrets : J+3, heures ouvrées, emails

Relance à J+3. Après un premier email, stockez {{ $now.toISO() }} dans votre base, puis un workflow planifié quotidien filtre avec {{ $now.diff(DateTime.fromISO($json.sent_at), 'days').days >= 3 }}. Alternative sans base de données : un node Wait réglé sur {{ $now.plus({days: 3}).set({hour: 9, minute: 0}) }}.

Fenêtre horaire ouvrée. Avant d'envoyer une notification, un node IF vérifie l'heure et le jour : {{ $now.setZone('Europe/Paris').hour >= 9 && $now.setZone('Europe/Paris').hour < 18 && $now.weekday <= 5 }} (dans Luxon, weekday va de 1 pour lundi à 7 pour dimanche). Hors fenêtre, routez vers un Wait qui patiente jusqu'au prochain créneau ouvré.

Dates lisibles dans les emails et rapports. Un « 2026-07-28T07:15:00.000Z » brut dans un email fait fuir : {{ DateTime.fromISO($json.date).setZone('Europe/Paris').setLocale('fr').toFormat("cccc d LLLL yyyy 'à' HH'h'mm") }} produit « mardi 28 juillet 2026 à 09h15 ». Même logique pour les invitations : notre guide Google Calendar avec n8n montre comment construire les dates de début et de fin d'événements avec ces expressions.

Le node Date & Time : l'alternative sans code

Tout ce qui précède peut aussi se faire visuellement avec le node Date & Time, qui propose des opérations prêtes à l'emploi : formater une date, ajouter ou soustraire une durée, arrondir, extraire une composante (jour, mois, heure), obtenir la date courante. Pour une équipe où tout le monde ne lit pas les expressions, c'est un choix de lisibilité tout à fait valable — le workflow documente lui-même ce qu'il fait. Les expressions Luxon reprennent l'avantage dès qu'il faut chaîner plusieurs opérations dans un seul champ, ou dans un node Code pour des logiques plus élaborées (si vous préférez Python, sachez que Luxon n'y est pas disponible : voir notre guide du node Code en Python pour les équivalents).

Pièges fréquents

  • Confondre l'heure serveur et l'heure locale. $now suit le fuseau de l'instance (souvent UTC en Docker), pas celui de votre navigateur. Définissez GENERIC_TIMEZONE et vérifiez la timezone du workflow avant de chercher un bug ailleurs.
  • Utiliser un décalage fixe au lieu d'un identifiant IANA. « UTC+1 » est faux la moitié de l'année en France ; Europe/Paris suit tout seul les changements d'heure.
  • Ignorer les DateTime invalides. DateTime.fromFormat() sur une chaîne mal formée ne plante pas : il renvoie un objet invalide qui produit des champs vides en aval. Testez .isValid sur toute donnée externe.
  • Se tromper de casse dans les tokens de format. MM = mois, mm = minutes, HH = heure sur 24 heures : un dd/mm/yyyy affiche les minutes à la place du mois, erreur discrète et fréquente.
  • Comparer une chaîne avec un DateTime. $json.date sorti d'une API est une chaîne : parsez-la avec DateTime.fromISO() avant toute comparaison ou tout .diff(), sinon le résultat est incohérent.
  • Oublier que $today n'a pas d'heure. Pratique pour comparer des jours, piégeux si vous l'utilisez pour horodater : tout se retrouve daté de minuit.

Pour aller plus loin

Ces calculs de dates sont l'ossature invisible de la plupart des automatisations sérieuses : sans eux, pas de relance au bon moment ni de rapport daté correctement. C'est exactement ce qui fait tourner le digest quotidien du Pack Inbox IA (79 €) : fenêtres horaires ouvrées pour ne synthétiser que les emails de la journée écoulée, envoi calé sur le fuseau de l'utilisateur, dates lisibles dans le résumé. Pour compléter le tableau côté déclenchement, le guide du Schedule Trigger couvre la planification cron, et celui du node Wait les pauses calculées dynamiquement — les deux compagnons naturels de Luxon dans un workflow temporel.

FAQ

Questions fréquentes

Quelle est la différence entre $now et $today dans n8n ?

Les deux sont des objets DateTime Luxon prêts à l'emploi dans les expressions. $now correspond à l'instant précis de l'exécution du node (date et heure complètes), tandis que $today représente la même date mais avec l'heure remise à 00:00:00. Pour comparer une date de dossier avec « aujourd'hui » sans être parasité par les heures et minutes, $today est le bon réflexe ; pour horodater un email ou calculer un délai précis, utilisez $now.

Pourquoi mes dates n8n s'affichent-elles avec plusieurs heures de décalage ?

Dans la plupart des cas, votre instance tourne en UTC (le défaut des images Docker) alors que vous raisonnez en heure locale. Définissez la variable d'environnement GENERIC_TIMEZONE (par exemple Europe/Paris) pour fixer le fuseau par défaut de l'instance, ou réglez la timezone dans les paramètres du workflow concerné. En dernier recours, convertissez ponctuellement avec .setZone('Europe/Paris') dans l'expression elle-même.

Comment parser une date reçue au format français comme 28/07/2026 ?

DateTime.fromISO ne fonctionne que pour les chaînes au format ISO 8601 (2026-07-28T09:00:00). Pour un format personnalisé, utilisez DateTime.fromFormat('28/07/2026', 'dd/MM/yyyy'), qui décrit explicitement la structure de la chaîne. Vérifiez ensuite la validité du résultat (propriété isValid) avant de poursuivre : une chaîne mal formée produit un DateTime invalide qui propage des valeurs vides dans tout le workflow.

Faut-il utiliser Luxon dans les expressions ou le node Date & Time ?

Les deux aboutissent au même résultat, la différence est une question de lisibilité et d'équipe. Le node Date & Time couvre sans code les opérations courantes (formater, ajouter ou soustraire une durée, arrondir, extraire une composante) et rend le workflow lisible pour un non-développeur. Les expressions Luxon sont plus compactes et plus puissantes dès qu'il faut combiner plusieurs opérations (parser, convertir de fuseau, comparer) dans un seul champ.

Bundle FlowKit Complet

269 €