Guide

Événements de calendrier récurrents : RRULE expliqué

Un standup hebdomadaire est un seul événement en base de données et cinquante-deux à l'écran. Chaque système de calendrier comble cet écart avec des règles de récurrence — les chaînes RRULE de la RFC 5545 — et chaque intégration finit par rencontrer les mêmes questions : ce que signifie la syntaxe de la règle, qui expanse la série en instances, et ce qui se passe quand une occurrence est déplacée. Ce guide répond aux trois, avec les spécificités de Google, Microsoft Graph et CalDAV côte à côte.

Written by Hazik Director of Product Management

VerifiedCLI 3.1.16 · last tested June 6, 2026

Références des commandes utilisées dans ce guide : nylas calendar events list et nylas calendar events show.

Qu'est-ce qu'une RRULE ?

Une RRULE est la grammaire de règles de récurrence définie dans la RFC 5545 (la spécification iCalendar) qui décrit un planning répétitif en une seule chaîne. Une règle remplace des centaines de lignes stockées : l'événement existe une seule fois, et le logiciel en dérive les occurrences. Les trois grands systèmes de calendrier stockent la RRULE directement ou la traduisent dans les deux sens à leurs frontières.

La grammaire comporte une partie obligatoire, FREQ, et un ensemble de modificateurs. Les 6 que vous utiliserez réellement sont listés ci-dessous — et une contrainte de la RFC compte plus que toutes les autres : la spécification indique que les parties UNTIL et COUNT "MUST NOT occur in the same 'recur'" (ne doivent pas figurer dans la même règle). Une règle se termine d'exactement 3 façons : jamais, après N occurrences, ou à une date.

PartieSignificationExemple
FREQCadence de base (obligatoire)FREQ=WEEKLY
INTERVALToutes les N périodes (1 par défaut)INTERVAL=2 — toutes les deux semaines
BYDAYSélecteur de jour de semaine ; accepte les ordinauxBYDAY=MO,WE,FR ou BYDAY=-1FR (dernier vendredi)
BYMONTHDAYSélecteur de jour du moisBYMONTHDAY=15
COUNTFin après N occurrencesCOUNT=12
UNTILFin à un horodatage UTCUNTIL=20261231T235959Z
# Every Monday and Wednesday for 12 occurrences
RRULE:FREQ=WEEKLY;BYDAY=MO,WE;COUNT=12

# Biweekly Friday, forever
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=FR

# Last Friday of each month through 2026
RRULE:FREQ=MONTHLY;BYDAY=-1FR;UNTIL=20261231T235959Z

Qui expanse la série en instances ?

L'expansion, c'est-à-dire transformer une règle en occurrences datées, se produit à un endroit différent selon l'API, et cet emplacement détermine la quantité de code de récurrence que vous écrivez. Google expanse côté serveur à la demande, Graph stocke un objet structuré et expose une vue instances, et CalDAV livre la règle brute en laissant le calcul au client. Les 3 modèles en bref :

  • API Google Calendar — passez singleEvents=true à events.list et la réponse contient les instances individuelles dans votre fenêtre temporelle, chacune portant un recurringEventId. Le guide des événements récurrents de Google couvre le modèle maître vs instance.
  • Microsoft Graph — les événements stockent un objet patternedRecurrence avec un pattern (cadence) et un range (bornes) au lieu d'une chaîne RRULE brute ; l'expansion des instances vient de la vue instances de l'événement maître de la série, bornée par des paramètres de début et de fin.
  • CalDAV — le serveur renvoie le VEVENT avec sa RRULE telle quelle, et le client l'expanse, calculs de fuseau horaire compris. Y parvenir correctement sans assistance est le chemin le plus difficile ; la section récurrence de la RFC 5545 s'étend sur des dizaines de pages, et ce n'est pas un hasard.

La règle pratique : si votre intégration a seulement besoin de "ce qui se passe cette semaine", préférez une API (ou un outil) qui expanse pour vous. Écrire un expanseur côté client correct est un projet, pas une fonction. Pour les différences entre ces 3 API au-delà de la récurrence (auth, disponibilités, webhooks), consultez le comparatif des API calendrier; cette page reste centrée sur le problème de la récurrence.

Que se passe-t-il quand une occurrence est déplacée ou annulée ?

Une occurrence unique modifiée devient une exception : un enregistrement autonome qui remplace la règle pour une date pendant que le reste de la série suit l'événement maître. Chaque système l'encode différemment — Google crée un événement séparé avec un champ originalStartTime, Graph le modélise comme un type d'occurrence exception, et iCalendar ajoute un second VEVENT avec un RECURRENCE-ID correspondant à l'instance déplacée.

Les exceptions sont l'endroit où le code de synchronisation naïf casse. Deux modes de défaillance expliquent la plupart des bugs : traiter une exception comme un événement tout neuf (la réunion apparaît deux fois : une fois via l'expansion de la règle, une fois comme dérogation), et appliquer une modification de série par-dessus une exception (le changement de salle ponctuel de l'utilisateur est silencieusement annulé). La logique de synchronisation doit vérifier la présence d'un marqueur d'exception avant d'appliquer l'un ou l'autre chemin — une vérification à 2 branches bien moins coûteuse que le ticket de support pour réunion en double.

Comment lire les événements récurrents depuis le CLI ?

La commande nylas calendar events list renvoie les instances expansées dans la fenêtre demandée, donc une série hebdomadaire apparaît comme des occurrences datées sans aucun parsing de RRULE de votre côté. Chaque instance d'une série récurrente porte un master_event_id qui pointe vers l'événement maître de la série, et l'identifiant propre de l'instance intègre l'horodatage de son occurrence — les deux sont visibles dans la sortie JSON.

# Expanded instances for the next 30 days
nylas calendar events list --days 30 --json | jq '.[] | {
  id: .id,
  title: .title,
  master: .master_event_id,
  start: .when.start_time
}'

# Group instances by series to spot every recurring meeting
nylas calendar events list --days 30 --json | jq '
  [.[] | select(.master_event_id != null)]
  | group_by(.master_event_id)
  | map({series: .[0].title, occurrences: length})'

Ces 2 mêmes commandes fonctionnent avec les comptes Google, Microsoft et iCloud, ce qui élimine, pour les chemins de lecture, les différences d'expansion par API de la section précédente. Une limite assumée : la création d'événements avec la commande calendar events create du CLI ne prend pas de flag de récurrence — la création de séries reste dans l'interface ou l'API de votre fournisseur, et le CLI gère la lecture, le filtrage et le scripting.

Pourquoi les événements récurrents se décalent-ils au changement d'heure ?

Une réunion hebdomadaire à 9 h 00 est à 9 h 00 dans un fuseau horaire nommé, pas à un décalage UTC fixe — donc deux fois par an, son heure UTC bouge d'une heure. Les règles expansées contre l'UTC brut dérivent après chaque transition d'heure d'été ; une expansion correcte résout chaque occurrence via la base de données des fuseaux horaires (America/Toronto, pas UTC-5). C'est en grande partie pourquoi l'expansion CalDAV côté client est difficile, et c'est aussi encodé dans l'exigence de la RFC 5545 selon laquelle les horodatages UNTIL doivent être en UTC alors que les débuts d'événements portent des références de fuseau horaire.

Quand vous auditez un bug de synchronisation qui apparaît en mars ou en novembre, vérifiez d'abord quelle horloge l'expanseur a utilisée. Le JSON d'instance ci-dessus inclut start_timezone sur les événements à plage horaire, donc un filtre jq d'une ligne sur les instances dont le fuseau diffère de votre attente trouve vite les séries qui ont dérivé.

Étapes suivantes