Found by a Python/Rust query parity suite (ActivityWatch/activitywatch, PR to follow). aw-core 33c8528.
Bucket.get() (aw_datastore/datastore.py) rounds the end of the requested period up to the next millisecond:
milliseconds = 1 + int(endtime.microsecond / 1000)
This adds 1 ms even when endtime is already a whole millisecond, so an event that runs past the period end is clipped 1 ms after it.
Minimal repro
Insert {"timestamp": "2026-01-01T10:00:00Z", "duration": 60, "data": {}} into bucket a and query with timeperiod 2026-01-01T09:00:00Z/2026-01-01T10:00:30Z:
RETURN = query_bucket("a");
- aw-server (Python):
duration: 30.001
- aw-server-rust:
duration: 30.0
Any view whose period ends while an event is still running (for example "today" with a current window event) reports 1 ms too much and extends past the period. Rounding up only when there are sub-millisecond digits (math.ceil(endtime.microsecond / 1000)) would keep the "don't miss events" intent without the offset.
Found by a Python/Rust query parity suite (ActivityWatch/activitywatch, PR to follow). aw-core 33c8528.
Bucket.get()(aw_datastore/datastore.py) rounds the end of the requested period up to the next millisecond:This adds 1 ms even when
endtimeis already a whole millisecond, so an event that runs past the period end is clipped 1 ms after it.Minimal repro
Insert
{"timestamp": "2026-01-01T10:00:00Z", "duration": 60, "data": {}}into bucketaand query with timeperiod2026-01-01T09:00:00Z/2026-01-01T10:00:30Z:duration: 30.001duration: 30.0Any view whose period ends while an event is still running (for example "today" with a current window event) reports 1 ms too much and extends past the period. Rounding up only when there are sub-millisecond digits (
math.ceil(endtime.microsecond / 1000)) would keep the "don't miss events" intent without the offset.