fix(analysis): _expected_ticks usa ceiling division (no off-by-one)
Il piano originale aveva `floor(span/15) + 1` che over-conta a span allineati (span=60min → 5 invece di 4). Il primo fix dell'implementer (`floor(span/15)`) under-conta a span non-allineati (span=16min → 1 invece di 2). Solo `ceil(span/15)` è corretto in entrambi i casi. Aggiunti 2 test che coprono gli scenari non-allineato e boundary-esatto per impedire regressioni. Plan doc allineato. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ parameters. To tune a threshold, edit this file.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import sqlite3
|
||||
import statistics
|
||||
from dataclasses import dataclass, field
|
||||
@@ -117,5 +118,9 @@ def _expected_ticks(since: datetime, until: datetime) -> int:
|
||||
)
|
||||
if first_tick >= until:
|
||||
return 0
|
||||
span = until - first_tick
|
||||
return int(span.total_seconds() // (_TICK_INTERVAL_MIN * 60))
|
||||
# Count ticks in [first_tick, until): the largest k with
|
||||
# first_tick + k*15min < until is ceil(span/15) - 1, so the
|
||||
# count is ceil(span_minutes / 15). floor() under-counts at
|
||||
# aligned multiples and would mis-count non-aligned spans.
|
||||
span_seconds = (until - first_tick).total_seconds()
|
||||
return math.ceil(span_seconds / (_TICK_INTERVAL_MIN * 60))
|
||||
|
||||
Reference in New Issue
Block a user