There are 2 basic approaches to time-tabling:
1. generate a table with a place for every posible time-slot: ie 1 hour slotsx10 hours a day = 10 timeslot cols per day record
2. just record the bookings made: ie each time-slot has it's own record
Which you choose depends on the environment and the application: eg
we built a room booking system for a uni that used the first because the time-table for a whole semester was generated in advance and lecturers then distributed it to their classes. We had a finite set of rooms that could be booked for predetermined periods by a finite set of lecturers running a finite set of courses. So generating the finite set of time-slots was easy and the best solution.
If your booking system is more open ended then the second solution is what you need.
With that second solution, you need a method of generating the matrix of available time-slots to left-join to the bookings made so that you can also display the available time-slots. Now, on a weekly basis that can be done thus:
table weekdays
w_id int not null primary key
dow char(4) not null
1| Mon
2 | Tues
3 | Wed
4 | Thur
5 | Fri
table slots
s_id, int, not null, primary key
start, time, not null
1 | 9:00
2 | 9:30
3 | 10:00
4 | 10:30
... etc
Joining these 2 tables with a cartesian join (,) will produce a matrix of possible time slots for each of the days of the week.
The bookings table then has foreign key columns for both those tables.
booking
b_id
w_id
s_id
cols for other data about who booked it etc
NB this does require sub-query support, or else you are going to have to create a table that holds the data and would need to be re-created if you ever change the days or hours.
// matrix query
$matrix = "SELECT * FROM weekdays , slots";
// used as a subquery to produce the booked and available data
$sql = "SELECT * FROM (SELECT * FROM weekdays , slots) AS m
LEFT JOIN bookings b ON (m.w_id=b.w_id AND m.s_id=b.s_id)";
Your results are going to look like this
m.w_id | m.dow | m.s_id | m.start | b.b_id | b.w_id | b.s_id | etc
1 | Mon | 1 | 9:00 | null
1 | Mon | 2 | 9:30 | 1 | 1 | 2 | etc
1 | Mon | 3 | 10:00 | 2 | 1 | 3 | etc
1 | Mon | 4 | 10:30 | null
That means that something is booked for mon at 9:30 and for Mon at 10:00 and available for the other slots. Just look for the NULL in your code and colour the table cell accordingly for available slots.
Of course you can, and should, use a column list in the select to reduce the replications in the resultset.