Forum Discussion

janmack79's avatar
janmack79
Regular Visitor
6 years ago

Help creating date table that includes fiscal hierarchy

Hello I used PBi occasionaly over the last year and just finished a 4 week weekend training and now trying to build more robust reporting and dashbaords and one of my goals is to create a Date Table using script and include our fiscal calender years and quarters.

 

Searching out in Google we found on another community posting this blog post where the user built their own script http://geekswithblogs.net/darrengosbell/archive/2014/03/23/extending-the-powerquery-date-table-generator-to-include-iso-weeks.aspx

 

Below I put together a table for an example of 8 fiscal years of what our FY calendar dates are and we have tried modfiying the script below that was in the blog post but to the right in my table is what the results are coming out as. Other than changing Jan4 to Jan1 in the script which I have alrdeay tried I am not sure where else I need to edit. Any help to lead me in the rigth path would very much be appreacited!

 

Fiscal Year

  • Starts on the Monday of the first week that contains 1/1
  • 5-4-4 weeks in each quarter
  • Every 6 years has 5th week in September

Example How it should look

    What the Query Script Below Generates  

Year

FY Start Date

End of 1st Week

FY End Date

  

Fiscal Year

Min Date

Max Date

2023

12/26/2022

1/1/2023

12/31/2023

5 weeks in Sept

 

2023

1/2/2023

12/31/2023

2022

12/27/2021

1/2/2022

12/25/2022

  

2022

1/3/2022

1/1/2023

2021

12/28/2020

1/3/2021

1/2/2022

  

2021

1/4/2021

1/2/2022

2020

12/30/2019

1/5/2020

12/27/2020

Leap year

 

2020

12/30/2019

1/3/2021

2019

12/31/2018

1/6/2019

12/29/2019

 

 

2019

12/31/2018

12/29/2019

2018

1/1/2018

1/6/2019

12/30/2018

 

 

2018

1/1/2018

12/30/2018

2017

12/26/2016

1/1/2017

12/31/2017

5 weeks in Sept

 

2017

1/2/2017

12/31/2017

2016

12/28/2015

1/3/2016

12/25/2016

Leap year

 

2016

1/4/2016

1/1/2017

         
         

 

let CreateDateTable = (StartDate as date, EndDate as date, optional Culture as nullable text) as table =>

let

DayCount = Duration.Days(Duration.From(EndDate - StartDate)),

