Dyota's blog

Power BI: Calendar table

This is my trusty calendar table, plus a months table (so that months can be sorted correctly, either Jan-Dec or Jul-Jun).

// dyota 2024-08-05
// this table includes a week number, and a financial year week number, to do week-to-week comparisons

let
    startYear = 2021,
    endYear = Date.Year(DateTime.LocalNow()) + 2,

    years = {startYear .. endYear},

    start = #date(startYear, 07, 01),
    end = #date(endYear, 06, 30),
    FiscalYearEndMonth = 6,

    TodaysDate = Date.From(DateTime.LocalNow()),

    CurrentFiscalYear = if Date.Month(DateTime.LocalNow()) <= 6 then Date.Year(DateTime.LocalNow()) else Date.Year(DateTime.LocalNow()) + 1,
    CurrentEOM = Date.From(Date.EndOfMonth(DateTime.LocalNow())),
    
    // "+1" included so that it will include the very last day (i.e. 30th June of the last year)
    Source = List.Dates(start, Duration.Days(end-start) + 1, #duration(1, 0, 0, 0)),

    tableAddColumns = (source as table, columnsToAdd as list) as table => 
        List.Accumulate(
            columnsToAdd,
            source,
            (base as table, thisColumn as list) => 
                Table.AddColumn(
                    base, 
                    thisColumn{0},
                    thisColumn{1},
                    try thisColumn{2} otherwise null
                )
            ),

    #"Converted to Table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "Date"}}),
    #"Changed to DateFormat" = Table.TransformColumnTypes(#"Renamed Columns",{{"Date", type date}}),
    
    #"Added Index" =            Table.AddIndexColumn(#"Changed to DateFormat", "Index", 1, 1, Int64.Type),

    q1 = {7..9},
    q2 = {10..12},
    q3 = {1..3},
    q4 = {4..6},
    h1 = q1 & q2,
    h2 = q3 & q4,

    addColumns = tableAddColumns(
        #"Added Index", 
        {
            // break down into numbers
            { "Day", each Date.Day([Date]), Int8.Type }, 
            { "Week Number", each Date.WeekOfYear([Date]), Int16.Type },
            { "Calendar Month", each Date.Month([Date]), Int64.Type }, 
            { "Calendar Quarter", each Date.QuarterOfYear([Date]), Int64.Type }, 
            { "Calendar Quarter Label", each "Q" & Text.From([Calendar Quarter]), type text }, 
            { "Calendar Year", each Date.Year([Date]), Int64.Type }, 

            // names
            { "Weekday", each Date.DayOfWeek([Date],Day.Monday), Int64.Type },
            { "DayName", each Date.ToText([Date],"ddd"), type text }, 
            { "Month Name", each Date.ToText([Date],"MMMM"), type text }, 
            { "Short Month Name", each Text.Start([Month Name], 3), type text }, 
            
            // surrounding dates
            { "Start Of Week", each Date.StartOfWeek([Date],Day.Monday), type date },
            { "End Of Week", each Date.EndOfWeek([Date]), type date }, 
            { "End Of Week Offset", each ([End Of Week] - Date.EndOfWeek(TodaysDate))/7, type duration },
            { "Start of Month", each Date.StartOfMonth([Date]), type date }, 
            { "End of Month", each Date.EndOfMonth([Date]), type date }, 

            // text formats
            { "DD-MMM-YY", each Date.ToText([Date],"d-MMM-yy"), type text }, 
            { "Week Ending", each "w/e " & Date.ToText([End Of Week],[Format="d-MMM-yy", Culture="en-AU"]), type text },
            { "YearMonthDay", each Date.ToText([Date], "yyyyMMdd"), type text }, 
            { "YearMonth", each Date.ToText([Date], "yyyyMM"), type text }, 

            // fiscal year
            { "Fiscal Month", each if [Calendar Month] <= FiscalYearEndMonth then [Calendar Month] + FiscalYearEndMonth else [Calendar Month] - FiscalYearEndMonth, Int8.Type }, 
            { "Fiscal Quarter", each if List.Contains(h1, [Calendar Month]) then [Calendar Quarter] - 2 else [Calendar Quarter] + 2 , Int64.Type },
            { "Fiscal Quarter Label", each "Q" & Text.From([Fiscal Quarter]), type text }, 
            { "Fiscal Year", each if [Fiscal Month] > FiscalYearEndMonth then [Calendar Year] else [Calendar Year] + 1, Int16.Type }, 
            { "Fiscal Year Label", each "FY" & Text.End(Text.From([Fiscal Year]), 2), type text }, 
            { "MMM-YY", each Date.ToText([Date],"MMM-yy"), type text }, 
            {
                "Fiscal Year (Week Basis)", 
                each 
                    let
                        month = Date.Month([Start Of Week]),
                        year = Date.Year([Start Of Week])
                    in
                        if (List.Contains(h1, month)) then year + 1 else year,
                Int16.Type
            }, 

            { "FiscalYearOffset", each [Fiscal Year] - CurrentFiscalYear, Int8.Type }, 

            { "MonthWeek", each Number.ToText(Date.Month([Date])) &  "|" & Number.ToText(Number.RoundUp( [Day] / 7, 0)), type text },

            // status/place
            { "CurrentMonth", each [End of Month] = CurrentEOM, type logical }, 
            { "PastMonth", each [Date] < Date.From(Date.StartOfMonth(DateTime.LocalNow())), type logical }, 
            { "CurrentYTD", each ([Fiscal Year]= CurrentFiscalYear) and ([End of Month] <= CurrentEOM), type logical },
            { "CurrentFiscalYear", each [Fiscal Year] = CurrentFiscalYear, type logical },
            
            { "Type", each if [Date] < TodaysDate then "Actuals" else "Forecast", type text }
        }
    ),

    // segment into financial years

    tableFinancialYears = 
        let
            subTables = List.Transform(
                years,
                each 
                    let
                        _fy = _,
                        datesThisFY = Table.SelectRows(
                            addColumns,
                            each [#"Fiscal Year (Week Basis)"] = _fy
                        )    
                    in
                        datesThisFY
            ),
            onlyWholeFYs = List.Select(
                subTables, 
                each Table.RowCount(_) > 50
            ),

            addIndex = List.Transform(
                onlyWholeFYs, 
                each 
                    let
                        addIndex = Table.AddIndexColumn(
                            _, 
                            "Week Number (FY)", 
                            1, 
                            1, 
                            Int8.Type
                        ),

                        weekNumber = Table.TransformColumns(
                            addIndex, 
                            {
                                {
                                    "Week Number (FY)",
                                    each Number.RoundUp(_ / 7),
                                    Int8.Type
                                }
                            }
                        )
                    in
                        weekNumber
            )
        in
            addIndex,
    recombine = Table.Combine(tableFinancialYears)

    
in
    recombine
let
    Source = Csv.Document(
        Text.Trim("
Month Name,Short Name,Calendar Order,Financial Year Order
January,Jan,1,7
February,Feb,2,8
March,Mar,3,9
April,Apr,4,10
May,May,5,11
June,Jun,6,12
July,Jul,7,1
August,Aug,8,2
September,Sep,9,3
October,Oct,10,4
November,Nov,11,5
December,Dec,12,6        
        "), 
        {"Month Name", "Short Name", "Calendar Order", "Financial Year Order"}
    ),
    #"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
    #"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Month Name", type text}, {"Short Name", type text}, {"Calendar Order", Int64.Type}, {"Financial Year Order", Int64.Type}})
in
    #"Changed Type"

#powerbi #powerquery