Field note · February 24, 2026
The time coverage of ab-test-checkout
28 populated days across a 28-day span, and what that allows you to compute.
Before any time series work on ab-test-checkout: what does exposed_on actually cover?
2024-03-04 to 2024-03-31. That is 28 calendar days, of which 28 have at least one row — so the series is dense. Average 214.3 rows per populated day.
select date_trunc('day', exposed_on) as day, count(*) as rows
from ab_test_checkout
group by 1
order by 1;A dense series means a plain group by day is safe. That is worth confirming rather than assuming — the moment a day drops out, every window function that counts rows instead of days starts comparing the wrong pair, and a seven-day lag silently becomes an eight-day one.
The other number worth having before you start: 214.3 rows per populated day tells you what granularity the data can actually support. Aggregating to something finer than the data is dense enough to fill produces a chart of noise, and there is no warning — the query returns, the line is drawn, and the wobble gets interpreted.
Because this is a date rather than a timestamp, daily aggregates are unambiguous — no time zone, no truncation, no boundary argument. That is a small thing that removes a whole category of bug, and it is worth preferring a date column whenever the time component is not genuinely used.
A date range is a fact about the data, not about the question. Filters outside it return empty results, filters half-inside it return partial ones, and neither raises anything. Check the range, then write the filter — and use a half-open interval when you do, because between on a timestamp includes exactly one instant of the final day.