Source = List.Dates(StartDate,DayCount,#duration(1,0,0,0)),

TableFromList = Table.FromList(Source, Splitter.SplitByNothing()),

ChangedType = Table.TransformColumnTypes(TableFromList,{{"Column1", type date}}),

RenamedColumns = Table.RenameColumns(ChangedType,{{"Column1", "Date"}}),

InsertYear = Table.AddColumn(RenamedColumns, "CALYear", each Date.Year([Date])),

InsertQuarter = Table.AddColumn(InsertYear, "CALQuarterOfYear", each Date.QuarterOfYear([Date])),

InsertMonth = Table.AddColumn(InsertQuarter, "CALMonthOfYear", each Date.Month([Date])),

InsertDay = Table.AddColumn(InsertMonth, "DayOfMonth", each Date.Day([Date])),

InsertDayInt = Table.AddColumn(InsertDay, "DateInt", each [CALYear] * 10000 + [CALMonthOfYear] * 100 + [DayOfMonth]),

InsertMonthName = Table.AddColumn(InsertDayInt, "CALMonthName", each Date.ToText([Date], "MMMM", Culture), type text),

InsertMonthShort = Table.AddColumn(InsertMonthName, "CALMonthNameShort", each Date.ToText([Date], "MMM", Culture), type text),

InsertCalendarMonth = Table.AddColumn(InsertMonthShort, "CALMonthInCalendar", each (try(Text.Range([CALMonthName],0,3)) otherwise [CALMonthName]) & " " & Number.ToText([CALYear])),

InsertCalendarQtr = Table.AddColumn(InsertCalendarMonth, "CALQuarterInCalendar", each "Q" & Number.ToText([CALQuarterOfYear]) & " " & Number.ToText([CALYear])),

InsertDayWeek = Table.AddColumn(InsertCalendarQtr, "DayInWeek", each Date.DayOfWeek([Date],1)+1),

InsertDayName = Table.AddColumn(InsertDayWeek, "DayOfWeekName", each Date.ToText([Date], "dddd", Culture), type text),

InsertWeekEnding = Table.AddColumn(InsertDayName, "WeekEndingFriday", each Date.EndOfWeek([Date],6), type date),

 

InsertFyYear = Table.AddColumn(InsertWeekEnding, "zFyYearSort", each if [CALMonthOfYear] <= 6 then Date.Year([Date])-1 else Date.Year([Date])),

InsertFyYearCode  = Table.AddColumn(InsertFyYear , "FyYear", each Text.End(Text.From([zFyYearSort]),2)&"/"&Text.End(Text.From([zFyYearSort]+1),2),type text),

InsertFyQtr = Table.AddColumn(InsertFyYearCode  , "FyQuarter", each if [CALMonthOfYear] <= 6 then "Q"&Text.From(Date.QuarterOfYear([Date])+2) else "Q"&Text.From(Date.QuarterOfYear([Date])-2)),

InsertFyMonth = Table.AddColumn(InsertFyQtr , "FyMonth", each if [CALMonthOfYear] <= 6 then Date.Month([Date])+6 else Date.Month([Date])-6),

InsertFyYearQtr = Table.AddColumn(InsertFyMonth , "FyYearQtr", each [FyYear] & "-" & [FyQuarter], type text),

InsertFyYearMonth = Table.AddColumn(InsertFyYearQtr , "FyYearMonth", each [FyYear] & "-" & [CALMonthNameShort], type text),

InsertFyYearMonthNum = Table.AddColumn(InsertFyYearMonth, "FyYearMonthNum", each Text.From([zFyYearSort]) & "-" & Text.PadStart(Text.From([FyMonth]),2,"0"), type text),

 

InsertCurrentThursday = Table.AddColumn(InsertFyYearMonthNum , "CurrentThursday", each Date.AddDays([Date], -Date.DayOfWeek([Date],1) + 3), type date),

InsertKratosWeekJan4 = Table.AddColumn(InsertCurrentThursday, "KratosWeekJan4", each Date.FromText(Number.ToText(Date.Year([CurrentThursday])) & "-01-04") ,type date),

InsertKratosWeekYear = Table.AddColumn(InsertKratosWeekJan4, "KratosYear", each Date.Year([CurrentThursday])) ,

InsertKratosWeekFirstMon = Table.AddColumn(InsertKratosWeekYear, "KratosWeekFirstMon", each

if [CurrentThursday] < [KratosWeekJan4]

then Date.AddDays([CurrentThursday],-3)

else Date.AddDays([KratosWeekJan4], - Date.DayOfWeek([KratosWeekJan4],1) )

,type date),

InsertKratosWeekNum = Table.AddColumn(InsertKratosWeekFirstMon, "KratosWeek", each Number.RoundUp(((Duration.Days(Duration.From([Date] - [KratosWeekFirstMon]))+1) /7 )), type number),

InsertKratosWeekID = Table.AddColumn(InsertKratosWeekNum, "KratosWeekID", each [KratosYear] * 100 + [KratosWeek], type number),

InsertKratosWeekName = Table.AddColumn(InsertKratosWeekID, "KratosWeekName", each Text.From([KratosYear]) & "W" & Text.End( "0" & Text.From(([KratosWeek]*10) + [DayInWeek]),3)),

InsertKratosWeekNameLong = Table.AddColumn(InsertKratosWeekName, "KratosWeekNameLong", each Text.From([KratosYear]) & "-W" & Text.End( "0" & Text.From([KratosWeek]),2) & "-" & Text.From([DayInWeek])),

 

fnPeriod544a = (weekNum) => let

Periods =

{

{(x)=>x<6, [P=1,Q=1,M="Jan"]},

{(x)=>x<10, [P=2,Q=1,M="Feb"]},

{(x)=>x<14, [P=3,Q=1,M="Mar"]},

{(x)=>x<19, [P=4,Q=2,M="Apr"]},

{(x)=>x<23, [P=5,Q=2,M="May"]},

{(x)=>x<27, [P=6,Q=2,M="Jun"]},

{(x)=>x<32, [P=7,Q=3,M="Jul"]},

{(x)=>x<36, [P=8,Q=3,M="Aug"]},

{(x)=>x<40, [P=9,Q=3,M="Sep"]},

{(x)=>x<45, [P=10,Q=4,M="Oct"]},

{(x)=>x<49, [P=11,Q=4,M="Nov"]},

{(x)=>true, [P=12,Q=4,M="Dec"]}

},

Result = List.First(List.Select(Periods, each _{0}(weekNum))){1}

in

Result,

 

InsertPeriod544 = Table.AddColumn(InsertKratosWeekNameLong, "Period544Record", each fnPeriod544a([KratosWeek])),

ExpandPeriod544 = Table.ExpandRecordColumn(InsertPeriod544, "Period544Record", {"P","Q","M" }, {"KratosMonth", "KratosQuarter", "KratosMonthName"}),

RemovedColumns = Table.RemoveColumns(ExpandPeriod544,{"CurrentThursday", "KratosWeekFirstMon", "KratosWeekJan4"}),

DaysFromToday = Table.AddColumn(RemovedColumns, "Days from Today" , each Date.From(DateTime.LocalNow()) - [Date]),

WeeksFromToday = Table.AddColumn(DaysFromToday, "Weeks from Today" , each (Date.From(DateTime.LocalNow()) - [Date])/7),

MonthsFromToday = Table.AddColumn(WeeksFromToday, "Months from Today" , each (Date.From(DateTime.LocalNow()) - [Date])/(365/12)),

ChangedType1 = Table.TransformColumnTypes(MonthsFromToday,{{"Date", type date}, {"CALYear", Int64.Type}, {"CALQuarterOfYear", Int64.Type}, {"Days from Today", Int64.Type}, {"Weeks from Today", Int64.Type}, {"Months from Today", Int64.Type}, {"CALMonthOfYear", Int64.Type}, {"DayOfMonth", Int64.Type}, {"DateInt", Int64.Type}, {"CALMonthName", type text}, {"CALMonthNameShort", type text}, {"CALMonthInCalendar", type date}, {"CALQuarterInCalendar", type text}, {"DayInWeek", Int64.Type}, {"DayOfWeekName", type text}, {"WeekEndingFriday", type date}, {"zFyYearSort", Int64.Type},{"FyYear", type text}, {"FyQuarter", type text}, {"FyMonth", Int64.Type}, {"KratosYear", Int64.Type}, {"KratosWeek", Int64.Type}, {"KratosWeekID", Int64.Type}, {"KratosWeekName", type text}, {"KratosWeekNameLong", type text}, {"KratosMonth", Int64.Type}, {"KratosQuarter", Int64.Type}, {"KratosMonthName", type text}}),

InFuture = Table.AddColumn(ChangedType1 , "Date in Future" , each if Number.Sign([Days from Today]) = -1 then true else false, type logical),

ReorderedColumns = Table.ReorderColumns(InFuture ,{"Date", "CALYear", "CALQuarterOfYear", "CALMonthOfYear", "DayOfMonth", "DateInt", "CALMonthName", "CALMonthNameShort", "CALMonthInCalendar", "CALQuarterInCalendar", "DayInWeek", "DayOfWeekName", "WeekEndingFriday", "zFyYearSort", "FyYear", "FyQuarter", "FyMonth", "FyYearQtr", "FyYearMonth", "FyYearMonthNum", "KratosYear", "KratosMonth", "KratosQuarter", "KratosWeek", "KratosMonthName", "KratosWeekID", "KratosWeekName", "KratosWeekNameLong"})

 

in

ReorderedColumns ,

    #"Invoked FunctionCreateDateTable" = CreateDateTable(#date(1997, 01, 01), #date(2030, 12, 31), "en-US"),

    #"Renamed Columns" = Table.RenameColumns(#"Invoked FunctionCreateDateTable",{{"Date", "Date (connect dates in data model)"}, {"KratosMonth", "KratosPeriod"}})

in

    #"Renamed Columns"

3 Replies

  • That is truly, uhm, impressive.

     

    Throw it away and instead maintain your fiscal calendar table in an outside tool like SQL Server or - worst case - an Excel file. Then load that data source into Power BI and mark it as a Dates table. Don't forget to disable the Auto Date/Time hierarchies.

    • janmack79's avatar
      janmack79
      Regular Visitor

      Hmm I am not familair wuth using SQL Server and my the whole point of this was to avoid worst case scenario of having to maintain a manual table that has to be updated 😕

      • lbendlin's avatar
        lbendlin
        Super User

        I know it is hard for people to learn from someone else's mistakes, but trust me on that. Having to maintain a manual table is NOT the worst case scenario in your situation.