BETWEEN a AND b is inclusive on both ends, and a bare date is midnight. So BETWEEN a date AND a month-end date stops at that day's midnight and loses everything later that day. The half-open form >= start AND < next-start keeps the whole span.
SELECT id FROM swipes WHERE ts BETWEEN TIMESTAMP '2024-01-01' AND TIMESTAMP '2024-01-31' ORDER BY id;
SELECT id FROM swipes WHERE ts >= TIMESTAMP '2024-01-01' AND ts < TIMESTAMP '2024-02-01' ORDER BY id;
The January report drops every swipe after midnight on the 31st — BETWEEN's closed upper bound is really '2024-01-31 00:00'.
Filter timestamp ranges with >= start AND < next_start — the half-open interval that includes the last day's daytime.