Back to Blog

Why Your AI's 'Last Month' Is Really the Last 30 Days

Asked for last month's revenue and got a number that doesn't match finance's September figure. The query used now() minus one month: a rolling window starting mid-day on the 5th, not the 1st.

You ask for "revenue last month." The AI returns $84,210. Finance says September was $79,880. Nobody is wrong about the money. The query answered a different question.

-- what the AI wrote
SELECT SUM(amount) FROM orders
WHERE created_at >= now() - interval '1 month'

Say today is October 5th, mid-afternoon. That window starts September 5th, mid-afternoon, and runs to this minute. It drops September 1st to 4th and includes the first five days of October.

Rolling window versus calendar month

-- Postgres: the calendar month before this one
WHERE created_at >= DATE_TRUNC('month', now()) - interval '1 month'
  AND created_at <  DATE_TRUNC('month', now())

-- MySQL
WHERE created_at >= DATE_FORMAT(CURDATE() - INTERVAL 1 MONTH, '%Y-%m-01')
  AND created_at <  DATE_FORMAT(CURDATE(), '%Y-%m-01')

-- SQL Server
WHERE created_at >= DATEADD(month, DATEDIFF(month, 0, GETDATE()) - 1, 0)
  AND created_at <  DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)

The end is < the start of the current month, not BETWEEN the last day. That keeps every row from the final day, which BETWEEN with dates drops.

The SQL runs either way. A rolling window gives a plausible number, so nothing looks broken until someone compares it with another report. Subtracting one month on March 31st also lands on February 28th in all three databases, so the "month" is not even a fixed length.

This is not the month extracted without the year, which mixes several Septembers together. Here the year is right and the window edges are wrong.

How to catch it

SELECT MIN(created_at), MAX(created_at) FROM orders WHERE <the filter the AI used>
-- for "last month" the first value should be the 1st at 00:00
-- and the last value should be before the 1st of this month

If the earliest row is not on the first of a month, the window is rolling.

The prompt that heads this off

When I say last month, last week or last quarter, use calendar boundaries:
start of the period inclusive, start of the next period exclusive.
Only use "the past N days" if I say "past N days". Show the exact start and
end timestamps you used.

The short version

now() - interval '1 month' means "since this time a month ago." "Last month" means the calendar month that just ended. If the AI's number is close to finance's but never equal, check where the window starts.


Read-only MCP gateway: mcpserver.design.

Related reading: