Coverage for src/keel/window.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-16 18:07 +0000

1"""Merge-window logic — the night no-merge invariant, as a pure function. 

2 

3A project's ``merge_window`` (e.g. ``07:00-01:30``) is the **open** window; its 

4complement is the night no-merge window. The window may wrap midnight. This module 

5answers "is the merge window open at time T in the project's timezone?" with no I/O, 

6so it is fully unit-testable with a fixed clock. 

7""" 

8 

9from __future__ import annotations 

10 

11from datetime import datetime, time 

12from zoneinfo import ZoneInfo 

13 

14 

15def parse_window(window: str) -> tuple[time, time]: 

16 """Parse ``HH:MM-HH:MM`` into ``(open, close)`` times.""" 

17 open_s, _, close_s = window.partition("-") 

18 return _parse_time(open_s), _parse_time(close_s) 

19 

20 

21def _parse_time(s: str) -> time: 

22 hh, _, mm = s.strip().partition(":") 

23 return time(int(hh), int(mm)) 

24 

25 

26def is_merge_open(timezone: str, merge_window: str, *, now: datetime | None = None) -> bool: 

27 """True if the merge window is open at ``now`` (default: real time) in ``timezone``.""" 

28 tz = ZoneInfo(timezone) 

29 if now is None: 

30 now = datetime.now(tz) 

31 elif now.tzinfo is None: 

32 now = now.replace(tzinfo=tz) 

33 current = now.astimezone(tz).time() 

34 

35 opens, closes = parse_window(merge_window) 

36 if opens <= closes: 

37 return opens <= current < closes 

38 # window wraps midnight (e.g. 07:00-01:30): open late or early 

39 return current >= opens or current < closes