Availability rules that survive contact with a real week
Most availability bugs come from one mistake: storing "the room is unavailable" without storing why. The reason determines how the rule behaves next week.
Three kinds of unavailable
A space closes a resource for reasons that look identical in a calendar and behave nothing alike over time.
- Opening hours. The space is shut at 21:00. This is a property of the workspace, applies to everything in it, and changes rarely.
- A one-off closure. The meeting room is being repainted from Thursday to Friday. It has real start and end instants, it will never recur, and it should disappear on its own.
- A recurring block. The large room is a yoga studio every Tuesday from 18:00. It has no end date, it repeats by rule, and it must apply to a booking attempted three months from now.
Collapse these into one table of "blocked periods" and you will either generate thousands of rows to represent the recurring case, or lose the recurrence entirely the moment someone books beyond the horizon you pre-generated.
Evaluate them separately, at request time
The approach that holds up is to keep one-off blocks as concrete intervals and recurring blocks as rules, then check both when a booking is attempted. Boris does exactly that: a date-range block is tested as an overlap, a recurring block is expanded against the requested window, and either one refuses the booking with its own message.
Two distinct messages, not one. "This room is blocked on those dates" and "this room is not available at that time of day" send the member to different solutions — the first invites them to pick another day, the second to pick another hour. A single generic refusal makes them guess.

Blocks are not bookings, and that matters
It is tempting to implement a closure as a fake booking owned by the house. It works until it does not. A blocking booking shows up in occupancy reports and inflates them. It appears in revenue exports as a zero. It can be cancelled by anyone with permission to cancel bookings. And on a per-seat resource it consumes capacity in the wrong units — a room closed for maintenance is not "twelve seats sold".
Keeping blocks in their own model costs one more check in the availability path and saves every one of those problems.
Rules people forget until a member complains
Minimum and maximum duration. A meeting room bookable in five-minute increments will be booked in five-minute increments, and your calendar becomes unreadable. A room bookable for twelve hours will be held all day by someone who needed ninety minutes.
How far ahead. Without a horizon, someone will book the boardroom for a date next year, and you will be honouring a price you set today. Without a minimum notice, someone books a cleaned-and-ready room four minutes before walking in.
Turnaround time. If a room needs ten minutes between meetings, the availability check must know that, or the calendar will look correct while the room is permanently late.
Who the rule applies to. Members with a plan that includes meeting-room hours and casual visitors paying per hour are not subject to the same limits, and this is the point where a simple availability model usually stops being enough.
Order the checks by the answer they give
When a booking is refused, the member should be told the most actionable reason. Check the cheap, absolute constraints first — is the space even open, is the resource blocked — before the expensive ones that require counting existing bookings. It is faster, and more importantly the message it produces is more useful: "we are closed then" is a better answer than "no capacity", even when both are true.
