[{"data":1,"prerenderedAt":2426},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas":2392,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas":2401,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas":2406,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas":2407},{"id":4,"title":5,"body":6,"dateModified":2358,"datePublished":2358,"description":2359,"extension":2360,"faq":2361,"keyword":2370,"kind":2371,"meta":2372,"navigation":99,"path":2381,"seo":2382,"slug":2385,"stem":2386,"topics":2387,"type":2390,"__hash__":2391},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas\u002Findex.md","Build a Date Table for Excel Reports with pandas",{"type":7,"value":8,"toc":2344},"minimark",[9,24,29,61,67,71,409,431,770,774,1037,1048,1052,1055,1281,1289,1293,1296,1475,1482,1663,1667,1670,1780,1787,1791,1798,2041,2067,2212,2216,2219,2223,2266,2270,2279,2283,2289,2295,2301,2307,2311,2340],[10,11,12,13,17,18,23],"p",{},"Sales data only has rows for days on which something was sold. Reports, though, need every day: a daily chart with gaps in it lies about the trend, a month with no sales on a Sunday should still show Sunday, and \"working days so far this month\" needs a calendar that knows about weekends and bank holidays. The standard answer in reporting tools is a ",[14,15,16],"em",{},"date table"," — one row per day, with every attribute a report might group or filter by already computed: year, quarter, month name, ISO week, fiscal period, weekend and holiday flags. pandas builds one in a few lines, and the same table can be written into the workbook for lookups, pivot tables and Power Pivot. This page belongs to ",[19,20,22],"a",{"href":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002F","Working with Dates and Times in Excel Data",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55,58],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," pandas",[40,56,57],{"class":50}," openpyxl",[40,59,60],{"class":50}," xlsxwriter\n",[10,62,63,66],{},[37,64,65],{},"daily_sales.xlsx"," has sales for ten days at the end of March and the first half of April 2026 — with gaps for weekends, the Easter holidays (Good Friday 3 April and Easter Monday 6 April are bank holidays in England in 2026) and one working day, 8 April, with no sales.",[25,68,70],{"id":69},"step-1-one-row-per-day","Step 1 — One row per day",[30,72,76],{"className":73,"code":74,"language":75,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndates = pd.DataFrame({\"Date\": pd.date_range(\"2026-01-01\", \"2026-12-31\", freq=\"D\")})\nd = dates[\"Date\"]\ndates[\"Year\"] = d.dt.year\ndates[\"Quarter\"] = \"Q\" + d.dt.quarter.astype(str)\ndates[\"Month\"] = d.dt.month\ndates[\"MonthName\"] = d.dt.strftime(\"%b\")\ndates[\"YearMonth\"] = d.dt.strftime(\"%Y-%m\")\ndates[\"Weekday\"] = d.dt.strftime(\"%a\")\ndates[\"WeekdayNum\"] = d.dt.weekday + 1                    # 1 = Monday\niso = d.dt.isocalendar()\ndates[\"ISOYear\"], dates[\"ISOWeek\"] = iso[\"year\"].astype(int), iso[\"week\"].astype(int)\nprint(dates.head(5).to_string(index=False))\nprint(len(dates), \"days\")\n","python",[37,77,78,94,101,142,158,175,203,218,244,263,286,311,322,364,390],{"__ignoreMap":35},[40,79,80,84,88,91],{"class":42,"line":43},[40,81,83],{"class":82},"s-kum","import",[40,85,87],{"class":86},"skGVy"," pandas ",[40,89,90],{"class":82},"as",[40,92,93],{"class":86}," pd\n",[40,95,97],{"class":42,"line":96},2,[40,98,100],{"emptyLinePlaceholder":99},true,"\n",[40,102,104,107,110,113,116,119,122,125,128,130,134,136,139],{"class":42,"line":103},3,[40,105,106],{"class":86},"dates ",[40,108,109],{"class":82},"=",[40,111,112],{"class":86}," pd.DataFrame({",[40,114,115],{"class":50},"\"Date\"",[40,117,118],{"class":86},": pd.date_range(",[40,120,121],{"class":50},"\"2026-01-01\"",[40,123,124],{"class":86},", ",[40,126,127],{"class":50},"\"2026-12-31\"",[40,129,124],{"class":86},[40,131,133],{"class":132},"sa561","freq",[40,135,109],{"class":82},[40,137,138],{"class":50},"\"D\"",[40,140,141],{"class":86},")})\n",[40,143,145,148,150,153,155],{"class":42,"line":144},4,[40,146,147],{"class":86},"d ",[40,149,109],{"class":82},[40,151,152],{"class":86}," dates[",[40,154,115],{"class":50},[40,156,157],{"class":86},"]\n",[40,159,161,164,167,170,172],{"class":42,"line":160},5,[40,162,163],{"class":86},"dates[",[40,165,166],{"class":50},"\"Year\"",[40,168,169],{"class":86},"] ",[40,171,109],{"class":82},[40,173,174],{"class":86}," d.dt.year\n",[40,176,178,180,183,185,187,190,193,196,200],{"class":42,"line":177},6,[40,179,163],{"class":86},[40,181,182],{"class":50},"\"Quarter\"",[40,184,169],{"class":86},[40,186,109],{"class":82},[40,188,189],{"class":50}," \"Q\"",[40,191,192],{"class":82}," +",[40,194,195],{"class":86}," d.dt.quarter.astype(",[40,197,199],{"class":198},"sP0c6","str",[40,201,202],{"class":86},")\n",[40,204,206,208,211,213,215],{"class":42,"line":205},7,[40,207,163],{"class":86},[40,209,210],{"class":50},"\"Month\"",[40,212,169],{"class":86},[40,214,109],{"class":82},[40,216,217],{"class":86}," d.dt.month\n",[40,219,221,223,226,228,230,233,236,240,242],{"class":42,"line":220},8,[40,222,163],{"class":86},[40,224,225],{"class":50},"\"MonthName\"",[40,227,169],{"class":86},[40,229,109],{"class":82},[40,231,232],{"class":86}," d.dt.strftime(",[40,234,235],{"class":50},"\"",[40,237,239],{"class":238},"sSjpA","%b",[40,241,235],{"class":50},[40,243,202],{"class":86},[40,245,247,249,252,254,256,258,261],{"class":42,"line":246},9,[40,248,163],{"class":86},[40,250,251],{"class":50},"\"YearMonth\"",[40,253,169],{"class":86},[40,255,109],{"class":82},[40,257,232],{"class":86},[40,259,260],{"class":50},"\"%Y-%m\"",[40,262,202],{"class":86},[40,264,266,268,271,273,275,277,279,282,284],{"class":42,"line":265},10,[40,267,163],{"class":86},[40,269,270],{"class":50},"\"Weekday\"",[40,272,169],{"class":86},[40,274,109],{"class":82},[40,276,232],{"class":86},[40,278,235],{"class":50},[40,280,281],{"class":238},"%a",[40,283,235],{"class":50},[40,285,202],{"class":86},[40,287,289,291,294,296,298,301,304,307],{"class":42,"line":288},11,[40,290,163],{"class":86},[40,292,293],{"class":50},"\"WeekdayNum\"",[40,295,169],{"class":86},[40,297,109],{"class":82},[40,299,300],{"class":86}," d.dt.weekday ",[40,302,303],{"class":82},"+",[40,305,306],{"class":198}," 1",[40,308,310],{"class":309},"s-wDw","                    # 1 = Monday\n",[40,312,314,317,319],{"class":42,"line":313},12,[40,315,316],{"class":86},"iso ",[40,318,109],{"class":82},[40,320,321],{"class":86}," d.dt.isocalendar()\n",[40,323,325,327,330,333,336,338,340,343,346,349,352,355,358,360,362],{"class":42,"line":324},13,[40,326,163],{"class":86},[40,328,329],{"class":50},"\"ISOYear\"",[40,331,332],{"class":86},"], dates[",[40,334,335],{"class":50},"\"ISOWeek\"",[40,337,169],{"class":86},[40,339,109],{"class":82},[40,341,342],{"class":86}," iso[",[40,344,345],{"class":50},"\"year\"",[40,347,348],{"class":86},"].astype(",[40,350,351],{"class":198},"int",[40,353,354],{"class":86},"), iso[",[40,356,357],{"class":50},"\"week\"",[40,359,348],{"class":86},[40,361,351],{"class":198},[40,363,202],{"class":86},[40,365,367,370,373,376,379,382,384,387],{"class":42,"line":366},14,[40,368,369],{"class":198},"print",[40,371,372],{"class":86},"(dates.head(",[40,374,375],{"class":198},"5",[40,377,378],{"class":86},").to_string(",[40,380,381],{"class":132},"index",[40,383,109],{"class":82},[40,385,386],{"class":198},"False",[40,388,389],{"class":86},"))\n",[40,391,393,395,398,401,404,407],{"class":42,"line":392},15,[40,394,369],{"class":198},[40,396,397],{"class":86},"(",[40,399,400],{"class":198},"len",[40,402,403],{"class":86},"(dates), ",[40,405,406],{"class":50},"\"days\"",[40,408,202],{"class":86},[10,410,411,414,415,418,419,422,423,426,427,430],{},[37,412,413],{},"date_range"," with daily frequency gives every date, gaps included. Each attribute is a vectorised ",[37,416,417],{},".dt"," accessor. Two details are easy to get wrong. ISO weeks belong to an ISO ",[14,420,421],{},"year"," that differs from the calendar year around New Year — 1 January 2026 is a Thursday in ISO week 1 of 2026, but 1 January 2027 falls in week 53 of 2026 — so keep ",[37,424,425],{},"ISOYear"," next to ",[37,428,429],{},"ISOWeek"," and group by both. And month names sort alphabetically in pivots unless a month number travels with them, which is why both columns are there.",[432,433,441,442,441,446,441,450,441,458,441,467,441,471,441,478,441,482,441,486,441,490,441,494,441,498,441,502,441,506,441,510,441,514,441,518,441,522,441,526,441,530,441,535,441,540,441,544,441,546,441,549,441,551,441,554,441,556,441,559,441,561,441,564,441,566,441,569,441,571,441,574,441,577,441,581,441,583,441,587,441,589,441,592,441,594,441,597,441,599,441,602,441,604,441,607,441,609,441,611,441,615,441,619,441,622,441,626,441,628,441,631,441,633,441,635,441,637,441,639,441,641,441,643,441,645,441,648,441,650,441,652,441,654,441,657,441,660,441,664,441,666,441,669,441,671,441,673,441,675,441,677,441,679,441,681,441,683,441,686,441,688,441,690,441,694,441,697,441,700,441,703,441,705,441,708,441,710,441,712,441,714,441,716,441,718,441,720,441,722,441,725,441,727,441,729,441,731,441,733,441,736,441,740,441,742,441,745,441,747,441,749,441,751,441,753,441,755,441,757,441,759,441,762,441,764,441,766,441,768],"svg",{"viewBox":434,"role":435,"ariaLabelledBy":436,"xmlns":439,"style":440},"84 0 592 224","img",[437,438],"dt-table-t","dt-table-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[443,444,445],"title",{"id":437},"The first rows of the date table",[447,448,449],"desc",{"id":438},"A sheet with columns Date, Year, Quarter, MonthName, Weekday, ISOWeek and IsWorkingDay. 1 January 2026 is a Thursday in ISO week 1 and a public holiday, so not a working day; 2 January is a working Friday; 3 and 4 January are weekend days.",[451,452],"rect",{"x":453,"y":454,"width":455,"height":456,"fill":457},"84","0","592","224","#ffffff",[451,459],{"x":460,"y":461,"width":462,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"100.4","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[451,468],{"x":469,"y":461,"width":470,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"132.4","95.6",[472,473,477],"text",{"x":474,"y":475,"style":476},"180.2","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[451,479],{"x":480,"y":461,"width":481,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"228.0","53.8",[472,483,485],{"x":484,"y":475,"style":476},"254.9","B",[451,487],{"x":488,"y":461,"width":489,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"281.8","67.4",[472,491,493],{"x":492,"y":475,"style":476},"315.5","C",[451,495],{"x":496,"y":461,"width":497,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"349.2","60.5",[472,499,501],{"x":500,"y":475,"style":476},"379.4","D",[451,503],{"x":504,"y":461,"width":505,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"409.7","77.4",[472,507,509],{"x":508,"y":475,"style":476},"448.4","E",[451,511],{"x":512,"y":461,"width":513,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"487.1","76.7",[472,515,517],{"x":516,"y":475,"style":476},"525.4","F",[451,519],{"x":520,"y":461,"width":521,"height":463,"rx":454,"fill":464,"stroke":465,"style":466},"563.8","95.8",[472,523,525],{"x":524,"y":475,"style":476},"611.7","G",[451,527],{"x":460,"y":528,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"40.0","28.0",[472,531,534],{"x":532,"y":533,"style":476},"116.4","59.0","1",[451,536],{"x":469,"y":528,"width":470,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[472,541,543],{"x":474,"y":533,"style":542},"font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Date",[451,545],{"x":480,"y":528,"width":481,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,547,548],{"x":484,"y":533,"style":542},"Year",[451,550],{"x":488,"y":528,"width":489,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,552,553],{"x":492,"y":533,"style":542},"Quarter",[451,555],{"x":496,"y":528,"width":497,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,557,558],{"x":500,"y":533,"style":542},"Month",[451,560],{"x":504,"y":528,"width":505,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,562,563],{"x":508,"y":533,"style":542},"Weekday",[451,565],{"x":512,"y":528,"width":513,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,567,568],{"x":516,"y":533,"style":542},"ISO week",[451,570],{"x":520,"y":528,"width":521,"height":529,"rx":454,"fill":537,"stroke":538,"style":539},[472,572,573],{"x":524,"y":533,"style":542},"Working day",[451,575],{"x":460,"y":576,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"68.0",[472,578,580],{"x":532,"y":579,"style":476},"87.0","2",[451,582],{"x":469,"y":576,"width":470,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,584,586],{"x":474,"y":579,"style":585},"font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","01 Jan 2026",[451,588],{"x":480,"y":576,"width":481,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,590,591],{"x":484,"y":579,"style":585},"2026",[451,593],{"x":488,"y":576,"width":489,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,595,596],{"x":492,"y":579,"style":585},"Q1",[451,598],{"x":496,"y":576,"width":497,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,600,601],{"x":500,"y":579,"style":585},"Jan",[451,603],{"x":504,"y":576,"width":505,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,605,606],{"x":508,"y":579,"style":585},"Thu",[451,608],{"x":512,"y":576,"width":513,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,610,534],{"x":516,"y":579,"style":585},[451,612],{"x":520,"y":576,"width":521,"height":529,"rx":454,"fill":613,"stroke":614,"style":539},"#fce9e9","var(--danger,#dc2626)",[472,616,618],{"x":524,"y":579,"style":617},"font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","FALSE",[451,620],{"x":460,"y":621,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"96.0",[472,623,625],{"x":532,"y":624,"style":476},"115.0","3",[451,627],{"x":469,"y":621,"width":470,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,629,630],{"x":474,"y":624,"style":585},"02 Jan 2026",[451,632],{"x":480,"y":621,"width":481,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,634,591],{"x":484,"y":624,"style":585},[451,636],{"x":488,"y":621,"width":489,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,638,596],{"x":492,"y":624,"style":585},[451,640],{"x":496,"y":621,"width":497,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,642,601],{"x":500,"y":624,"style":585},[451,644],{"x":504,"y":621,"width":505,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,646,647],{"x":508,"y":624,"style":585},"Fri",[451,649],{"x":512,"y":621,"width":513,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,651,534],{"x":516,"y":624,"style":585},[451,653],{"x":520,"y":621,"width":521,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,655,656],{"x":524,"y":624,"style":585},"TRUE",[451,658],{"x":460,"y":659,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"124.0",[472,661,663],{"x":532,"y":662,"style":476},"143.0","4",[451,665],{"x":469,"y":659,"width":470,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,667,668],{"x":474,"y":662,"style":585},"03 Jan 2026",[451,670],{"x":480,"y":659,"width":481,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,672,591],{"x":484,"y":662,"style":585},[451,674],{"x":488,"y":659,"width":489,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,676,596],{"x":492,"y":662,"style":585},[451,678],{"x":496,"y":659,"width":497,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,680,601],{"x":500,"y":662,"style":585},[451,682],{"x":504,"y":659,"width":505,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,684,685],{"x":508,"y":662,"style":585},"Sat",[451,687],{"x":512,"y":659,"width":513,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,689,534],{"x":516,"y":662,"style":585},[451,691],{"x":520,"y":659,"width":521,"height":529,"rx":454,"fill":692,"stroke":693,"style":539},"#fdefd8","var(--gold,#b4740a)",[472,695,618],{"x":524,"y":662,"style":696},"font-size:11px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[451,698],{"x":460,"y":699,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"152.0",[472,701,375],{"x":532,"y":702,"style":476},"171.0",[451,704],{"x":469,"y":699,"width":470,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,706,707],{"x":474,"y":702,"style":585},"04 Jan 2026",[451,709],{"x":480,"y":699,"width":481,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,711,591],{"x":484,"y":702,"style":585},[451,713],{"x":488,"y":699,"width":489,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,715,596],{"x":492,"y":702,"style":585},[451,717],{"x":496,"y":699,"width":497,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,719,601],{"x":500,"y":702,"style":585},[451,721],{"x":504,"y":699,"width":505,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,723,724],{"x":508,"y":702,"style":585},"Sun",[451,726],{"x":512,"y":699,"width":513,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,728,534],{"x":516,"y":702,"style":585},[451,730],{"x":520,"y":699,"width":521,"height":529,"rx":454,"fill":692,"stroke":693,"style":539},[472,732,618],{"x":524,"y":702,"style":696},[451,734],{"x":460,"y":735,"width":462,"height":529,"rx":454,"fill":464,"stroke":465,"style":466},"180.0",[472,737,739],{"x":532,"y":738,"style":476},"199.0","6",[451,741],{"x":469,"y":735,"width":470,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,743,744],{"x":474,"y":738,"style":585},"05 Jan 2026",[451,746],{"x":480,"y":735,"width":481,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,748,591],{"x":484,"y":738,"style":585},[451,750],{"x":488,"y":735,"width":489,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,752,596],{"x":492,"y":738,"style":585},[451,754],{"x":496,"y":735,"width":497,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,756,601],{"x":500,"y":738,"style":585},[451,758],{"x":504,"y":735,"width":505,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,760,761],{"x":508,"y":738,"style":585},"Mon",[451,763],{"x":512,"y":735,"width":513,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,765,580],{"x":516,"y":738,"style":585},[451,767],{"x":520,"y":735,"width":521,"height":529,"rx":454,"fill":457,"stroke":465,"style":466},[472,769,656],{"x":524,"y":738,"style":585},[25,771,773],{"id":772},"step-2-weekends-holidays-and-working-days","Step 2 — Weekends, holidays and working days",[30,775,777],{"className":73,"code":776,"language":75,"meta":35,"style":35},"HOLIDAYS = pd.to_datetime([\"2026-01-01\", \"2026-04-03\", \"2026-04-06\", \"2026-05-04\", \"2026-05-25\",\n                           \"2026-08-31\", \"2026-12-25\", \"2026-12-28\"])     # England & Wales, 2026\ndates[\"IsWeekend\"] = dates[\"WeekdayNum\"] >= 6\ndates[\"IsHoliday\"] = dates[\"Date\"].isin(HOLIDAYS)\ndates[\"IsWorkingDay\"] = ~dates[\"IsWeekend\"] & ~dates[\"IsHoliday\"]\ndates[\"WorkingDayOfMonth\"] = (dates[\"IsWorkingDay\"].astype(int)\n                              .groupby(dates[\"YearMonth\"]).cumsum()\n                              .where(dates[\"IsWorkingDay\"]))\ndates[\"WorkingDaysInMonth\"] = dates.groupby(\"YearMonth\")[\"IsWorkingDay\"].transform(\"sum\")\nprint(dates.loc[dates[\"Month\"] == 4, [\"Date\", \"Weekday\", \"IsHoliday\", \"IsWorkingDay\", \"WorkingDayOfMonth\"]].head(9).to_string(index=False))\n",[37,778,779,815,836,859,881,912,934,944,954,983],{"__ignoreMap":35},[40,780,781,784,787,790,792,794,797,799,802,804,807,809,812],{"class":42,"line":43},[40,782,783],{"class":198},"HOLIDAYS",[40,785,786],{"class":82}," =",[40,788,789],{"class":86}," pd.to_datetime([",[40,791,121],{"class":50},[40,793,124],{"class":86},[40,795,796],{"class":50},"\"2026-04-03\"",[40,798,124],{"class":86},[40,800,801],{"class":50},"\"2026-04-06\"",[40,803,124],{"class":86},[40,805,806],{"class":50},"\"2026-05-04\"",[40,808,124],{"class":86},[40,810,811],{"class":50},"\"2026-05-25\"",[40,813,814],{"class":86},",\n",[40,816,817,820,822,825,827,830,833],{"class":42,"line":96},[40,818,819],{"class":50},"                           \"2026-08-31\"",[40,821,124],{"class":86},[40,823,824],{"class":50},"\"2026-12-25\"",[40,826,124],{"class":86},[40,828,829],{"class":50},"\"2026-12-28\"",[40,831,832],{"class":86},"])     ",[40,834,835],{"class":309},"# England & Wales, 2026\n",[40,837,838,840,843,845,847,849,851,853,856],{"class":42,"line":103},[40,839,163],{"class":86},[40,841,842],{"class":50},"\"IsWeekend\"",[40,844,169],{"class":86},[40,846,109],{"class":82},[40,848,152],{"class":86},[40,850,293],{"class":50},[40,852,169],{"class":86},[40,854,855],{"class":82},">=",[40,857,858],{"class":198}," 6\n",[40,860,861,863,866,868,870,872,874,877,879],{"class":42,"line":144},[40,862,163],{"class":86},[40,864,865],{"class":50},"\"IsHoliday\"",[40,867,169],{"class":86},[40,869,109],{"class":82},[40,871,152],{"class":86},[40,873,115],{"class":50},[40,875,876],{"class":86},"].isin(",[40,878,783],{"class":198},[40,880,202],{"class":86},[40,882,883,885,888,890,892,895,897,899,901,904,906,908,910],{"class":42,"line":160},[40,884,163],{"class":86},[40,886,887],{"class":50},"\"IsWorkingDay\"",[40,889,169],{"class":86},[40,891,109],{"class":82},[40,893,894],{"class":82}," ~",[40,896,163],{"class":86},[40,898,842],{"class":50},[40,900,169],{"class":86},[40,902,903],{"class":82},"&",[40,905,894],{"class":82},[40,907,163],{"class":86},[40,909,865],{"class":50},[40,911,157],{"class":86},[40,913,914,916,919,921,923,926,928,930,932],{"class":42,"line":177},[40,915,163],{"class":86},[40,917,918],{"class":50},"\"WorkingDayOfMonth\"",[40,920,169],{"class":86},[40,922,109],{"class":82},[40,924,925],{"class":86}," (dates[",[40,927,887],{"class":50},[40,929,348],{"class":86},[40,931,351],{"class":198},[40,933,202],{"class":86},[40,935,936,939,941],{"class":42,"line":205},[40,937,938],{"class":86},"                              .groupby(dates[",[40,940,251],{"class":50},[40,942,943],{"class":86},"]).cumsum()\n",[40,945,946,949,951],{"class":42,"line":220},[40,947,948],{"class":86},"                              .where(dates[",[40,950,887],{"class":50},[40,952,953],{"class":86},"]))\n",[40,955,956,958,961,963,965,968,970,973,975,978,981],{"class":42,"line":246},[40,957,163],{"class":86},[40,959,960],{"class":50},"\"WorkingDaysInMonth\"",[40,962,169],{"class":86},[40,964,109],{"class":82},[40,966,967],{"class":86}," dates.groupby(",[40,969,251],{"class":50},[40,971,972],{"class":86},")[",[40,974,887],{"class":50},[40,976,977],{"class":86},"].transform(",[40,979,980],{"class":50},"\"sum\"",[40,982,202],{"class":86},[40,984,985,987,990,992,994,997,1000,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1024,1027,1029,1031,1033,1035],{"class":42,"line":265},[40,986,369],{"class":198},[40,988,989],{"class":86},"(dates.loc[dates[",[40,991,210],{"class":50},[40,993,169],{"class":86},[40,995,996],{"class":82},"==",[40,998,999],{"class":198}," 4",[40,1001,1002],{"class":86},", [",[40,1004,115],{"class":50},[40,1006,124],{"class":86},[40,1008,270],{"class":50},[40,1010,124],{"class":86},[40,1012,865],{"class":50},[40,1014,124],{"class":86},[40,1016,887],{"class":50},[40,1018,124],{"class":86},[40,1020,918],{"class":50},[40,1022,1023],{"class":86},"]].head(",[40,1025,1026],{"class":198},"9",[40,1028,378],{"class":86},[40,1030,381],{"class":132},[40,1032,109],{"class":82},[40,1034,386],{"class":198},[40,1036,389],{"class":86},[10,1038,1039,1040,1043,1044,23],{},"The holiday list is data, not logic: keep it in a sheet or file per country and load it, because rules such as \"a Christmas Day that falls on Saturday moves to Monday\" are exactly what an official list already resolves (25 December 2026 is a Friday, so the Boxing Day holiday moves to Monday 28th). ",[37,1041,1042],{},"WorkingDayOfMonth"," numbers each working day 1, 2, 3 within its month and leaves non-working days empty; April 2026 starts on a Wednesday, so 1 April is working day 1, Good Friday and Easter Monday are skipped, and Tuesday 7 April is only working day 3. Business-day arithmetic between two dates is covered in ",[19,1045,1047],{"href":1046},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-business-days-between-excel-dates-with-pandas\u002F","Calculate Business Days Between Excel Dates with pandas",[25,1049,1051],{"id":1050},"step-3-fiscal-year-and-period","Step 3 — Fiscal year and period",[10,1053,1054],{},"Many organisations run an April–March year. With the start month as a parameter, fiscal year and period are arithmetic on the month:",[30,1056,1058],{"className":73,"code":1057,"language":75,"meta":35,"style":35},"FY_START = 4                                           # April\nshifted = (dates[\"Month\"] - FY_START) % 12             # 0 = first fiscal month\ndates[\"FiscalYear\"] = \"FY\" + (dates[\"Year\"] + (dates[\"Month\"] >= FY_START).astype(int)).astype(str).str[-2:]\ndates[\"FiscalPeriod\"] = \"P\" + (shifted + 1).astype(str).str.zfill(2)\ndates[\"FiscalQuarter\"] = \"FQ\" + (shifted \u002F\u002F 3 + 1).astype(str)\nprint(dates.loc[dates[\"Date\"].isin(pd.to_datetime([\"2026-03-31\", \"2026-04-01\", \"2026-12-31\"])),\n                [\"Date\", \"FiscalYear\", \"FiscalPeriod\", \"FiscalQuarter\"]].to_string(index=False))\n",[37,1059,1060,1072,1103,1157,1191,1225,1251],{"__ignoreMap":35},[40,1061,1062,1065,1067,1069],{"class":42,"line":43},[40,1063,1064],{"class":198},"FY_START",[40,1066,786],{"class":82},[40,1068,999],{"class":198},[40,1070,1071],{"class":309},"                                           # April\n",[40,1073,1074,1077,1079,1081,1083,1085,1088,1091,1094,1097,1100],{"class":42,"line":96},[40,1075,1076],{"class":86},"shifted ",[40,1078,109],{"class":82},[40,1080,925],{"class":86},[40,1082,210],{"class":50},[40,1084,169],{"class":86},[40,1086,1087],{"class":82},"-",[40,1089,1090],{"class":198}," FY_START",[40,1092,1093],{"class":86},") ",[40,1095,1096],{"class":82},"%",[40,1098,1099],{"class":198}," 12",[40,1101,1102],{"class":309},"             # 0 = first fiscal month\n",[40,1104,1105,1107,1110,1112,1114,1117,1119,1121,1123,1125,1127,1129,1131,1133,1135,1137,1140,1142,1145,1147,1150,1152,1154],{"class":42,"line":103},[40,1106,163],{"class":86},[40,1108,1109],{"class":50},"\"FiscalYear\"",[40,1111,169],{"class":86},[40,1113,109],{"class":82},[40,1115,1116],{"class":50}," \"FY\"",[40,1118,192],{"class":82},[40,1120,925],{"class":86},[40,1122,166],{"class":50},[40,1124,169],{"class":86},[40,1126,303],{"class":82},[40,1128,925],{"class":86},[40,1130,210],{"class":50},[40,1132,169],{"class":86},[40,1134,855],{"class":82},[40,1136,1090],{"class":198},[40,1138,1139],{"class":86},").astype(",[40,1141,351],{"class":198},[40,1143,1144],{"class":86},")).astype(",[40,1146,199],{"class":198},[40,1148,1149],{"class":86},").str[",[40,1151,1087],{"class":82},[40,1153,580],{"class":198},[40,1155,1156],{"class":86},":]\n",[40,1158,1159,1161,1164,1166,1168,1171,1173,1176,1178,1180,1182,1184,1187,1189],{"class":42,"line":144},[40,1160,163],{"class":86},[40,1162,1163],{"class":50},"\"FiscalPeriod\"",[40,1165,169],{"class":86},[40,1167,109],{"class":82},[40,1169,1170],{"class":50}," \"P\"",[40,1172,192],{"class":82},[40,1174,1175],{"class":86}," (shifted ",[40,1177,303],{"class":82},[40,1179,306],{"class":198},[40,1181,1139],{"class":86},[40,1183,199],{"class":198},[40,1185,1186],{"class":86},").str.zfill(",[40,1188,580],{"class":198},[40,1190,202],{"class":86},[40,1192,1193,1195,1198,1200,1202,1205,1207,1209,1212,1215,1217,1219,1221,1223],{"class":42,"line":160},[40,1194,163],{"class":86},[40,1196,1197],{"class":50},"\"FiscalQuarter\"",[40,1199,169],{"class":86},[40,1201,109],{"class":82},[40,1203,1204],{"class":50}," \"FQ\"",[40,1206,192],{"class":82},[40,1208,1175],{"class":86},[40,1210,1211],{"class":82},"\u002F\u002F",[40,1213,1214],{"class":198}," 3",[40,1216,192],{"class":82},[40,1218,306],{"class":198},[40,1220,1139],{"class":86},[40,1222,199],{"class":198},[40,1224,202],{"class":86},[40,1226,1227,1229,1231,1233,1236,1239,1241,1244,1246,1248],{"class":42,"line":177},[40,1228,369],{"class":198},[40,1230,989],{"class":86},[40,1232,115],{"class":50},[40,1234,1235],{"class":86},"].isin(pd.to_datetime([",[40,1237,1238],{"class":50},"\"2026-03-31\"",[40,1240,124],{"class":86},[40,1242,1243],{"class":50},"\"2026-04-01\"",[40,1245,124],{"class":86},[40,1247,127],{"class":50},[40,1249,1250],{"class":86},"])),\n",[40,1252,1253,1256,1258,1260,1262,1264,1266,1268,1270,1273,1275,1277,1279],{"class":42,"line":205},[40,1254,1255],{"class":86},"                [",[40,1257,115],{"class":50},[40,1259,124],{"class":86},[40,1261,1109],{"class":50},[40,1263,124],{"class":86},[40,1265,1163],{"class":50},[40,1267,124],{"class":86},[40,1269,1197],{"class":50},[40,1271,1272],{"class":86},"]].to_string(",[40,1274,381],{"class":132},[40,1276,109],{"class":82},[40,1278,386],{"class":198},[40,1280,389],{"class":86},[10,1282,1283,1284,1288],{},"31 March 2026 is the last day of FY26 (period 12); 1 April 2026 starts FY27 in period 1. Naming conventions differ — some firms call the April 2026 – March 2027 year \"FY26\" after its starting year — so make the label rule explicit and agree it with finance once. Grouping by these columns replaces date arithmetic scattered through report code; see ",[19,1285,1287],{"href":1286},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fgroup-excel-data-by-fiscal-year-and-iso-week\u002F","Group Excel Data by Fiscal Year and ISO Week"," for the grouping side.",[25,1290,1292],{"id":1291},"step-4-fill-the-gaps-in-the-sales-data","Step 4 — Fill the gaps in the sales data",[10,1294,1295],{},"Joining the sales to the date table gives a row for every day, with zero where nothing was sold:",[30,1297,1299],{"className":73,"code":1298,"language":75,"meta":35,"style":35},"sales = pd.read_excel(\"daily_sales.xlsx\")\ndaily = dates.merge(sales, on=\"Date\", how=\"left\").fillna({\"Sales\": 0})\nwindow = daily[(daily[\"Date\"] >= \"2026-03-30\") & (daily[\"Date\"] \u003C= \"2026-04-14\")]\nprint(window[[\"Date\", \"Weekday\", \"IsWorkingDay\", \"Sales\"]].to_string(index=False))\nprint(\"average per working day in April:\",\n      round(daily.loc[(daily[\"Month\"] == 4) & daily[\"IsWorkingDay\"], \"Sales\"].mean(), 1))\n",[37,1300,1301,1316,1357,1396,1427,1438],{"__ignoreMap":35},[40,1302,1303,1306,1308,1311,1314],{"class":42,"line":43},[40,1304,1305],{"class":86},"sales ",[40,1307,109],{"class":82},[40,1309,1310],{"class":86}," pd.read_excel(",[40,1312,1313],{"class":50},"\"daily_sales.xlsx\"",[40,1315,202],{"class":86},[40,1317,1318,1321,1323,1326,1329,1331,1333,1335,1338,1340,1343,1346,1349,1352,1354],{"class":42,"line":96},[40,1319,1320],{"class":86},"daily ",[40,1322,109],{"class":82},[40,1324,1325],{"class":86}," dates.merge(sales, ",[40,1327,1328],{"class":132},"on",[40,1330,109],{"class":82},[40,1332,115],{"class":50},[40,1334,124],{"class":86},[40,1336,1337],{"class":132},"how",[40,1339,109],{"class":82},[40,1341,1342],{"class":50},"\"left\"",[40,1344,1345],{"class":86},").fillna({",[40,1347,1348],{"class":50},"\"Sales\"",[40,1350,1351],{"class":86},": ",[40,1353,454],{"class":198},[40,1355,1356],{"class":86},"})\n",[40,1358,1359,1362,1364,1367,1369,1371,1373,1376,1378,1380,1383,1385,1387,1390,1393],{"class":42,"line":103},[40,1360,1361],{"class":86},"window ",[40,1363,109],{"class":82},[40,1365,1366],{"class":86}," daily[(daily[",[40,1368,115],{"class":50},[40,1370,169],{"class":86},[40,1372,855],{"class":82},[40,1374,1375],{"class":50}," \"2026-03-30\"",[40,1377,1093],{"class":86},[40,1379,903],{"class":82},[40,1381,1382],{"class":86}," (daily[",[40,1384,115],{"class":50},[40,1386,169],{"class":86},[40,1388,1389],{"class":82},"\u003C=",[40,1391,1392],{"class":50}," \"2026-04-14\"",[40,1394,1395],{"class":86},")]\n",[40,1397,1398,1400,1403,1405,1407,1409,1411,1413,1415,1417,1419,1421,1423,1425],{"class":42,"line":144},[40,1399,369],{"class":198},[40,1401,1402],{"class":86},"(window[[",[40,1404,115],{"class":50},[40,1406,124],{"class":86},[40,1408,270],{"class":50},[40,1410,124],{"class":86},[40,1412,887],{"class":50},[40,1414,124],{"class":86},[40,1416,1348],{"class":50},[40,1418,1272],{"class":86},[40,1420,381],{"class":132},[40,1422,109],{"class":82},[40,1424,386],{"class":198},[40,1426,389],{"class":86},[40,1428,1429,1431,1433,1436],{"class":42,"line":160},[40,1430,369],{"class":198},[40,1432,397],{"class":86},[40,1434,1435],{"class":50},"\"average per working day in April:\"",[40,1437,814],{"class":86},[40,1439,1440,1443,1446,1448,1450,1452,1454,1456,1458,1461,1463,1466,1468,1471,1473],{"class":42,"line":177},[40,1441,1442],{"class":198},"      round",[40,1444,1445],{"class":86},"(daily.loc[(daily[",[40,1447,210],{"class":50},[40,1449,169],{"class":86},[40,1451,996],{"class":82},[40,1453,999],{"class":198},[40,1455,1093],{"class":86},[40,1457,903],{"class":82},[40,1459,1460],{"class":86}," daily[",[40,1462,887],{"class":50},[40,1464,1465],{"class":86},"], ",[40,1467,1348],{"class":50},[40,1469,1470],{"class":86},"].mean(), ",[40,1472,534],{"class":198},[40,1474,389],{"class":86},[10,1476,1477,1478,23],{},"The left merge keeps every date and fills missing sales with 0. Now the averages mean something: averaging only the days that had sales overstates performance, and averaging every calendar day understates it on weekends. Dividing by working days — a column the date table already has — answers the question managers actually ask. Filling missing dates without a full date table is shown in ",[19,1479,1481],{"href":1480},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas\u002F","Fill Missing Dates in a Time Series from Excel with pandas",[432,1483,441,1489,441,1492,441,1495,441,1499,441,1504,441,1509,441,1513,441,1517,441,1520,441,1524,441,1526,441,1529,441,1532,441,1538,441,1542,441,1545,441,1548,441,1551,441,1554,441,1557,441,1560,441,1564,441,1568,441,1571,441,1575,441,1579,441,1583,441,1587,441,1591,441,1596,441,1601,441,1606,441,1611,441,1615,441,1618,441,1621,441,1624,441,1629,441,1632,441,1637,441,1641,441,1644,441,1647,441,1652,441,1657],{"viewBox":1484,"role":435,"ariaLabelledBy":1485,"xmlns":439,"style":1488},"4 0 748 278",[1486,1487],"dt-gaps-t","dt-gaps-d","width:100%;max-width:748px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[443,1490,1491],{"id":1486},"Daily sales before and after joining the date table",[447,1493,1494],{"id":1487},"Sales from 30 March to 14 April 2026 shown for every day. Weekends and the Good Friday and Easter Monday holidays appear as zero instead of being missing, so the chart shows the real rhythm of trading days.",[451,1496],{"x":663,"y":454,"width":1497,"height":1498,"fill":457},"748","278",[42,1500],{"x1":1501,"y1":1502,"x2":1503,"y2":1502,"stroke":465,"style":466},"53.4","214.0","736.0",[472,1505,454],{"x":1506,"y":1507,"style":1508},"30.7","218.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[42,1510],{"x1":1501,"y1":1511,"x2":1503,"y2":1511,"stroke":465,"style":1512},"150.7","stroke-width:1px;stroke-dasharray:5 4",[472,1514,1516],{"x":1506,"y":1515,"style":1508},"154.7","500",[42,1518],{"x1":1501,"y1":1519,"x2":1503,"y2":1519,"stroke":465,"style":1512},"87.3",[472,1521,1523],{"x":1506,"y":1522,"style":1508},"91.3","1k",[42,1525],{"x1":1501,"y1":463,"x2":1503,"y2":463,"stroke":465,"style":1512},[472,1527,1528],{"x":1506,"y":529,"style":1508},"1.5k",[42,1530],{"x1":1501,"y1":1502,"x2":1503,"y2":1502,"stroke":1531,"style":539},"var(--muted,#5b6780)",[472,1533,1537],{"x":1534,"y":1535,"style":1536},"74.8","232.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","30",[472,1539,1541],{"x":1540,"y":1535,"style":1536},"117.4","31",[472,1543,534],{"x":1544,"y":1535,"style":1536},"160.1",[472,1546,580],{"x":1547,"y":1535,"style":1536},"202.7",[472,1549,625],{"x":1550,"y":1535,"style":1536},"245.4",[472,1552,663],{"x":1553,"y":1535,"style":1536},"288.1",[472,1555,375],{"x":1556,"y":1535,"style":1536},"330.7",[472,1558,739],{"x":1559,"y":1535,"style":1536},"373.4",[472,1561,1563],{"x":1562,"y":1535,"style":1536},"416.0","7",[472,1565,1567],{"x":1566,"y":1535,"style":1536},"458.7","8",[472,1569,1026],{"x":1570,"y":1535,"style":1536},"501.4",[472,1572,1574],{"x":1573,"y":1535,"style":1536},"544.0","10",[472,1576,1578],{"x":1577,"y":1535,"style":1536},"586.7","11",[472,1580,1582],{"x":1581,"y":1535,"style":1536},"629.3","12",[472,1584,1586],{"x":1585,"y":1535,"style":1536},"672.0","13",[472,1588,1590],{"x":1589,"y":1535,"style":1536},"714.7","14",[451,1592],{"x":1593,"y":1594,"width":1595,"height":699,"rx":625,"fill":537,"stroke":538,"style":539},"60.8","62.0","27.9",[451,1597],{"x":1598,"y":1599,"width":1595,"height":1600,"rx":625,"fill":537,"stroke":538,"style":539},"103.5","93.7","120.3",[451,1602],{"x":1603,"y":1604,"width":1595,"height":1605,"rx":625,"fill":537,"stroke":538,"style":539},"146.1","163.3","50.7",[451,1607],{"x":1608,"y":1609,"width":1595,"height":1610,"rx":625,"fill":537,"stroke":538,"style":539},"188.8","48.1","165.9",[451,1612],{"x":1613,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"231.5","0.0",[451,1616],{"x":1617,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"274.1",[451,1619],{"x":1620,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"316.8",[451,1622],{"x":1623,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"359.5",[451,1625],{"x":1626,"y":1627,"width":1595,"height":1628,"rx":625,"fill":537,"stroke":538,"style":539},"402.1","84.8","129.2",[451,1630],{"x":1631,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"444.8",[451,1633],{"x":1634,"y":1635,"width":1595,"height":1636,"rx":625,"fill":537,"stroke":538,"style":539},"487.4","103.8","110.2",[451,1638],{"x":1639,"y":463,"width":1595,"height":1640,"rx":625,"fill":537,"stroke":538,"style":539},"530.1","190.0",[451,1642],{"x":1643,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"572.8",[451,1645],{"x":1646,"y":1502,"width":1595,"height":1614,"rx":625,"fill":537,"stroke":538,"style":539},"615.4",[451,1648],{"x":1649,"y":1650,"width":1595,"height":1651,"rx":625,"fill":537,"stroke":538,"style":539},"658.1","122.8","91.2",[451,1653],{"x":1654,"y":1655,"width":1595,"height":1656,"rx":625,"fill":537,"stroke":538,"style":539},"700.7","88.6","125.4",[472,1658,1662],{"x":1659,"y":1660,"style":1661},"380.0","258.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","3 and 6 April are bank holidays; 8 April is a working day with no sales.",[25,1664,1666],{"id":1665},"step-5-last-working-day-and-other-flags","Step 5 — Last working day and other flags",[10,1668,1669],{},"Flags precomputed in the date table turn awkward report logic into a filter:",[30,1671,1673],{"className":73,"code":1672,"language":75,"meta":35,"style":35},"dates[\"IsLastWorkingDayOfMonth\"] = dates[\"IsWorkingDay\"] & (\n    dates[\"WorkingDayOfMonth\"] == dates[\"WorkingDaysInMonth\"])\ndates[\"IsMonthEnd\"] = dates[\"Date\"].dt.is_month_end\nlast_wd = dates.loc[dates[\"IsLastWorkingDayOfMonth\"], [\"YearMonth\", \"Date\", \"Weekday\"]]\nprint(last_wd.head(6).to_string(index=False))\n",[37,1674,1675,1697,1715,1733,1761],{"__ignoreMap":35},[40,1676,1677,1679,1682,1684,1686,1688,1690,1692,1694],{"class":42,"line":43},[40,1678,163],{"class":86},[40,1680,1681],{"class":50},"\"IsLastWorkingDayOfMonth\"",[40,1683,169],{"class":86},[40,1685,109],{"class":82},[40,1687,152],{"class":86},[40,1689,887],{"class":50},[40,1691,169],{"class":86},[40,1693,903],{"class":82},[40,1695,1696],{"class":86}," (\n",[40,1698,1699,1702,1704,1706,1708,1710,1712],{"class":42,"line":96},[40,1700,1701],{"class":86},"    dates[",[40,1703,918],{"class":50},[40,1705,169],{"class":86},[40,1707,996],{"class":82},[40,1709,152],{"class":86},[40,1711,960],{"class":50},[40,1713,1714],{"class":86},"])\n",[40,1716,1717,1719,1722,1724,1726,1728,1730],{"class":42,"line":103},[40,1718,163],{"class":86},[40,1720,1721],{"class":50},"\"IsMonthEnd\"",[40,1723,169],{"class":86},[40,1725,109],{"class":82},[40,1727,152],{"class":86},[40,1729,115],{"class":50},[40,1731,1732],{"class":86},"].dt.is_month_end\n",[40,1734,1735,1738,1740,1743,1745,1748,1750,1752,1754,1756,1758],{"class":42,"line":144},[40,1736,1737],{"class":86},"last_wd ",[40,1739,109],{"class":82},[40,1741,1742],{"class":86}," dates.loc[dates[",[40,1744,1681],{"class":50},[40,1746,1747],{"class":86},"], [",[40,1749,251],{"class":50},[40,1751,124],{"class":86},[40,1753,115],{"class":50},[40,1755,124],{"class":86},[40,1757,270],{"class":50},[40,1759,1760],{"class":86},"]]\n",[40,1762,1763,1765,1768,1770,1772,1774,1776,1778],{"class":42,"line":160},[40,1764,369],{"class":198},[40,1766,1767],{"class":86},"(last_wd.head(",[40,1769,739],{"class":198},[40,1771,378],{"class":86},[40,1773,381],{"class":132},[40,1775,109],{"class":82},[40,1777,386],{"class":198},[40,1779,389],{"class":86},[10,1781,1782,1783,23],{},"May 2026 ends on a Sunday, so its last working day is Friday 29 May; August ends on the bank-holiday Monday the 31st, so its last working day is Friday 28 August. Scheduling a report for \"the last working day of the month\" becomes a lookup in this table, as in ",[19,1784,1786],{"href":1785},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-a-report-on-the-last-business-day-of-the-month\u002F","Run a Report on the Last Business Day of the Month",[25,1788,1790],{"id":1789},"step-6-write-it-as-an-excel-table","Step 6 — Write it as an Excel table",[10,1792,1793,1794,1797],{},"Written as a proper Excel table, the date table becomes a lookup source for ",[37,1795,1796],{},"XLOOKUP",", a dimension for pivot tables, and a table Power Pivot can mark as the model's date table:",[30,1799,1801],{"className":73,"code":1800,"language":75,"meta":35,"style":35},"with pd.ExcelWriter(\"date_table.xlsx\", engine=\"xlsxwriter\", datetime_format=\"yyyy-mm-dd\") as xw:\n    out = dates.copy()\n    out.to_excel(xw, sheet_name=\"Dates\", index=False, startrow=1, header=False)\n    ws = xw.sheets[\"Dates\"]\n    ws.add_table(0, 0, len(out), len(out.columns) - 1,\n                 {\"name\": \"DateTable\", \"style\": \"Table Style Light 9\",\n                  \"columns\": [{\"header\": c} for c in out.columns]})\n    ws.freeze_panes(1, 1)\n    ws.set_column(0, 0, 12)\nprint(len(dates), \"rows,\", len(dates.columns), \"columns written as table DateTable\")\n",[37,1802,1803,1841,1851,1892,1906,1935,1960,1986,1999,2016],{"__ignoreMap":35},[40,1804,1805,1808,1811,1814,1816,1819,1821,1824,1826,1829,1831,1834,1836,1838],{"class":42,"line":43},[40,1806,1807],{"class":82},"with",[40,1809,1810],{"class":86}," pd.ExcelWriter(",[40,1812,1813],{"class":50},"\"date_table.xlsx\"",[40,1815,124],{"class":86},[40,1817,1818],{"class":132},"engine",[40,1820,109],{"class":82},[40,1822,1823],{"class":50},"\"xlsxwriter\"",[40,1825,124],{"class":86},[40,1827,1828],{"class":132},"datetime_format",[40,1830,109],{"class":82},[40,1832,1833],{"class":50},"\"yyyy-mm-dd\"",[40,1835,1093],{"class":86},[40,1837,90],{"class":82},[40,1839,1840],{"class":86}," xw:\n",[40,1842,1843,1846,1848],{"class":42,"line":96},[40,1844,1845],{"class":86},"    out ",[40,1847,109],{"class":82},[40,1849,1850],{"class":86}," dates.copy()\n",[40,1852,1853,1856,1859,1861,1864,1866,1868,1870,1872,1874,1877,1879,1881,1883,1886,1888,1890],{"class":42,"line":103},[40,1854,1855],{"class":86},"    out.to_excel(xw, ",[40,1857,1858],{"class":132},"sheet_name",[40,1860,109],{"class":82},[40,1862,1863],{"class":50},"\"Dates\"",[40,1865,124],{"class":86},[40,1867,381],{"class":132},[40,1869,109],{"class":82},[40,1871,386],{"class":198},[40,1873,124],{"class":86},[40,1875,1876],{"class":132},"startrow",[40,1878,109],{"class":82},[40,1880,534],{"class":198},[40,1882,124],{"class":86},[40,1884,1885],{"class":132},"header",[40,1887,109],{"class":82},[40,1889,386],{"class":198},[40,1891,202],{"class":86},[40,1893,1894,1897,1899,1902,1904],{"class":42,"line":144},[40,1895,1896],{"class":86},"    ws ",[40,1898,109],{"class":82},[40,1900,1901],{"class":86}," xw.sheets[",[40,1903,1863],{"class":50},[40,1905,157],{"class":86},[40,1907,1908,1911,1913,1915,1917,1919,1921,1924,1926,1929,1931,1933],{"class":42,"line":160},[40,1909,1910],{"class":86},"    ws.add_table(",[40,1912,454],{"class":198},[40,1914,124],{"class":86},[40,1916,454],{"class":198},[40,1918,124],{"class":86},[40,1920,400],{"class":198},[40,1922,1923],{"class":86},"(out), ",[40,1925,400],{"class":198},[40,1927,1928],{"class":86},"(out.columns) ",[40,1930,1087],{"class":82},[40,1932,306],{"class":198},[40,1934,814],{"class":86},[40,1936,1937,1940,1943,1945,1948,1950,1953,1955,1958],{"class":42,"line":177},[40,1938,1939],{"class":86},"                 {",[40,1941,1942],{"class":50},"\"name\"",[40,1944,1351],{"class":86},[40,1946,1947],{"class":50},"\"DateTable\"",[40,1949,124],{"class":86},[40,1951,1952],{"class":50},"\"style\"",[40,1954,1351],{"class":86},[40,1956,1957],{"class":50},"\"Table Style Light 9\"",[40,1959,814],{"class":86},[40,1961,1962,1965,1968,1971,1974,1977,1980,1983],{"class":42,"line":205},[40,1963,1964],{"class":50},"                  \"columns\"",[40,1966,1967],{"class":86},": [{",[40,1969,1970],{"class":50},"\"header\"",[40,1972,1973],{"class":86},": c} ",[40,1975,1976],{"class":82},"for",[40,1978,1979],{"class":86}," c ",[40,1981,1982],{"class":82},"in",[40,1984,1985],{"class":86}," out.columns]})\n",[40,1987,1988,1991,1993,1995,1997],{"class":42,"line":220},[40,1989,1990],{"class":86},"    ws.freeze_panes(",[40,1992,534],{"class":198},[40,1994,124],{"class":86},[40,1996,534],{"class":198},[40,1998,202],{"class":86},[40,2000,2001,2004,2006,2008,2010,2012,2014],{"class":42,"line":246},[40,2002,2003],{"class":86},"    ws.set_column(",[40,2005,454],{"class":198},[40,2007,124],{"class":86},[40,2009,454],{"class":198},[40,2011,124],{"class":86},[40,2013,1582],{"class":198},[40,2015,202],{"class":86},[40,2017,2018,2020,2022,2024,2026,2029,2031,2033,2036,2039],{"class":42,"line":265},[40,2019,369],{"class":198},[40,2021,397],{"class":86},[40,2023,400],{"class":198},[40,2025,403],{"class":86},[40,2027,2028],{"class":50},"\"rows,\"",[40,2030,124],{"class":86},[40,2032,400],{"class":198},[40,2034,2035],{"class":86},"(dates.columns), ",[40,2037,2038],{"class":50},"\"columns written as table DateTable\"",[40,2040,202],{"class":86},[10,2042,2043,2044,124,2047,2050,2051,2054,2055,2058,2059,2062,2063,23],{},"Writing the data without pandas' header (",[37,2045,2046],{},"header=False",[37,2048,2049],{},"startrow=1",") and letting ",[37,2052,2053],{},"add_table"," supply the headers produces a clean table named ",[37,2056,2057],{},"DateTable",", which formulas can reference as ",[37,2060,2061],{},"DateTable[FiscalYear]",". The approach is explained in ",[19,2064,2066],{"href":2065},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fcreate-an-excel-table-with-python\u002F","Create an Excel Table with Python",[432,2068,441,2074,441,2077,441,2080,441,2084,441,2091,441,2102,441,2107,441,2111,441,2118,441,2125,441,2131,441,2135,441,2138,441,2143,441,2146,441,2152,441,2156,441,2160,441,2163,441,2166,441,2169,441,2175,441,2179,441,2183,441,2186,441,2189,441,2192,441,2198,441,2202,441,2206,441,2209],{"viewBox":2069,"role":435,"ariaLabelledBy":2070,"xmlns":439,"style":2073},"4 0 752 436",[2071,2072],"dt-uses-t","dt-uses-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[443,2075,2076],{"id":2071},"One date table, many uses",[447,2078,2079],{"id":2072},"The date table feeds zero-filled daily charts, fiscal-period reporting, working-day averages, last-working-day scheduling and Excel lookups and pivots.",[451,2081],{"x":663,"y":454,"width":2082,"height":2083,"fill":457},"752","436",[451,2085],{"x":2086,"y":2087,"width":2088,"height":2089,"rx":1582,"fill":537,"stroke":538,"style":2090},"20.0","171.5","220.0","93.0","stroke-width:2px",[2092,2093,2095],"g",{"transform":2094},"translate(118.0,184.5) scale(1.000)",[2096,2097],"path",{"d":2098,"fill":2099,"stroke":2100,"style":2101},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[472,2103,2057],{"x":2104,"y":2105,"style":2106},"130.0","231.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[472,2108,2110],{"x":2104,"y":2109,"style":1536},"250.5","one row per day",[451,2112],{"x":2113,"y":461,"width":2114,"height":2115,"rx":1582,"fill":2116,"stroke":2117,"style":2090},"490.0","250.0","92.0","#d9f4f1","var(--teal,#0f9488)",[2092,2119,2121],{"transform":2120},"translate(603.0,29.0) scale(1.000)",[2096,2122],{"d":2123,"fill":2099,"stroke":2124,"style":2101},"M4 20h16 M4 20V4 M6 16l4-5 4 3 6-7","var(--teal-ink,#0b6157)",[472,2126,2130],{"x":2127,"y":2128,"style":2129},"615.0","75.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Gap-free daily charts",[472,2132,2134],{"x":2127,"y":2133,"style":585},"94.0","left merge + fillna(0)",[2096,2136],{"d":2137,"fill":2099,"stroke":538,"style":2090},"M244.0,218.0 C364.0,218.0 364.0,62.0 480.0,62.0",[2139,2140],"polygon",{"points":2141,"fill":2142},"484.0,62.0 475.0,67.0 475.0,57.0","#5b5cf0",[451,2144],{"x":2113,"y":2145,"width":2114,"height":2115,"rx":1582,"fill":2116,"stroke":2117,"style":2090},"120.0",[2092,2147,2149],{"transform":2148},"translate(603.0,133.0) scale(1.000)",[2096,2150],{"d":2151,"fill":2099,"stroke":2124,"style":2101},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[472,2153,2155],{"x":2127,"y":2154,"style":2129},"179.0","Fiscal reporting",[472,2157,2159],{"x":2127,"y":2158,"style":585},"198.0","FiscalYear, FiscalPeriod",[2096,2161],{"d":2162,"fill":2099,"stroke":538,"style":2090},"M244.0,218.0 C364.0,218.0 364.0,166.0 480.0,166.0",[2139,2164],{"points":2165,"fill":2142},"484.0,166.0 475.0,171.0 475.0,161.0",[451,2167],{"x":2113,"y":2168,"width":2114,"height":2115,"rx":1582,"fill":2116,"stroke":2117,"style":2090},"224.0",[2092,2170,2172],{"transform":2171},"translate(603.0,237.0) scale(1.000)",[2096,2173],{"d":2174,"fill":2099,"stroke":2124,"style":2101},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[472,2176,2178],{"x":2127,"y":2177,"style":2129},"283.0","Per-working-day averages",[472,2180,2182],{"x":2127,"y":2181,"style":585},"302.0","IsWorkingDay",[2096,2184],{"d":2185,"fill":2099,"stroke":538,"style":2090},"M244.0,218.0 C364.0,218.0 364.0,270.0 480.0,270.0",[2139,2187],{"points":2188,"fill":2142},"484.0,270.0 475.0,275.0 475.0,265.0",[451,2190],{"x":2113,"y":2191,"width":2114,"height":2115,"rx":1582,"fill":2116,"stroke":2117,"style":2090},"328.0",[2092,2193,2195],{"transform":2194},"translate(603.0,341.0) scale(1.000)",[2096,2196],{"d":2197,"fill":2099,"stroke":2124,"style":2101},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2",[472,2199,2201],{"x":2127,"y":2200,"style":2129},"387.0","Scheduling",[472,2203,2205],{"x":2127,"y":2204,"style":585},"406.0","IsLastWorkingDayOfMonth",[2096,2207],{"d":2208,"fill":2099,"stroke":538,"style":2090},"M244.0,218.0 C364.0,218.0 364.0,374.0 480.0,374.0",[2139,2210],{"points":2211,"fill":2142},"484.0,374.0 475.0,379.0 475.0,369.0",[25,2213,2215],{"id":2214},"performance-notes","Performance notes",[10,2217,2218],{},"A date table is tiny — 365 rows a year, a few thousand for a decade — so building it is instant and joining it to millions of fact rows is a fast hash merge. Build it once per run, or once a year into a shared file that every report reads, rather than recomputing calendar logic inside each report. When several reports use it, keeping it in one file also guarantees they agree on what a working day or a fiscal period is, which is the real benefit: two reports that compute \"working days in March\" differently produce two different averages and a long meeting.",[25,2220,2222],{"id":2221},"common-pitfalls","Common pitfalls",[2224,2225,2226,2234,2240,2254,2260],"ul",{},[2227,2228,2229,2233],"li",{},[2230,2231,2232],"strong",{},"ISO week without ISO year."," Late December and early January belong to the neighbouring ISO year; group by both.",[2227,2235,2236,2239],{},[2230,2237,2238],{},"Hard-coded holiday rules."," Load official lists per country and year instead of computing them.",[2227,2241,2242,2245,2246,2249,2250,2253],{},[2230,2243,2244],{},"Inner joins."," Merging sales to dates with ",[37,2247,2248],{},"how=\"inner\""," drops the empty days you wanted; use ",[37,2251,2252],{},"how=\"left\""," from the date table.",[2227,2255,2256,2259],{},[2230,2257,2258],{},"Month names sorting alphabetically."," Keep the month number, or use an ordered categorical.",[2227,2261,2262,2265],{},[2230,2263,2264],{},"Fiscal labels."," \"FY27\" can mean either end-year or start-year naming; document the rule.",[25,2267,2269],{"id":2268},"conclusion","Conclusion",[10,2271,2272,2273,2275,2276,2278],{},"A date table is one row per day with every attribute a report groups or filters by: calendar parts, ISO weeks with their ISO year, weekend and holiday flags, working-day counters, fiscal year and period. Build it with ",[37,2274,413],{}," and ",[37,2277,417],{}," accessors, left-join facts to it so missing days become zeros, use its flags for working-day averages and month-end logic, and write it as a named Excel table for lookups and pivots.",[25,2280,2282],{"id":2281},"frequently-asked-questions","Frequently asked questions",[10,2284,2285,2288],{},[2230,2286,2287],{},"How do I create a calendar table with every date in pandas?","\nUse pd.DataFrame({\"Date\": pd.date_range(start, end, freq=\"D\")}) and add columns with the .dt accessors, such as dt.year, dt.quarter, dt.strftime(\"%b\") and dt.isocalendar().",[10,2290,2291,2294],{},[2230,2292,2293],{},"How do I add a fiscal year that starts in April?","\nCompute (month - 4) % 12 for the fiscal month index, and add one to the calendar year for months from April onwards. Agree with finance whether the fiscal year is named after its start or end year.",[10,2296,2297,2300],{},[2230,2298,2299],{},"How do I show days with no sales as zero?","\nLeft-join the sales to the date table on the date column with how=\"left\" from the date table, then fill the missing sales with 0.",[10,2302,2303,2306],{},[2230,2304,2305],{},"How do I mark working days and bank holidays?","\nFlag weekends with dt.weekday >= 5, flag holidays by checking membership in an official holiday list with isin, and define working days as neither.",[25,2308,2310],{"id":2309},"related","Related",[2224,2312,2313,2319,2326,2333],{},[2227,2314,2315,2316,2318],{},"Up one level: ",[19,2317,22],{"href":21}," — dates and times in spreadsheet data.",[2227,2320,2321,2325],{},[19,2322,2324],{"href":2323},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fgroup-excel-rows-by-month-and-quarter-with-pandas\u002F","Group Excel Rows by Month and Quarter with pandas"," — calendar grouping.",[2227,2327,2328,2332],{},[19,2329,2331],{"href":2330},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-month-to-date-and-year-to-date-totals-with-pandas\u002F","Calculate Month-to-Date and Year-to-Date Totals with pandas"," — period-to-date figures built on a date table.",[2227,2334,2335,2339],{},[19,2336,2338],{"href":2337},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fbuild-a-calendar-sheet-in-excel-with-python\u002F","Build a Calendar Sheet in Excel with Python"," — a printable month view.",[2341,2342,2343],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":35,"searchDepth":96,"depth":96,"links":2345},[2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357],{"id":27,"depth":96,"text":28},{"id":69,"depth":96,"text":70},{"id":772,"depth":96,"text":773},{"id":1050,"depth":96,"text":1051},{"id":1291,"depth":96,"text":1292},{"id":1665,"depth":96,"text":1666},{"id":1789,"depth":96,"text":1790},{"id":2214,"depth":96,"text":2215},{"id":2221,"depth":96,"text":2222},{"id":2268,"depth":96,"text":2269},{"id":2281,"depth":96,"text":2282},{"id":2309,"depth":96,"text":2310},"2026-10-02","Generate a calendar table — one row per day with year, quarter, month, ISO week, weekday, fiscal year, public holidays and working-day numbers — and use it to fill days with no sales, report by fiscal period, find the last working day of each month, and write it as an Excel table that lookups, pivots and Power Pivot can use.","md",[2362,2364,2366,2368],{"q":2287,"a":2363},"Use pd.DataFrame({\"Date\": pd.date_range(start, end, freq=\"D\")}) and add columns with the .dt accessors, such as dt.year, dt.quarter, dt.strftime(\"%b\") and dt.isocalendar().",{"q":2293,"a":2365},"Compute (month - 4) % 12 for the fiscal month index, and add one to the calendar year for months from April onwards. Agree with finance whether the fiscal year is named after its start or end year.",{"q":2299,"a":2367},"Left-join the sales to the date table on the date column with how=\"left\" from the date table, then fill the missing sales with 0.",{"q":2305,"a":2369},"Flag weekends with dt.weekday >= 5, flag holidays by checking membership in an official holiday list with isin, and define working days as neither.","build a date table for excel reports with pandas",null,{"breadcrumb":2373},[2374,2377,2380],{"name":2375,"item":2376},"Home","\u002F",{"name":2378,"item":2379},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":22,"item":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas",{"title":2383,"description":2384},"Build a Date Table (Calendar Table) for Excel with pandas","Create a date dimension in pandas: date_range, ISO weeks, fiscal years, holidays and working-day counters, zero-filling days without data, last working day of the month, and exporting it as a named Excel table for lookups and pivots.","build-a-date-table-for-excel-reports-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-date-table-for-excel-reports-with-pandas\u002Findex",[2388,2389],"dates-and-times","report-layouts","how-to","4tzEMAUKDfoVbB35RufPxXfzHPkumWv4qDLUnmqZ4_0",[2393,2397],{"title":2394,"path":2395,"stem":2396,"children":-1},"Build a 4-4-5 Fiscal Calendar with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-4-4-5-fiscal-calendar-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fbuild-a-4-4-5-fiscal-calendar-with-pandas\u002Findex",{"title":2398,"path":2399,"stem":2400,"children":-1},"Calculate Age and Tenure from Dates with pandas (DATEDIF Equivalent)","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-age-and-tenure-from-dates-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-age-and-tenure-from-dates-with-pandas\u002Findex",[2402,2404],{"path":2403,"title":2378},"\u002Fadvanced-data-transformation-and-cleaning",{"path":2405,"title":22},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data",[],[2408,2411,2414,2417,2420,2423],{"path":2409,"title":2410,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-overdue-dates-with-openpyxl","Highlight Overdue Dates in Excel with openpyxl",{"path":2412,"title":2413,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-weekends-and-holidays-in-a-date-column","Highlight Weekends and Holidays in a Date Column",{"path":2415,"title":2416,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fkeep-the-latest-record-per-key-with-pandas","Keep the Latest Record per Key When Deduplicating with pandas",{"path":2418,"title":2419,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling","Calculate Moving Averages with pandas rolling",{"path":2421,"title":2422,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Feomonth-and-edate-equivalents-in-pandas","EOMONTH and EDATE Equivalents in pandas",{"path":2424,"title":2425,"kind":2390},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python","YEARFRAC and DAYS360 Equivalents in Python",1790972101047]