I've got this database query I use to select restaurants from a database, which are currently open. "Currently" comes from the browser via javascript, so it gets hard-coded into the query. This query seems to be working great, but I still wanted to run it by more eyes before I move on to the next part of this site.
So what makes this task (selecting currently open restaurants) more complicated than you initially might think, is that many restaurants, particularly on the weekends, are open past midnight. But those few hours past midnight, are still considered part of the day before. I store the times in 24-hour time, and so for times after midnight, I just keep going. For example, if a restaurant closes at 2am, the close time gets stored in my database as 26:00:00.
So my restaurant table rest is very straight forward, the only relevant fields are:
id / name / city
7 / 'Jims' / 36
Hours are stored in a table called hours. Each day that a restaurant is open, has it's own row in hours. So if a restaurant is open monday through friday, there will be 5 entries in hours, for that particular restaurant. As I said above, close times after midnight are entered as times past 24:00:00.
id / rid / day / open / close
63 / 7 / 5 / 12:00:00 / 26:00:00
id is a unique id.
rid is the id of the restaurant.
day is the day of the week.
open and close are the open and close times.
So in the sample row above, it would indicate that restaurant 7 (Jims), is open on Friday from noon till 2am.
Also note that if a restaurant isn't open on a day, there is simply no entry for that day. And if a restaurant's hours are unknown, there are no entries in the hours table for that restaurant at all.
So with all of that info, here is a query I use to select restaurants that are open right now:
SELECT
`rest`.`id` AS `id`,
`rest`.`name` AS `name`
FROM
`rest`
LEFT JOIN `hours`
ON `hours`.`rid` = `rest`.`id`
WHERE
`rest`.`city` = $city
$filters
AND ( (`hours`.`day` = $today AND '$now' > `hours`.`open` AND '$now' < `hours`.`close`)
OR (`hours`.`day` = $yesterday AND '$now' < SUBTIME(`hours`.`close`,'24:00:00')) )
GROUP BY
`rest`.`id`
ORDER BY
TRIM(LEADING 'The ' FROM `rest`.`name`)
$today and $yesterday are integers of the appropriate days of the week, which gets figured out in javascript and passed along to php. $now is the current time that gets passed the same way. I get this info in javascript so I don't have to worry about time zones. Everything is always local to the user. Note the odd OR in the WHERE clause, where I have to see if $now is within today's open and close hours, but I also have to see if $now is before $yesterday's close hours, since many hours 'leak' past midnight. $filters are just some other WHERE checks for simple columns in rest (i.e."AND rest.delivers = 1")
So this is my solution. Any problems with my logic? Or my query?
The only other obvious alternative to the way I do it, would be to break up each day's hours into separate pieces. For example, a single restaurant would have to hours entries for Saturday. A midnight to 2am row, and then a noon to midnight row (assuming the restaurant was open till 2am friday night, and then opened noon to 2am saturday night too). Saturday night's midnight to 2am would get entered in as one of sunday's hours. Storing my database like this would make my queries a little simpler but it would make the overall displaying of hours and open & close days much more complicated.