Dynamic Calendar in Excel 365

Brass Contributor

Hi,

 

I create a dynamic array formula for projects at work. It takes project start & end dates and returns a calendar where its duration is based off of those two inputs. Please let me know if there's any errors I missed or it can be improved in any ways.

 

  • 3 inputs to define the calendar size: start_date & end_date (vertical) and mth_in_row_num (horizontal)
  • Format Cells: 'd;d;;@' to show date and text and hide 0
  • Conditional Formatting:
    • draw calendar borders
    • grey out Sat and Sun dates
    • highlight holidays in red
    • highlight milestone dates in corresponding colors
  • CA/US Holidays: drop-down list to switch holiday region
  • Holidays tab: refer to the attached excel file

Edit: The calendar formula and conditional formatting have been updated as @djclements pointed out the rows of calendar should be 6, not 5, to capture the missing 30th or 31st in some months.

 

dynamic calendar.PNG

format_cells.pngconditional_formatting.pngtab_holidays.png

 

=LET(
    start_date,$B$7,
    end_date,$B$15,
    mth_in_row_num,3,

    mth_num,(YEAR(end_date)-YEAR(start_date))*12+(MONTH(end_date)-MONTH(start_date))+1,
    mth_num_mult,CEILING.MATH(mth_num,mth_in_row_num),
    mth_num_div,mth_num_mult/mth_in_row_num,
    cal_col_num,7*mth_in_row_num,
    cal_horiz,DROP(
        REDUCE(0,SEQUENCE(mth_num_mult,,0),
            LAMBDA(a,v,HSTACK(a,
                LET(
                    mth_start,EOMONTH(start_date,v-1)+1,
                    cal_head,HSTACK(INDEX("",SEQUENCE(,3)^0),TEXT(mth_start,"mmm-yyyy"),INDEX("",SEQUENCE(,3)^0)),
                    cal_week,TEXT(SEQUENCE(,7),"ddd"),
                    cal_body,SEQUENCE(6,7,mth_start-WEEKDAY(mth_start)+1),
                    cal_body_filt,(MONTH(cal_body)=MONTH(mth_start))*cal_body,
                    VSTACK(cal_head,cal_week,cal_body_filt))))),
        ,1),

    DROP(
        REDUCE(0,SEQUENCE(mth_num_div,,0),
            LAMBDA(a,v,VSTACK(a,
                CHOOSECOLS(cal_horiz,SEQUENCE(cal_col_num,,1+cal_col_num*v))))),
        1)
)

 

4 Replies

@DaeyunPablo Looks pretty good. Just one comment for now... monthly calendars should contain 6 rows of data (not 5). Months beginning on a Friday or Saturday take up 6 weeks on the grid. There are a number of months on your sample calendar that are missing the last day or two (Mar-2024, Jun-2024, Mar-2025, Aug-2025, etc.). 😉

 

I'll work on an array manipulation alternative to the REDUCE / STACK method when I have some spare time. Cheers!

Thanks a lot for catching the error! I updated the attached file. I'll be waiting for an alternative to REDUCE/STACK method as well. There's a project I'm working on and and that method gets quite heavy if there are lots of inputs and the formula become complex. It's still great, but I'd like to try a new one if it's more efficient.

@DaeyunPablo Here's my array manipulation variant, using TOCOL, IF, SORTBY and WRAPROWS:

 

=LET(
    wrap_count, 3,
    start_date, MIN(B7:B15),
    end_date, MAX(B7:B15),
    months, CEILING.MATH((YEAR(end_date) - YEAR(start_date)) * 12 + MONTH(end_date) - MONTH(start_date) + 1, wrap_count),
    month_start, DATE(YEAR(start_date), MONTH(start_date) + SEQUENCE(months,, 0), 1),
    period_start, month_start - WEEKDAY(month_start) - 13,
    period_end, period_start + 55,
    seq_start, MIN(period_start),
    all_dates, SEQUENCE(, MAX(period_end) - seq_start + 1, seq_start),
    test, (period_start > all_dates) + (period_end < all_dates),
    period_dates, TOCOL(IF(test, NA(), all_dates), 2),
    first_dates, TOCOL(IF(test, NA(), month_start), 2),
    wrap_ID, INT(SEQUENCE(ROWS(period_dates),, 0) / (wrap_count * 56)),
    week_ID, TOCOL(IF(SEQUENCE(months), TOROW(IF(SEQUENCE(, 7), SEQUENCE(8))))),
    sort_ID, wrap_ID * 8 + week_ID,
    output, IF(week_ID = 1, IF(WEEKDAY(period_dates) = 4, TEXT(first_dates, "mmm-yyyy"), ""),
        IF(week_ID = 2, TEXT(period_dates, "ddd"), IF(MONTH(period_dates) = MONTH(first_dates), period_dates, 0))),
    WRAPROWS(SORTBY(output, sort_ID), wrap_count * 7)
)

 

Change the wrap_count if you want to output 2 or 4 months across, for example. I expanded the conditional formatting rules to accommodate up to 4 months across for demonstration purposes, but you could expand them further, if desired.

 

EDIT: after testing it with a larger date range (with the conditional formatting rules removed), it didn't hold up as well as I'd hoped. The REDUCE / STACK method was actually faster... so sad 😞

Unfortunate, I was hoping to see an alternative which can be used in general instead of REDUCE/STACK method... But nice version of yours, looking at it I updated my formula which has the 3rd input 'mth_in_row_num' to expand/shrink the calendar horizontally.

Please do let us know if you come up with a faster alternative later!