Creating a Power BI Calendar using DAX

C
Collab365 TeamAuthorPublished Dec 31, 2018
4

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.

Stop relying on Power BI'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.

Fix the foundation first. Then let the time intelligence functions do their job.

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.

Step 1: The Foundation

Start simple. Navigate to the Modeling ribbon and click New table.

Step 2: Define the Boundaries

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.

Calendar = CALENDAR(DATE(2015,1,1),DATE(2025,12,31))

Step 3: Structure with Variables

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 cal1, and use the RETURN command to output the result.

Calendar = 
// Simple Calendar
var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31))

Return
cal1

Step 4: Expand the Table

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.

Calendar = 
// Simple Calendar
var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31))

// Add Columns
var cal2 = ADDCOLUMNS(cal1,
"Year" , YEAR([Date])
)

Return 
cal2

Step 5: Build the Full Arsenal

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 "MMMM" gives you the full month name, and "DDDD" gives you the day of the week.

Calendar = 
// Simple Calendar
var cal1 = CALENDAR(DATE(2015,1,1),DATE(2025,12,31))

// Add Columns
var cal2 = ADDCOLUMNS(cal1,
"Year" , YEAR([Date]) ,
"Month" , FORMAT([Date] , "MMMM") ,
"MonthNo" , MONTH([Date]) ,
"WeekNo" , WEEKNUM([Date]) ,
"DayText" , FORMAT([Date],"DDDD")
)

Return
cal2

Step 6: Handle Dependent Columns

Here is where most people get stuck. You cannot reference a newly created column inside the exact same ADDCOLUMNS statement. If you want to flag weekends based on your new DayText 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.

var cal3 = ADDCOLUMNS(cal2,
"DayType" , IF(OR([DayText]="Saturday",[DayText]="Sunday"),"Weekend","Weekday")
)

Change the final return statement to output cal3 and your table is complete.

The Hard Truth About Time Intelligence

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.

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.