- 10.4%average monthly errorweekly totals rolled into months
- 42 of 48months off by more than 5%
- +20.3%worst monthApril 2024
- −15.9%April 2025 growth on the shortcutreally +3.7%
The Monday problem
Picture a monthly pack that says March was weak and a weekly dashboard that says nothing happened. Finance trusts one, trading trusts the other, and the data team spends the week explaining that both are “right”.
The calendar effect behind that disagreement is large enough that statistics offices and retailers call it out when they publish sales figures:
- Statistics Sweden reported grocery sales volume up 7.2% in April 2025 on April 2024, but 1.9% after calendar adjustment, largely because Easter fell in April in 2025 and in March in 2024. SCB, May 2025 (opens in a new tab)
- Kesko's grocery trade sales rose 11.3% in March 2026, and the company put the impact of Easter timing on sales to grocery stores at about 6 to 7 percentage points. Kesko, April 2026 (opens in a new tab)
- UK total retail sales grew 7.0% in April 2025. The British Retail Consortium reported March and April together, 4.3%, to offset Easter moving from March to April. BRC, May 2025 (opens in a new tab)
- The UK Office for National Statistics corrected how its four- and five-week collection periods line up with calendar months (alongside a seasonal adjustment review). April 2025 sales volume growth went from +1.7% to −0.4%, and January 2025 from +1.4% to −0.4%. ONS, September 2025 (opens in a new tab)
Those are published figures, disclosed with an explanation beside them. Internal reports are exposed to the same calendar effects whenever they compare weeks with months, or one month with another, but an explanation only travels with them if someone adds it to each report. The engineering answer is to build the calendar logic into the platform once, so every report inherits it.
Scope. The calendar figures use one public series: daily card spending in Denmark (Danmarks Nationalbank, table DNBSK1), every day from January 2022 to December 2025. It covers all card payments in the country, not one retailer's sales. The customer and promotion chapters need customer-level sales, which no recent free dataset offers, so they use UCI Online Retail II, a UK online retailer, December 2009 to December 2011.
One week, two months
Trading teams often review figures by week, while finance closes by calendar month. A week does not fit inside a month, so some rule has to decide which month a boundary week belongs to. A simple one is to add up the weeks that start in the month (here, Monday-to-Sunday weeks). It is easy to build and passes every check on totals, which is what makes it risky. This page calls it the shortcut.
The week's total is identical either way. Only the month it lands in changes, so no sum check fails.
| Month | Correct | Shortcut | Error |
|---|---|---|---|
| March 2024 | 47,383 | 41,652 | −12.1% |
| April 2024 | 48,224 | 58,020 | +20.3% |
| April 2025 | 48,165 | 45,481 | −5.6% |
Across 48 complete months the average month was off by 10.4%, and 42 of 48 were off by more than 5%. No day is lost or counted twice: the shortcut only moves days between neighbouring months, which is why the error can survive review.
April 2024 · 5 weeks start in this month
- Correct
- DKK 48,224m
- Shortcut
- DKK 58,020m
- Error
- +20.3%
- Growth on March
- +1.8% correct · +39.3% shortcut
All 48 months as a table
| Month | Weeks starting | Correct (DKK m) | Shortcut (DKK m) | Error |
|---|---|---|---|---|
| January 2022 | 5 | 40,076 | 47,817 | +19.3% |
| February 2022 | 4 | 41,206 | 41,572 | +0.9% |
| March 2022 | 4 | 45,590 | 41,572 | −8.8% |
| April 2022 | 4 | 48,765 | 44,935 | −7.9% |
| May 2022 | 5 | 49,760 | 57,363 | +15.3% |
| June 2022 | 4 | 47,917 | 44,095 | −8.0% |
| July 2022 | 4 | 49,819 | 44,137 | −11.4% |
| August 2022 | 5 | 49,167 | 56,390 | +14.7% |
| September 2022 | 4 | 45,306 | 41,567 | −8.3% |
| October 2022 | 5 | 45,647 | 52,221 | +14.4% |
| November 2022 | 4 | 48,752 | 46,334 | −5.0% |
| December 2022 | 4 | 52,791 | 46,326 | −12.2% |
| January 2023 | 5 | 42,461 | 47,416 | +11.7% |
| February 2023 | 4 | 38,398 | 40,912 | +6.5% |
| March 2023 | 4 | 47,303 | 42,565 | −10.0% |
| April 2023 | 4 | 48,404 | 44,498 | −8.1% |
| May 2023 | 5 | 50,375 | 54,896 | +9.0% |
| June 2023 | 4 | 47,474 | 46,894 | −1.2% |
| July 2023 | 5 | 49,717 | 54,519 | +9.7% |
| August 2023 | 4 | 48,075 | 45,279 | −5.8% |
| September 2023 | 4 | 44,083 | 40,063 | −9.1% |
| October 2023 | 5 | 46,662 | 53,089 | +13.8% |
| November 2023 | 4 | 49,440 | 47,428 | −4.1% |
| December 2023 | 4 | 53,272 | 46,930 | −11.9% |
| January 2024 | 5 | 41,435 | 48,324 | +16.6% |
| February 2024 | 4 | 39,832 | 38,675 | −2.9% |
| March 2024 | 4 | 47,383 | 41,652 | −12.1% |
| April 2024 | 5 | 48,224 | 58,020 | +20.3% |
| May 2024 | 4 | 52,460 | 46,640 | −11.1% |
| June 2024 | 4 | 51,509 | 47,533 | −7.7% |
| July 2024 | 5 | 50,183 | 57,918 | +15.4% |
| August 2024 | 4 | 51,143 | 45,384 | −11.3% |
| September 2024 | 5 | 46,317 | 55,021 | +18.8% |
| October 2024 | 4 | 47,762 | 42,875 | −10.2% |
| November 2024 | 4 | 49,958 | 46,532 | −6.9% |
| December 2024 | 5 | 51,518 | 56,339 | +9.4% |
| January 2025 | 4 | 43,698 | 40,175 | −8.1% |
| February 2025 | 4 | 41,140 | 40,810 | −0.8% |
| March 2025 | 5 | 46,465 | 54,075 | +16.4% |
| April 2025 | 4 | 48,165 | 45,481 | −5.6% |
| May 2025 | 4 | 52,274 | 45,986 | −12.0% |
| June 2025 | 5 | 49,958 | 59,115 | +18.3% |
| July 2025 | 4 | 50,000 | 44,931 | −10.1% |
| August 2025 | 4 | 51,125 | 45,065 | −11.9% |
| September 2025 | 5 | 46,701 | 55,954 | +19.8% |
| October 2025 | 4 | 48,744 | 43,408 | −10.9% |
| November 2025 | 4 | 51,271 | 47,355 | −7.6% |
| December 2025 | 5 | 53,774 | 59,267 | +10.2% |
The part that can change decisions is growth. On the shortcut, April 2024 grew +39.3% on March; in reality it grew +1.8%. A year later the shortcut showed April 2025 falling 15.9% when it actually grew +3.7%. Nothing about the customers changed: April 2024 had five Mondays and March four, and in 2025 it was the other way round.
The rest of the gap is the calendar itself: Easter, leap days and the mix of weekdays. That is what the Swedish, Finnish and UK figures above quantify. Compare March with last March and part of the change is just the calendar.
How it is engineered
The fix is not a better chart. It is calendar, customer and measurement logic built once in the platform, so every report inherits it. Each chapter shows how, with the diagrams and checks from the pipeline itself.
One configuration generates the calendars. Every calendar date carries its week and its retail period, and calendar months are built from dates only. Facts join on the transaction date. On every run, error-level checks confirm that every transaction date maps to exactly one week and period and that totals are conserved in all three configured calendars. Weekly, period and monthly reports then aggregate the same daily facts.
- ConfigureOne calendar filePattern, week start and year end, declared once.
- calendars.
toml
- calendars.
- GenerateDate spineEvery date carries its week and its period; months come from dates only.
- calendar_
date
- calendar_
- JoinFacts on the dateEach sale line joins the spine by its date, never by its week.
- fct_
transaction_ line
checked every runa mismatch stops the run - fct_
- ReportWeek, period, monthEvery view aggregates the same daily facts, so they agree.
- 01The calendar fixOne calendar, built once, proved on every runOne configuration generates every calendar the business reports in. Facts join on the date, and three checks run on real data before anything reaches a report.One configuration · From dates to every calendar · The 53rd week · Checked on every run · 504 calendars, 40 yearsRead chapter 1
- 02Customer historyCustomer counts that don’t change behind your backCustomer attributes are versioned, status comes from purchase dates and thresholds held in configuration, and a test fails if a sale matches two customer versions.Versions, not overwrites · Status from purchase dates · Gates on the historyRead chapter 2
- 03Promotion measurementTesting the method on campaigns that did nothingSimulated campaigns with a known effect of zero. The first estimator failed that test, was changed once for a stated reason, and the remaining miss is reported.The simulation · The estimator · The zero test · What gets reportedRead chapter 3
- 04The platformFrom a pinned public file to tested Gold tablesAcquire, Bronze, Silver, Gold and export on Databricks, with quality gates that stop the run, quarantine instead of silent drops, and infrastructure as code.End to end · The data · Quality gates · Deployment and CIRead chapter 4
What this doesn't prove
Card spending in one country is not a retailer's sales with stores, regions and a loyalty scheme. The size of the calendar error depends on the calendar, the week-start day and how sales fall across the week; your numbers will differ. The mechanism, and the checks that catch it, carry over.
Four questions for your own reporting
- Are your monthly numbers built from dates, or by rolling up weeks?
- If you rerun last quarter's customer counts today, do they change, and can anyone say why?
- Do your churn and activity definitions live in versioned configuration, or inside report logic?
- Has your promotion measurement ever been shown to return zero on a campaign that did nothing?
If any answer made you pause, start a conversation.
Data and sources
Calendar figures: Danmarks Nationalbank, table DNBSK1 (daily card spending, DKK); own calculations. Customer and promotion chapters: Chen, D. (2012). Online Retail II [Dataset]. UCI Machine Learning Repository. doi.org/10.24432/C5CG6D (opens in a new tab) (CC BY 4.0).