Forum Discussion

jaryszek's avatar
jaryszek
Icon for Super User rankSuper User
1 year ago
Solved

How to handle 53th week 445 calendar

Hi Guys, I am try to use this code: https://gorilla.bi/power-query/445-calendar/  let // ===== USER PARAMETERS ===== StartDate = #date(2024, 1, 29), // Enter your fiscal calenda...
  • Ahmedx's avatar
    1 year ago

    pls try

    let
        // ===== USER PARAMETERS =====
        StartDate = #date(2024, 1, 29),         // Enter your fiscal calendar start date
        EndDate   = #date(2030, 1, 25),         // Enter your fiscal calendar end date
        Order445  = {4, 4, 5},                  // Pattern for months in quarter: 4-4-5
    
        // ===== 53RD WEEK LOGIC =====
        // Function to determine if a fiscal year needs 53 weeks
        Needs53Weeks = (fiscalYearStart as date) as logical =>
            let
                // Method 1: Check if fiscal year start falls on Thursday, Friday, or Saturday
                startDayOfWeek = Date.DayOfWeek(fiscalYearStart, Day.Monday), // 0=Monday, 6=Sunday
                needsExtra = startDayOfWeek >= 3 and startDayOfWeek <= 5 // Thu, Fri, Sat
            in needsExtra,
    
        // Alternative method: Check if adding 364 days overshoots the next fiscal year start
        // This is more accurate for custom fiscal calendars
        Needs53WeeksAlt = (fiscalYearStart as date, nextFiscalYearStart as date) as logical =>
            let
                standardYearEnd = Date.AddDays(fiscalYearStart, 363), // 364 days - 1
                daysBetween = Duration.Days(nextFiscalYearStart - fiscalYearStart),
                needsExtra = daysBetween > 364
            in needsExtra,
    
        // ===== CREATE BASE DATE LIST =====
        Dates = List.Dates(StartDate, Duration.Days(EndDate-StartDate)+1, #duration(1,0,0,0)),
        Source = Table.FromList(Dates, Splitter.SplitByNothing(), {"Date"}),
        AddDayIndex = Table.AddIndexColumn(Source, "DayIndex", 0, 1, Int64.Type),
    
        // ===== FISCAL YEAR CALCULATION WITH 53RD WEEK =====
        BaseDaysInYear = List.Sum(Order445) * 4 * 7, // 364 days
        DaysInQuarter = List.Sum(Order445) * 7,       // 91 days
    
        // Determine fiscal year boundaries considering 53rd week
        AddFiscalYearInfo = Table.AddColumn(AddDayIndex, "FiscalYearInfo", 
            (row) =>
                let
                    currentDate = row[Date],
                    yearsSinceStart = Number.IntegerDivide(row[DayIndex], BaseDaysInYear),
                    
                    // Calculate this fiscal year's start
                    baseYearStart = Date.AddDays(StartDate, yearsSinceStart * BaseDaysInYear),
                    
                    // Check if this fiscal year needs 53 weeks
                    needs53 = Needs53Weeks(baseYearStart),
                    actualDaysInYear = if needs53 then BaseDaysInYear + 7 else BaseDaysInYear,
                    
                    // Recalculate year index with variable year lengths
                    adjustedYearIndex = 
                        let
                            totalDays = 0,
                            yearIndex = 1,
                            findYear = List.Generate(
                                () => [year = 1, cumulativeDays = 0],
                                each [cumulativeDays] <= row[DayIndex],
                                each 
                                    let
                                        yearStart = Date.AddDays(StartDate, [cumulativeDays]),
                                        yearNeeds53 = Needs53Weeks(yearStart),
                                        yearDays = if yearNeeds53 then BaseDaysInYear + 7 else BaseDaysInYear
                                    in [year = [year] + 1, cumulativeDays = [cumulativeDays] + yearDays]
                            ),
                            lastItem = List.Last(findYear)
                        in lastItem[year] - 1,
                    
                    fiscalYear = Date.Year(StartDate) + adjustedYearIndex - 1
                in
                    [
                        YearIndex = adjustedYearIndex,
                        FiscalYear = fiscalYear,
                        Has53Weeks = needs53,
                        DaysInThisYear = actualDaysInYear
                    ],
            type record
        ),
    
        // Extract fiscal year info
        AddYearIndex = Table.AddColumn(AddFiscalYearInfo, "YearIndex", each [FiscalYearInfo][YearIndex], Int64.Type),
        AddFiscalYear = Table.AddColumn(AddYearIndex, "FiscalYear", each [FiscalYearInfo][FiscalYear], Int64.Type),
        AddHas53Weeks = Table.AddColumn(AddFiscalYear, "Has53Weeks", each [FiscalYearInfo][Has53Weeks], Logical.Type),
        AddDaysInYear = Table.AddColumn(AddHas53Weeks, "DaysInThisYear", each [FiscalYearInfo][DaysInThisYear], Int64.Type),
    
        // Calculate day of fiscal year (1-364 or 1-371)
        AddDayOfYear = Table.AddColumn(AddDaysInYear, "DayOfYear", 
            (row) =>
                let
                    // Find the start of this fiscal year
                    yearStartDayIndex = 
                        let
                            findStart = List.Generate(
                                () => [year = 1, cumulativeDays = 0],
                                each [year] < row[YearIndex],
                                each 
                                    let
                                        yearStart = Date.AddDays(StartDate, [cumulativeDays]),
                                        yearNeeds53 = Needs53Weeks(yearStart),
                                        yearDays = if yearNeeds53 then BaseDaysInYear + 7 else BaseDaysInYear
                                    in [year = [year] + 1, cumulativeDays = [cumulativeDays] + yearDays]
                            ),
                            result = if List.Count(findStart) = 0 then 0 else List.Last(findStart)[cumulativeDays]
                        in result,
                    dayOfYear = row[DayIndex] - yearStartDayIndex + 1
                in dayOfYear,
            Int64.Type
        ),
    
        // ===== QUARTER AND PERIOD CALCULATION =====
        AddQuarterInfo = Table.AddColumn(AddDayOfYear, "QuarterInfo",
            (row) =>
                let
                    dayOfYear = row[DayOfYear],
                    has53Weeks = row[Has53Weeks],
                    
                    // Standard quarter calculation
                    quarterIndex = Number.IntegerDivide(dayOfYear-1, DaysInQuarter) + 1,
                    dayOfQuarter = Number.Mod(dayOfYear-1, DaysInQuarter) + 1,
                    
                    // Adjust for 53rd week (add to Q4)
                    adjustedQuarter = if has53Weeks and dayOfYear > (DaysInQuarter * 3) then
                        [
                            QuarterIndex = 4,
                            DayOfQuarter = dayOfYear - (DaysInQuarter * 3),
                            DaysInThisQuarter = DaysInQuarter + 7  // Q4 gets extra week
                        ]
                    else if quarterIndex > 4 then  // Safety check
                        [
                            QuarterIndex = 4,
                            DayOfQuarter = dayOfQuarter + DaysInQuarter,
                            DaysInThisQuarter = DaysInQuarter + 7
                        ]
                    else
                        [
                            QuarterIndex = quarterIndex,
                            DayOfQuarter = dayOfQuarter,
                            DaysInThisQuarter = if quarterIndex = 4 and has53Weeks then DaysInQuarter + 7 else DaysInQuarter
                        ]
                in adjustedQuarter,
            type record
        ),
    
        AddQuarterIndex = Table.AddColumn(AddQuarterInfo, "QuarterIndex", each [QuarterInfo][QuarterIndex], Int64.Type),
        AddDayOfQuarter = Table.AddColumn(AddQuarterIndex, "DayOfQuarter", each [QuarterInfo][DayOfQuarter], Int64.Type),
        AddDaysInQuarter = Table.AddColumn(AddDayOfQuarter, "DaysInThisQuarter", each [QuarterInfo][DaysInThisQuarter], Int64.Type),
    
        // ===== PERIOD CALCULATION WITH 53RD WEEK =====
        AddPeriodInfo = Table.AddColumn(AddDaysInQuarter, "PeriodInfo",
            (row) =>
                let
                    dayOfQuarter = row[DayOfQuarter],
                    quarterIndex = row[QuarterIndex],
                    has53Weeks = row[Has53Weeks],
                    order = Order445,
                    
                    // Calculate period within quarter
                    periodOfQuarter = 
                        if dayOfQuarter <= order{0} * 7 then 1
                        else if dayOfQuarter <= (order{0} + order{1}) * 7 then 2
                        else 3,
                    
                    // For 53rd week: add to last period of last quarter (Period 12)
                    isLastPeriodWith53 = quarterIndex = 4 and periodOfQuarter = 3 and has53Weeks,
                    
                    // Calculate days in this period
                    daysInPeriod = 
                        let
                            standardDays = order{periodOfQuarter-1} * 7
                        in
                            if isLastPeriodWith53 then standardDays + 7 else standardDays,
                    
                    // Calculate day of period
                    dayOfPeriod = 
                        let
                            offset = if periodOfQuarter = 1 then 0 
                                    else if periodOfQuarter = 2 then order{0} * 7 
                                    else (order{0} + order{1}) * 7
                        in dayOfQuarter - offset,
                    
                    // Period number in fiscal year
                    periodOfYear = (quarterIndex - 1) * 3 + periodOfQuarter
                in
                    [
                        PeriodOfQuarter = periodOfQuarter,
                        Period = periodOfYear,
                        DayOfPeriod = dayOfPeriod,
                        DaysInPeriod = daysInPeriod,
                        IsLeapPeriod = isLastPeriodWith53
                    ],
            type record
        ),
    
        AddPeriodOfQuarter = Table.AddColumn(AddPeriodInfo, "PeriodOfQuarter", each [PeriodInfo][PeriodOfQuarter], Int64.Type),
        AddPeriod = Table.AddColumn(AddPeriodOfQuarter, "Period", each [PeriodInfo][Period], Int64.Type),
        AddDayOfPeriod = Table.AddColumn(AddPeriod, "DayOfPeriod", each [PeriodInfo][DayOfPeriod], Int64.Type),
        AddDaysInPeriod = Table.AddColumn(AddDayOfPeriod, "DaysInPeriod", each [PeriodInfo][DaysInPeriod], Int64.Type),
        AddIsLeapPeriod = Table.AddColumn(AddDaysInPeriod, "IsLeapPeriod", each [PeriodInfo][IsLeapPeriod], Logical.Type),
    
        // ===== WEEK CALCULATIONS =====
        AddWeekIndex = Table.AddColumn(AddIsLeapPeriod, "WeekIndex", each Number.IntegerDivide([DayOfYear]-1, 7) + 1, Int64.Type),
        AddWeekOfQuarter = Table.AddColumn(AddWeekIndex, "WeekOfQuarter", each Number.Mod([WeekIndex]-1, if [Has53Weeks] and [QuarterIndex] = 4 then 14 else 13) + 1, Int64.Type),
        AddWeekOfPeriod = Table.AddColumn(AddWeekOfQuarter, "WeekOfPeriod", each Number.IntegerDivide([DayOfPeriod]-1, 7) + 1, Int64.Type),
    
        // ===== LABELS =====
        AddQuarterLabel = Table.AddColumn(AddWeekOfPeriod, "QuarterLabel", each "Q" & Text.From([QuarterIndex])),
        AddPeriodLabel = Table.AddColumn(AddQuarterLabel, "PeriodLabel", each "P" & Text.PadStart(Text.From([Period]),2,"0")),
        AddWeekLabel = Table.AddColumn(AddPeriodLabel, "WeekLabel", each "W" & Text.PadStart(Text.From([WeekIndex]),2,"0")),
    
        // ===== DATE RANGES =====
        // Week Start and End (assuming weeks start on Monday)
        AddWeekStart = Table.AddColumn(AddWeekLabel, "WeekStart", each Date.AddDays([Date], -Number.Mod(Date.DayOfWeek([Date], Day.Monday), 7))),
        AddWeekEnd = Table.AddColumn(AddWeekStart, "WeekEnd", each Date.AddDays([WeekStart], 6)),
    
        // Fiscal Year Start and End
        AddYearStart = Table.AddColumn(AddWeekEnd, "YearStart", each Date.AddDays([Date], -([DayOfYear]-1))),
        AddYearEnd = Table.AddColumn(AddYearStart, "YearEnd", each Date.AddDays([YearStart], [DaysInThisYear]-1)),
    
        // Fiscal Quarter Start and End
        AddQuarterStart = Table.AddColumn(AddYearEnd, "QuarterStart", each Date.AddDays([Date], -([DayOfQuarter]-1))),
        AddQuarterEnd = Table.AddColumn(AddQuarterStart, "QuarterEnd", each Date.AddDays([QuarterStart], [DaysInThisQuarter]-1)),
    
        // Period Start and End
        AddPeriodStart = Table.AddColumn(AddQuarterEnd, "PeriodStart", each Date.AddDays([Date], -([DayOfPeriod]-1))),
        AddPeriodEnd = Table.AddColumn(AddPeriodStart, "PeriodEnd", each Date.AddDays([PeriodStart], [DaysInPeriod]-1)),
    
        // Clean up and reorder columns
        RemoveHelperColumns = Table.RemoveColumns(AddPeriodEnd, {"DayIndex", "FiscalYearInfo", "QuarterInfo", "PeriodInfo"}),
        
        FinalTable = Table.SelectColumns(
            RemoveHelperColumns, 
            {
                "Date", "FiscalYear", "YearStart", "YearEnd", "Has53Weeks", "DaysInThisYear",
                "QuarterIndex", "QuarterLabel", "QuarterStart", "QuarterEnd", "DaysInThisQuarter",
                "Period", "PeriodLabel", "PeriodStart", "PeriodEnd", "DaysInPeriod", "IsLeapPeriod",
                "WeekIndex", "WeekLabel", "WeekOfQuarter", "WeekOfPeriod", "WeekStart", "WeekEnd",
                "DayOfYear", "DayOfQuarter", "DayOfPeriod"
            }
        )
    in
        FinalTable
  • lbendlin's avatar
    1 year ago

    Calendars are immutable. There is no point trying to do this in Power Query or DAX.  Use an external precomputed reference table.