Giggabytes

Case study · public data

When the weekly report and the monthly report disagree.

Why retail KPIs don't reconcile, and how to engineer them correctly. Every number here is reproducible from public data.

  • 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.

One week, two months: Mon 29 Apr to Sun 5 May 2024

The week's total is identical either way. Only the month it lands in changes, so no sum check fails.

Card spending, DKK million: correct vs shortcut
MonthCorrectShortcutError
March 202447,38341,652−12.1%
April 202448,22458,020+20.3%
April 202548,16545,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.

Shortcut error by month, % of correct card spending48 complete months · above the line the shortcut overstates, below it understates
Choose a month

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
MonthWeeks startingCorrect (DKK m)Shortcut (DKK m)Error
January 2022540,07647,817+19.3%
February 2022441,20641,572+0.9%
March 2022445,59041,572−8.8%
April 2022448,76544,935−7.9%
May 2022549,76057,363+15.3%
June 2022447,91744,095−8.0%
July 2022449,81944,137−11.4%
August 2022549,16756,390+14.7%
September 2022445,30641,567−8.3%
October 2022545,64752,221+14.4%
November 2022448,75246,334−5.0%
December 2022452,79146,326−12.2%
January 2023542,46147,416+11.7%
February 2023438,39840,912+6.5%
March 2023447,30342,565−10.0%
April 2023448,40444,498−8.1%
May 2023550,37554,896+9.0%
June 2023447,47446,894−1.2%
July 2023549,71754,519+9.7%
August 2023448,07545,279−5.8%
September 2023444,08340,063−9.1%
October 2023546,66253,089+13.8%
November 2023449,44047,428−4.1%
December 2023453,27246,930−11.9%
January 2024541,43548,324+16.6%
February 2024439,83238,675−2.9%
March 2024447,38341,652−12.1%
April 2024548,22458,020+20.3%
May 2024452,46046,640−11.1%
June 2024451,50947,533−7.7%
July 2024550,18357,918+15.4%
August 2024451,14345,384−11.3%
September 2024546,31755,021+18.8%
October 2024447,76242,875−10.2%
November 2024449,95846,532−6.9%
December 2024551,51856,339+9.4%
January 2025443,69840,175−8.1%
February 2025441,14040,810−0.8%
March 2025546,46554,075+16.4%
April 2025448,16545,481−5.6%
May 2025452,27445,986−12.0%
June 2025549,95859,115+18.3%
July 2025450,00044,931−10.1%
August 2025451,12545,065−11.9%
September 2025546,70155,954+19.8%
October 2025448,74443,408−10.9%
November 2025451,27147,355−7.6%
December 2025553,77459,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.

What fixes it: one calendar, joined on the date, checked on every runThe full diagrams are in chapter 1.

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.

  1. ConfigureOne calendar filePattern, week start and year end, declared once.
    • calendars.toml
  2. GenerateDate spineEvery date carries its week and its period; months come from dates only.
    • calendar_date
  3. JoinFacts on the dateEach sale line joins the spine by its date, never by its week.
    • fct_transaction_line
  4. ReportWeek, period, monthEvery view aggregates the same daily facts, so they agree.
  1. 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
  2. 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
  3. 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
  4. 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

  1. Are your monthly numbers built from dates, or by rolling up weeks?
  2. If you rerun last quarter's customer counts today, do they change, and can anyone say why?
  3. Do your churn and activity definitions live in versioned configuration, or inside report logic?
  4. 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).