Creating a Power BI Calendar using DAX

C
Collab365 TeamAuthorPublished Dec 31, 2018
505

At a Glance

Target Audience
Power BI Analysts, Data Modelers
Problem Solved
Year-over-year calculations failing due to improper date tables and reliance on Power BI auto-date features.
Use Case
Time intelligence reports like sales dashboards needing YoY, MoM, QoQ analysis over 10-year spans.
<p>Stop relying on Power BI&#39;s auto-date feature. I see analysts struggling with basic year-over-year calculations because they refuse to build a proper date table. You cannot drop a raw date field into a matrix and expect SAMEPERIODLASTYEAR to just work.</p> <p>Fix the foundation first. Then let the time intelligence functions do their job.</p> <p>This guide covers 6 steps to build a custom DAX calendar from scratch. It handles exactly 3,653 dates across a 10-year span. It is not sexy. It is just the consistent, boring work that makes your data model bulletproof.</p> <h3 id="step-1-the-foundation">Step 1: The Foundation</h3><p>Start simple. Navigate to the Modeling ribbon and click New table.</p> <h3 id="step-2-define-the-boundaries">Step 2: Define the Boundaries</h3><p>You have two paths here. You can use CALENDARAUTO to automatically scan your dataset for minimum and maximum dates. Or you can take total control with CALENDAR. We are taking control. We will define a strict boundary from January 1, 2015, to December 31, 2025.</p> <pre><code class="hljs language-dax">Calendar = CALENDAR(DATE(2015,1,1),DATE(2025,12,31)) </code></pre><p></p> <h3 id="step-3-structure-with-variables">Step 3: Structure with Variables</h3><p>That gives you a single column of dates. But a single column does not help you filter by month or quarter. We need to expand this table. The secret is using variables to keep your DAX clean. Press Shift+Return to add line breaks, define a variable called <code>cal1</code>, and use the <code>RETURN</code> command to output the result.</p> <pre><code class="hljs language-dax">Calendar = // Simple Calendar var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31)) Return cal1 </code></pre><p></p> <h3 id="step-4-expand-the-table">Step 4: Expand the Table</h3><p>Now we wrap our base calendar in the ADDCOLUMNS function. This takes your initial table and appends new data row by row. Let us start by extracting the YEAR.</p> <pre><code class="hljs language-dax">Calendar = // Simple Calendar var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31)) // Add Columns var cal2 = ADDCOLUMNS(cal1, &quot;Year&quot; , YEAR([Date]) ) Return cal2 </code></pre><p></p> <h3 id="step-5-build-the-full-arsenal">Step 5: Build the Full Arsenal</h3><p>One column is a start, but we need the full picture. We will add the MONTH number, the WEEKNUM, and text labels. The FORMAT function is perfect here. Passing &quot;MMMM&quot; gives you the full month name, and &quot;DDDD&quot; gives you the day of the week.</p> <pre><code class="hljs language-dax">Calendar = // Simple Calendar var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31)) // Add Columns var cal2 = ADDCOLUMNS(cal1, &quot;Year&quot; , YEAR([Date]) , &quot;Month&quot; , FORMAT([Date] , &quot;MMMM&quot;) , &quot;MonthNo&quot; , MONTH([Date]) , &quot;WeekNo&quot; , WEEKNUM([Date]) , &quot;DayText&quot; , FORMAT([Date],&quot;DDDD&quot;) ) Return cal2 </code></pre><p></p> <h3 id="step-6-handle-dependent-columns">Step 6: Handle Dependent Columns</h3><p>Here is where most people get stuck. You cannot reference a newly created column inside the exact same <code>ADDCOLUMNS</code> statement. If you want to flag weekends based on your new <code>DayText</code> column, you have to nest it. You create a third variable that builds on top of the second one. We will use a simple IF statement combined with an OR condition to check for Saturday and Sunday.</p> <pre><code class="hljs language-dax">var cal3 = ADDCOLUMNS(cal2, &quot;DayType&quot; , IF(OR([DayText]=&quot;Saturday&quot;,[DayText]=&quot;Sunday&quot;),&quot;Weekend&quot;,&quot;Weekday&quot;) ) </code></pre><p>Change the final return statement to output <code>cal3</code> and your table is complete.</p> <p></p> <h3 id="the-hard-truth-about-time-intelligence">The Hard Truth About Time Intelligence</h3><p>This framework gives you a standard Gregorian calendar. It works perfectly for most use cases. But what if your company uses a custom 4-4-5 fiscal calendar? That requires more heavy lifting.</p> <p>Before you spend hours writing complex DAX to force a fiscal structure, ask yourself one question. Does your data warehouse already have a corporate date table you can import? Sometimes the best DAX is no DAX at all.</p>