Anchor-date cycle position arithmetic (Feature 05, task 2)
presence.cycle_week_index: ((target_date - anchor_date).days // 7) % cycle_length_weeks — anchor-date arithmetic ONLY (D-004). Python floor division makes pre-anchor dates walk the cycle backwards (day -1 is the last week), never inverting; the modulo of a negative stays non-negative, so the index is always a valid week index. The target date passes through the model's strict plain-date parsing. Tests: formula offsets, three-week pattern wrap, backwards walking, day-by-day stability across three Dec-31 -> Jan-1 boundaries including 2020 (verified 53 ISO weeks — the parity-inversion regression), and a source guard that the module never reads ISO week numbers. Suite: 980 passed.
This commit is contained in:
@@ -298,3 +298,22 @@ class PresenceOverride:
|
||||
def covers(self, date: datetime.date) -> bool:
|
||||
"""Return True when the override applies to ``date``."""
|
||||
return self.start_date <= date <= self.end_date
|
||||
|
||||
|
||||
def cycle_week_index(
|
||||
schedule: PresenceSchedule, target_date: object
|
||||
) -> int:
|
||||
"""Return the cycle week index ``target_date`` falls in.
|
||||
|
||||
Anchor-date arithmetic ONLY (D-004): ``((target_date -
|
||||
anchor_date).days // 7) % cycle_length_weeks``. Python's floor
|
||||
division makes dates BEFORE the anchor behave correctly — they
|
||||
walk the cycle backwards (day -1 is the last week of the cycle,
|
||||
day -8 wraps one further) instead of inverting, which is exactly
|
||||
what ISO week parity does wrong in 53-week years. The modulo of
|
||||
a negative in Python is non-negative, so the index is always a
|
||||
valid week index.
|
||||
"""
|
||||
target = _parse_date(target_date, "target_date")
|
||||
days = (target - schedule.anchor_date).days
|
||||
return (days // 7) % schedule.cycle_length_weeks
|
||||
|
||||
Reference in New Issue
Block a user