Two tables deals with this information:
- appartments (the actual appartment, whith info on address, current price etc)
- rented (start_date, end_date, actual price)
First off, the reason I list the actual price and current price (both likely to be named simply "price") is that when a user views all appartments, you list appartments.price info. When they rent the appartment, you insert appartments.price into rented.price. If you later change the rental price on the appartment, all current bookings (wether concerning past or future dates, the agreed upon price remains unchanged).
Now, each appartment will have one entry in rented for each period it's unavailable. So when users search for a date range checking for availability, you have to make sure the appartment is NOT listed as unavailable.
There are 2 possible combinations of searched start and end (SS, SE) and rented starts and ends which works, while there are 4 combinations when it doesn't work
These are OK
1. Search period is before (outside) rented period
SS -- SE
RS -- RE
2. Search period is after (outside) rented period
SS -- SE
RS -- RE
These are not OK
3. Search period has end date after rental start date (inside range)
SS -- SE
RS -- RE
3. Search period has start after rental start date (inside range)
SS -- SE
RS -- RE
3. Search period has both start and end dates inside range
SS -- SE
RS -- RE
4. Search period has both dates outside range, while there are searched dates inside the range
SS -- SE
RS -- RE
Look at how the searched start date relates to the rented end date, and how the searched end date relates to the rented start date. EVERY time the search period falls within a date range during which the appartment has been rented, the searched start is < the rented end AND the searched end is > the rented start.
What about considering <= and/or >=, that is when one of the days in the searched range is not just less than or greater than, but actually equal to one of the rented days.
Well, if you search for 1-2 may and there is an appartment rented from 2 may - whatever, then the person searching would move in on 1st, and leave around noon the 2nd, giving a few hours to clean the place letting the second person move in at 4PM the 2nd. The same reasoning goes for the start date. That is, in this case the comparisons should be <= and >= respectively.
Should you however always need more than a few hours to inspect and possibly clean the place, so that you can't manage one person departing the same day another arrives, you'd use < and > comparisons.
In the first case, assuming you from time to time need an extra day (or more) to handle repairs or whatever, you can add them to the rental table (also add a specials column or whatever you'd call it)
TABLE: rented
appartment_id start_date end_date specials client_id price
1 2012-05-01 2012-05-02 repairs NULL 0
which means this entry would work just like any other booking when searching for availability. It would however disable the use of FOREIGN KEY (client_id) REFERENCES client(id). You could create a client called repair and set its client_id in this table. Or you could create a second table called maintenance with app_id, start_date, end_date, comment, thus leaving the rented table with only info on the actual rental periods. And yes, this is the way to go... Just showing the detour I usually take in these cases as I discover the needs. But don't get sloppy when designing the schema, you will regret it later since you'd have to deal with special cases in code instead (horrible work).