Find Free Time in a Calendar
The scheduling assistant needs a person's free time for a day, worked out from their busy intervals. The README has the rules, and free_time.py has the time helpers and the outline. Build it with the assistant: accept, edit, or reject each suggestion, and keep the tests green.
free_time.py1 of 3
Files
free_time.py
Python · EditableDAY_START = "09:00"
DAY_END = "17:00"
def to_minutes(hhmm):
"""'09:30' -> 570"""
hours, minutes = hhmm.split(":")
return int(hours) * 60 + int(minutes)
def to_clock(minutes):
"""570 -> '09:30'"""
return f"{minutes // 60:02d}:{minutes % 60:02d}"
def free_slots(busy, day_start=DAY_START, day_end=DAY_END, min_minutes=30):
"""Free slots in the working day that last at least min_minutes, as ("HH:MM", "HH:MM") pairs."""
start, end = to_minutes(day_start), to_minutes(day_end)
intervals = [(to_minutes(a), to_minutes(b)) for a, b in busy]
gaps = find_gaps(merge_busy(intervals), start, end)
return [(to_clock(a), to_clock(b)) for a, b in gaps]
def merge_busy(intervals):
"""Merges overlapping and touching (start, end) intervals. Returns them sorted by start."""
raise NotImplementedError
def find_gaps(merged, start, end):
"""The gaps between sorted, non-overlapping busy intervals, clipped to start and end."""
raise NotImplementedError
import pytest
from free_time import free_slots, merge_busy
def test_merge_combines_overlapping_and_touching():
assert merge_busy([(540, 600), (570, 630), (630, 660)]) == [(540, 660)]
def test_merge_handles_unsorted_input():
assert merge_busy([(780, 840), (570, 600), (600, 620)]) == [(570, 620), (780, 840)]
def test_empty_day_is_one_free_slot():
assert free_slots([]) == [("09:00", "17:00")]
def test_free_slots_between_meetings():
busy = [("10:00", "11:00"), ("12:00", "13:00")]
assert free_slots(busy) == [("09:00", "10:00"), ("11:00", "12:00"), ("13:00", "17:00")]
def test_meetings_in_any_order():
busy = [("14:00", "15:00"), ("10:00", "11:00")]
assert free_slots(busy) == [("09:00", "10:00"), ("11:00", "14:00"), ("15:00", "17:00")]
def test_busy_time_outside_the_day_is_clipped():
busy = [("08:00", "09:30"), ("16:30", "18:00")]
assert free_slots(busy) == [("09:30", "16:30")]
def test_slot_of_exactly_min_minutes_is_kept():
busy = [("09:30", "12:00"), ("12:30", "17:00")]
assert free_slots(busy) == [("09:00", "09:30"), ("12:00", "12:30")]
def test_short_gaps_are_dropped():
busy = [("09:10", "12:00"), ("12:20", "17:00")]
assert free_slots(busy) == []
def test_rejects_busy_time_that_ends_before_it_starts():
with pytest.raises(ValueError):
free_slots([("11:00", "10:00")])
def test_rejects_day_that_ends_before_it_starts():
with pytest.raises(ValueError):
free_slots([], day_start="17:00", day_end="09:00")
# Free Time
The scheduling assistant offers meeting times from a person's free slots. free_slots (free_time.py) takes their busy times for one day and returns the free slots inside working hours.
## Rules
- Busy times are (start, end) pairs of "HH:MM" strings, in any order. They can overlap or touch.
- Free slots lie between day_start and day_end, 09:00 to 17:00 by default. Busy time outside those hours is ignored.
- A free slot lasts at least min_minutes, 30 by default.
- Free slots come back in order, as "HH:MM" pairs.
- Raise ValueError for a busy time that ends before it starts, or a day_end that isn't after day_start.
merge_busy and find_gaps work in minutes since midnight. The tests are in test_free_time.py.
Test Output
No runs yet.