Skip to content

Commit 7670ae6

Browse files
post(Mars Calendar): release
1 parent e4c3168 commit 7670ae6

4 files changed

Lines changed: 179 additions & 0 deletions

File tree

204 KB
Loading
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---
2+
title: Calendar-Based Time Intelligence Is Out Of This World
3+
description: Darian Calendar using Calendar-Based Time Intelligence
4+
image: /assets/images/blog/2025/2025-11-20-CalendarTimeIntel/hero.jpg
5+
date:
6+
created: 2025-11-20
7+
authors:
8+
- jDuddy
9+
comments: true
10+
categories:
11+
- Data Modelling
12+
slug: posts/CalendarTimeIntel
13+
links:
14+
- MS Docs: https://learn.microsoft.com/en-us/power-bi/transform-model/desktop-time-intelligence#calendar-based-time-intelligence-preview
15+
- SQLBI: https://www.sqlbi.com/articles/introducing-calendar-based-time-intelligence-in-dax
16+
---
17+
18+
Microsoft recently released [Calendar-based time intelligence](https://learn.microsoft.com/en-us/power-bi/transform-model/desktop-time-intelligence#calendar-based-time-intelligence-preview) and it is out of this world. So much so lets try and make a Martian Calendar.
19+
20+
## Calendar-Based Time Intelligence
21+
22+
Calendar-Based Time Intelligence allows you to add metadata to your Date Dimension, specifying fields as periods (Year, Month, Quarter, **Week**), and you can define one of more different calendars for the same dimension. But why is this helpful?
23+
24+
- **Any Calendar**: Not limited to single Gregorian, you can have Shifted Gregorian, 445, or something more exotic
25+
- **Sparse dates**: Classic time intelligence requires complete date columns with no missing dates between first and last dates. Calendar-based time intelligence operates on dates as-is, so if your stores are closed on weekends, you can skip those days entirely
26+
- **Week-based calculations**: Direct support for week granularity functions like `TOTALWTD()`
27+
- **Optimized queries**: If you filter by a defined period (Year, Month etc.) the engine can filter by the period (`Year: {2021, 2022, 2023}`), rather than falling back to date (`Date: {01-01-2021,....,31-12-2023}`), resulting in smaller cache and faster queries
28+
29+
## Martian Calendar
30+
31+
On Mars a day (Sol) is 24 hours, 39 minutes, and 35 seconds, nearly 40 minutes longer than a day on Earth, and the Martian year is 668.5907 sols (686.9711 Earth days). One proposed calendar for Mars is the [Darian calendar](https://en.wikipedia.org/wiki/Darian_calendar), which splits the year into 24 months with varying lengths.
32+
33+
=== "Darian Calendar"
34+
35+
|MartianYear | MartianMonth | SolOfMonth | MonthName | Date | SolsSinceEpoch |
36+
|---|---|---|---|---|---|
37+
|200 | 1 | 1 | Sagittarius | 1-Sagittarius-200 | 1 |
38+
|200 | 1 | 2 | Sagittarius | 2-Sagittarius-200 | 2 |
39+
|200 | 1 | 3 | Sagittarius | 3-Sagittarius-200 | 3 |
40+
|200 | 1 | 4 | Sagittarius | 4-Sagittarius-200 | 4 |
41+
|200 | 1 | 5 | Sagittarius | 5-Sagittarius-200 | 5 |
42+
43+
=== "DAX"
44+
45+
```dax
46+
Dates =
47+
VAR StartMartianYear = 200 // Adjust as needed
48+
VAR EndMartianYear = 220
49+
VAR EarthEpoch = DATE(1609, 3, 11) // Telescopic Epoch (Start of Mars Year 0)
50+
VAR SolsPerEarthDay = 1.02749125
51+
VAR StandardYearSols = 668 // Sols in a standard Martian Year (Darian Calendar)
52+
VAR LeapYearSols = 669 // Sols in a leap Martian Year (Darian Calendar)
53+
54+
VAR MonthNames =
55+
DATATABLE(
56+
"MartianMonth", INTEGER,
57+
"MonthName", STRING,
58+
{
59+
{1, "Sagittarius"}, {2, "Dhanus"}, {3, "Capricornus"}, {4, "Makara"},
60+
{5, "Aquarius"}, {6, "Kumbha"}, {7, "Pisces"}, {8, "Mina"},
61+
{9, "Aries"}, {10, "Mesha"}, {11, "Taurus"}, {12, "Rishabha"},
62+
{13, "Gemini"}, {14, "Mithuna"}, {15, "Cancer"}, {16, "Karka"},
63+
{17, "Leo"}, {18, "Simha"}, {19, "Virgo"}, {20, "Kanya"},
64+
{21, "Libra"}, {22, "Tula"}, {23, "Scorpius"}, {24, "Vrishika"}
65+
}
66+
)
67+
68+
VAR IsLeapYear =
69+
SELECTCOLUMNS(
70+
GENERATESERIES(StartMartianYear, EndMartianYear, 1),
71+
"MartianYear", [Value],
72+
// Your Leap Rule: Odd years OR years ending in 0 (Custom Darian)
73+
"IsLeap", MOD([Value], 2) = 1 || MOD([Value], 10) = 0,
74+
"SolsInYear", IF(MOD([Value], 2) = 1 || MOD([Value], 10) = 0, LeapYearSols, StandardYearSols)
75+
)
76+
77+
VAR CalendarMonthSols =
78+
SELECTCOLUMNS(
79+
GENERATE(
80+
IsLeapYear,
81+
GENERATESERIES(1, 24, 1) // 24 months
82+
),
83+
[MartianYear],
84+
"MartianMonth", [Value],
85+
"SolsInMonth",
86+
IF(
87+
MOD([Value] - 1, 6) + 1 <= 5,
88+
28,
89+
IF([Value] = 24 && [IsLeap], 28, 27)
90+
)
91+
)
92+
93+
VAR CalendarDayBase =
94+
SELECTCOLUMNS(
95+
GENERATE(
96+
CalendarMonthSols,
97+
GENERATESERIES(1, [SolsInMonth], 1)
98+
),
99+
[MartianYear],
100+
[MartianMonth],
101+
"SolOfMonth", [Value]
102+
)
103+
104+
VAR AddSolsSinceEpoch =
105+
ADDCOLUMNS(
106+
CalendarDayBase,
107+
"SolsSinceEpoch",
108+
VAR CurrentYear = [MartianYear]
109+
VAR CurrentMonth = [MartianMonth]
110+
VAR CurrentSol = [SolOfMonth]
111+
// Calculate total sols from all complete previous years
112+
VAR SolsFromPreviousYears =
113+
SUMX(
114+
FILTER(IsLeapYear, [MartianYear] < CurrentYear),
115+
[SolsInYear]
116+
)
117+
// Calculate sols from complete previous months in current year
118+
VAR SolsFromPreviousMonths =
119+
SUMX(
120+
FILTER(CalendarMonthSols,
121+
[MartianYear] = CurrentYear && [MartianMonth] < CurrentMonth
122+
),
123+
[SolsInMonth]
124+
)
125+
RETURN SolsFromPreviousYears + SolsFromPreviousMonths + CurrentSol
126+
)
127+
128+
VAR Combine =
129+
ADDCOLUMNS(
130+
NATURALLEFTOUTERJOIN(
131+
AddSolsSinceEpoch,
132+
MonthNames
133+
),
134+
"Date", [SolOfMonth] & "-" & [MonthName] & "-" & [MartianYear]
135+
)
136+
RETURN
137+
Combine
138+
```
139+
140+
Since the `[Date]` field is not a tradition date, we are not able to mark this as a date table. But we are still able to define our calendar.
141+
142+
![Define Calendar](DefineCalendar.png)
143+
144+
And we can create some fake weather data to test our calendar.
145+
146+
??? demo "Weather Table"
147+
148+
```dax
149+
FactWeather =
150+
VAR BaseTemp = -60 // Average global surface temp on Mars in Celsius
151+
VAR TempRange = 25 // Typical daily temperature variation
152+
VAR MaxDailyVariance = 5
153+
RETURN
154+
ADDCOLUMNS(
155+
SELECTCOLUMNS(
156+
Dates,
157+
"Date", [Date],
158+
"AvgTemp_C", BaseTemp + (RAND() * TempRange)
159+
),
160+
"MinTemp_C", [AvgTemp_C] - (RAND() * MaxDailyVariance),
161+
"MaxTemp_C", [AvgTemp_C] + (RAND() * MaxDailyVariance)
162+
)
163+
```
164+
165+
Then we are able to use any of the regular time intelligence functions by specifying our calendar, like seeing what the weather was like last year.
166+
167+
```dax
168+
Avg Temp Previous Year =
169+
CALCULATE(
170+
[Avg Temp],
171+
SAMEPERIODLASTYEAR( 'Darian')
172+
)
173+
```
174+
175+
![Weather Last Year](./TempLastYear.png)
176+
177+
## Conclusion
178+
179+
Calendar-Based Time Intelligence opens up exciting possibilities beyond traditional Gregorian calendars, and final Martian's can use DAX's Time Intelligence functions.
62.2 KB
Loading
61.8 KB
Loading

0 commit comments

Comments
 (0)