Tutoriel Power Query · 8 min de lecture

Créer un calendrier dynamique dans Power BI avec Power Query

Une table de dates qui s’étend toute seule à chaque actualisation, sans date de fin codée en dur. Le code complet, puis les colonnes qui servent vraiment.

Mis à jour le

Un modèle Power BI sans table de dates ne va pas loin. Les fonctions de temps de DAX, TOTALYTD, SAMEPERIODLASTYEAR, DATEADD, refusent de fonctionner correctement sans elle. Et tant que la table s’arrête à une date écrite en dur, le rapport cesse d’afficher les données du jour dès qu’on a dépassé cette borne, en général au pire moment.

Ce guide construit une table de dates qui repousse sa borne de fin toute seule à chaque rafraîchissement.

La méthode courte

Une requête, à coller telle quelle

Dans Power BI Desktop : Accueil → Transformer les données → Nouvelle source → Requête vide, puis Éditeur avancé. Remplacez tout par ceci.

let
    Debut  = #date(2020, 1, 1),
    Fin    = Date.EndOfYear(DateTime.Date(DateTime.FixedLocalNow())),
    Nombre = Duration.Days(Fin - Debut) + 1,
    Liste  = List.Dates(Debut, Nombre, #duration(1, 0, 0, 0)),
    Base   = Table.FromList(Liste, Splitter.SplitByNothing(), {"Date"}),
    Typee  = Table.TransformColumnTypes(Base, {{"Date", type date}})
in
    Typee

C’est tout pour le squelette. La ligne qui fait le travail est Fin : elle prend la date du jour au moment de l’actualisation et la pousse à la fin de l’année en cours. La table couvre donc toujours l’exercice complet, sans intervention.

Adaptez Debut à votre historique réel. Une table qui démarre dix ans avant vos premières données alourdit le modèle et allonge inutilement les listes déroulantes.

Variante

Caler la fin sur vos données plutôt que sur le calendrier

Si vos données s’arrêtent au mois dernier, une table qui va jusqu’au 31 décembre affichera des mois vides dans tous les visuels. Dans ce cas, on prend la date maximale de la table de faits :

Fin = Date.EndOfMonth(List.Max(Ventes[Date_Commande])),

La table suit alors la réalité des données. En contrepartie, elle dépend d’une autre requête, ce qui peut ralentir l’actualisation. Le choix entre les deux se fait au cas par cas ; l’année civile suffit dans la plupart des situations.

La méthode par l’interface

Sans écrire de code, pour comprendre ce qui se passe

C’est l’approche à privilégier quand on veut voir le mécanisme plutôt que le subir.

  1. Créez une table à deux colonnes avec Entrer des données : une colonne « Début », une colonne « Fin », une seule ligne. Vérifiez que les deux sont bien au format Date, pas Texte.
  2. Ajoutez une colonne personnalisée nommée « Date », avec la formule {Number.From([Début])..Number.From([Fin])}. Chaque date devient un nombre, et l’opérateur .. engendre la liste de tous les entiers entre les deux.
  3. Développez la liste sur de nouvelles lignes avec l’icône à deux flèches en tête de colonne. Une ligne par jour apparaît.
  4. Convertissez la colonne Date au format Date, puis supprimez « Début » et « Fin » qui ne servent plus.
  5. Rendez la fin dynamique. Retournez sur l’étape « Personnalisée ajoutée » et remplacez Number.From([Fin]) par Number.From(DateTime.Date(DateTime.LocalNow())). La table s’arrête désormais au jour de l’actualisation.

Le résultat est identique à la requête plus haut. La version en code reste plus lisible et plus facile à reprendre, mais ces cinq étapes montrent bien d’où sort la liste de dates.

Étape suivante

Les colonnes qui servent vraiment

Une colonne de dates seule ne suffit pas : il faut les axes sur lesquels les utilisateurs vont découper. Ajoutez-les à la fin de la requête.

    Annee     = Table.AddColumn(Typee, "Année",     each Date.Year([Date]),          Int64.Type),
    NumMois   = Table.AddColumn(Annee, "N° mois",   each Date.Month([Date]),         Int64.Type),
    NomMois   = Table.AddColumn(NumMois, "Mois",    each Date.MonthName([Date]),     type text),
    Trimestre = Table.AddColumn(NomMois, "Trimestre", each "T" & Text.From(Date.QuarterOfYear([Date])), type text),
    AnneeMois = Table.AddColumn(Trimestre, "Année-mois", each Date.ToText([Date], "yyyy-MM"), type text)
in
    AnneeMois

La colonne « N° mois » n’est pas décorative : sans elle, « Mois » se trie par ordre alphabétique et vos graphiques commencent en avril. Dans la vue de données, sélectionnez la colonne « Mois », puis Trier par colonne → N° mois. C’est l’oubli le plus fréquent sur une table de dates.

La colonne « Année-mois » au format 2026-08 se trie naturellement et rend service dans les axes de graphiques comme dans les segments.

L’autre voie

La variante DAX : une table calculée

Le même calendrier se construit côté modèle, sans passer par l’éditeur de requêtes. Dans Power BI Desktop : Modélisation → Nouvelle table, puis ce script.

Calendrier =
ADDCOLUMNS (
    CALENDARAUTO (),
    "Année",          YEAR ( [Date] ),
    "NumMois",        MONTH ( [Date] ),
    "Mois",           FORMAT ( [Date], "mmm", "fr-FR" ),
    "AnnéeMois",      FORMAT ( [Date], "yyyy-MM" ),
    "Trimestre",      "T" & FORMAT ( [Date], "q" ),
    "AnnéeTrimestre", YEAR ( [Date] ) & " T" & FORMAT ( [Date], "q" ),
    "NumJourSemaine", WEEKDAY ( [Date], 2 ),
    "JourSemaine",    FORMAT ( [Date], "ddd", "fr-FR" ),
    "EstJourOuvré",   IF ( WEEKDAY ( [Date], 2 ) <= 5, 1, 0 )
)

CALENDARAUTO balaie toutes les colonnes de dates du modèle et génère des années civiles complètes. Le calendrier suit donc l’arrivée de nouvelles données sans maintenance, ce qui est son intérêt principal.

C’est aussi sa faiblesse. Une seule date aberrante quelque part, une date de naissance de 1970 oubliée dans une table de personnel, et la table s’étend sur cinquante ans. Pour maîtriser les bornes, remplacez l’appel par CALENDAR ( DATE ( 2022, 1, 1 ), DATE ( YEAR ( TODAY () ), 12, 31 ) ).

Quelle voie choisir ? La version Power Query dès que le calendrier doit être partagé entre plusieurs modèles, via un flux de données par exemple, ou quand vous préférez garder la logique hors de DAX. La table calculée quand vous voulez aller vite sur un modèle unique. Le résultat est équivalent, et les trois réglages décrits ci-dessous restent obligatoires dans les deux cas.

À ne pas oublier

Déclarer la table comme table de dates

Une fois la table chargée, deux gestes restent à faire, et ils conditionnent tout le reste.

Reliez la table à chaque table datée du modèle, de la colonne Date vers la colonne de date de la table de faits, en relation un à plusieurs.

Marquez-la comme table de dates : sélectionnez-la, puis Outils de table → Marquer comme table de dates, et désignez la colonne Date. Sans cette déclaration, les fonctions de temps de DAX donnent des résultats faux au lieu de refuser de s’exécuter, ce qui est bien plus difficile à repérer.

Dernier point : la table doit contenir tous les jours sans exception, du 1er janvier au 31 décembre de chaque année couverte. Un trou dans les dates, et les calculs cumulés deviennent silencieusement inexacts.

Dépannage

Ce qui casse les calendriers

Quatre symptômes reviennent en audit, et ils ont tous la même origine : un réglage oublié, jamais une erreur de formule.

SymptômeCause et correctif
SAMEPERIODLASTYEAR renvoie videTable non marquée comme table de dates, ou visuel construit sur la date de la table de faits au lieu de celle du calendrier.
La relation ne filtre rienVos faits portent un datetime avec les heures : la jointure sur une colonne date pure ne correspond jamais. Tronquez la date en Power Query et reliez la colonne obtenue.
Le calendrier démarre en 1900CALENDARAUTO a trouvé une date parasite quelque part dans le modèle. Passez à CALENDAR avec des bornes explicites.
Des hiérarchies de dates partoutDésactivez Date/heure automatique dans Options, Chargement des données : ces hiérarchies cachées alourdissent le modèle et court-circuitent votre calendrier.

Le dernier point mérite d’être fait dès la création du fichier. Power BI crée par défaut une table de dates invisible pour chaque colonne de date du modèle. Sur une vingtaine de colonnes, cela fait vingt tables cachées que personne ne voit et que tout le monde paie.

Questions fréquentes

Ce qu’on nous demande sur la table de dates

Pourquoi ne pas utiliser CALENDARAUTO en DAX ?

CALENDARAUTO dépanne très bien pour un prototype : elle balaie toutes les colonnes de date du modèle et couvre l’amplitude trouvée. Le problème est qu’elle la couvre vraiment toute : une seule date aberrante en 1900 ou en 2099 dans un coin du modèle, et la table explose. En Power Query, les bornes sont explicites et personne ne les élargit par accident.

Comment gérer un exercice fiscal décalé ?

Ajoutez les colonnes propres à l’exercice à côté des colonnes civiles, sans les remplacer. Pour un exercice démarrant en juillet, l’année fiscale s’écrit each if Date.Month([Date]) >= 7 then Date.Year([Date]) + 1 else Date.Year([Date]). Le paramètre de CALENDARAUTO permet la même chose en DAX, mais on garde alors les limites décrites plus haut.

Faut-il une table de dates par table de faits ?

Non, une seule pour tout le modèle. C’est justement son intérêt : un filtre sur l’année agit simultanément sur les commandes, les factures et les stocks. Quand une table de faits porte plusieurs dates, date de commande et date de livraison par exemple, on relie la première et on gère la seconde avec USERELATIONSHIP.

Comment ajouter les jours fériés ?

Le plus solide est une petite table de référence des dates fériées, fusionnée sur la colonne Date. Les listes issues du web sont pratiques mais dépendent d’un site tiers, ce qui finit toujours par casser une actualisation. Pour un calcul de jours ouvrés, cette colonne devient indispensable.

À côté

Les fonctions qu’on utilise avec

Sources — documentation officielle de l’éditeur, consultée le 22 août 2026 : CALENDAR, CALENDARAUTO, List.Dates.

Des comparaisons annuelles qui ne tombent pas juste ?

Neuf fois sur dix, la table de dates est en cause. Nous auditons le modèle et corrigeons la structure. Premier retour sous cinq jours ouvrés.

Demander un audit ou 01 83 64 60 25
01 83 64 60 25 Réserver un appel