Skip to content

Minimum stay, closed to arrival: the rules that decide if a date is bookable

A season is one commercial decision — "August is 140, minimum three nights, no Saturday arrivals" — but it is two different questions for the software. What does this stay cost, and may it be taken at all?

Updated · By

Price and permission are different questions

Operators think in seasons: August is 140 a night, minimum three nights, no Saturday arrivals. That is one decision. For the software it is two, asked at different moments by different callers.

The price is asked once someone is nearly committed. The permission — may this stay exist? — has to be answerable before anyone has agreed to pay anything, because the availability endpoint needs it to decide whether to even draw a date as bookable.

Both answers should read the same rule rows. Split them into separate tables and the day will come when someone changes August's price and leaves August's minimum stay behind.

Boris calendar showing reservations across resources
Price and restrictions come from the same seasonal rule, so a calendar never offers a date the checkout will refuse.

Rates resolve per night

A stay that starts in the shoulder season and ends in high season does not have "a rate". Each night pays the rate of the rule covering it, and the total is their sum.

This sounds obvious and is routinely got wrong, because a single rate per stay is so much easier to store. The failure is invisible: no error, no complaint, just a quietly wrong total on every booking that crosses a boundary — in whichever direction the implementation happened to favour.

When several rules could apply to the same night, precedence has to be deterministic rather than whatever the database returns first. In Boris a rule for a specific room beats the workspace-wide default, and between two rules at the same level the newest start date wins.

Restrictions belong to the arrival night

Minimum and maximum nights, and closed-to-arrival days, are judged by the rule covering the night the stay starts. This is the convention every channel manager follows, and it is not an arbitrary choice.

Consider the alternative. If a minimum stay applied to every rule the stay touches, a guest arriving on 30 July for four nights would be refused because their stay reaches into an August rule demanding three nights — a stay of four nights refused for being too short. Checking every night turns a floor into a trap.

Closed to departure is the exception, and it is the detail most implementations miss: the departure date is not a night of the stay. The arrival night's rule may not extend that far into the calendar, so the departure date needs its own lookup. Fold it into the arrival rule and the bug only shows up on long stays, which are the bookings you least want to get wrong.

Zero is not a limit

A small thing with large consequences: in a database where the minimum-nights column is nullable, both NULL and 0 will appear, and both mean "no limit". A system that treats a stored 0 as a real minimum will compare every stay against it and refuse nothing — or, with the comparison written the other way, refuse everything. Normalise this once, at the point of reading, not in each caller.

Refusing at checkout is a failure, not a safeguard

A restriction that is only enforced when a booking is submitted is still correct and still a bad experience. The guest has chosen dates, entered their details, reached payment, and only then is told Saturdays are not available for arrival.

The fix is to expose the restriction, not just the verdict. Boris's availability endpoint can answer what an arrival date demands — minimum nights, maximum nights, whether arrival or departure is closed — so a storefront can disable the dates that would be refused instead of refusing them after checkout. Same rule, one query earlier, a completely different conversion rate.

For the same reason, every booking path has to consult the rules. A rate or restriction that applies in the back office but not through the storefront, the mobile app or the API is worse than having none: it looks enforced, which stops anyone from checking.

Nothing should change until a rule exists

The last property of a sane implementation: when no rule touches a stay, the resolver returns nothing and the caller keeps the price it already had. Installing seasonal pricing should alter no existing price until someone deliberately writes a season.

Gaps are worth reporting rather than filling. A missing night inside a daily plan can fall back to that plan's price defensibly. A monthly price does not divide into nights in any way you could justify to a customer, so the honest behaviour there is to decline the quote rather than invent a number.

Frequently asked questions

Does a minimum stay apply to every night of a stay?

No. It is checked against the rule covering the arrival night, which is the convention every channel manager uses. Otherwise a stay that merely reaches into a three-night season would be refused for its second night — a guest arriving in the shoulder season and leaving in high season would be told their stay is too short, which is not what the rule meant.

If a stay crosses two seasons, which rate applies?

Each night pays the rate of the rule covering that night. A per-stay rate cannot express a stay that starts cheap and ends expensive, so it rounds to whichever end it was built around, and your revenue quietly leaks on exactly the bookings that straddle a season boundary.

Why is closed to departure a separate check?

Because the departure date is not a night of the stay. The rule covering the arrival night may not reach that far into the calendar, so it needs its own lookup on the departure date. Treating it as part of the arrival rule is a bug that only appears on long stays.

Should restrictions apply to hourly bookings?

Never. "Minimum three nights" is meaningless for a meeting room or a desk, and silently applying a nights rule to an hourly booking would close a coworking space by accident. A request with no nights must skip the check entirely.