Intervals¶
Interval is a small dataclass with union / intersection / containment / shift / scale operators. It works on any comparable type (int, float, date, datetime).
TimeWindow inherits from Interval — everything here applies to both.
Constructing¶
Both endpoints must satisfy start <= end, otherwise construction raises AssertionError. Single-point intervals (Interval(3, 3)) are allowed — this lets touching intersections like [1, 3] & [3, 5] produce [3, 3] instead of crashing.
Intersection & union¶
a.intersects(b) # True
a.intersection(b) # Interval(3, 5)
a & b # Interval(3, 5) (operator form)
Interval.union([a, b]) # Interval(1, 8)
a | b # Interval(1, 8)
Union requires overlap or contact by default — a gap raises ValueError. Pass allow_gaps=True to Interval.union([...]) to get the bounding envelope even when there's a gap:
c = Interval(10, 15)
Interval.union([a, c]) # raises
Interval.union([a, c], allow_gaps=True) # Interval(1, 15)
Containment¶
3 in a # True
Interval(2, 4) in a # True (fully contained)
Interval(2, 6) in a # False (b.end > a.end)
Shift & scale¶
Because Interval is generic over comparable types, arithmetic is delegated to the underlying type:
from datetime import timedelta
from vayu.time_utils import time_now
from vayu import TimeWindow
w = TimeWindow.behind(hours=1)
w + timedelta(minutes=30) # shift forward
w - timedelta(minutes=30) # shift back
Interval(1, 5) * 2 # Interval(2, 10)
range is the span:
Subclassing¶
Because operators use self.__class__, subclasses flow through naturally — two TimeWindows intersected return a TimeWindow, not a plain Interval:
from vayu import TimeWindow
(TimeWindow.behind(hours=2) & TimeWindow.ahead(hours=1)).__class__ # TimeWindow