简体   繁体   中英

Storing Time Ranges in MySQL and Determine if Current Time is within Time Range

I have a database table storing opening hours of a shop as a time range in TIME format. Eg if the shop's opening hours are '9am-5pm', there will be 2 columns 'hours_open' and 'hours_close' where I store 9:00 in 'hours_open' and 17:00 in 'hours_close'. To determine if the shop is currently open or closed, I will run the query:

SELECT * 
  FROM shop 
 WHERE CAST(NOW() AS TIME) BETWEEN hours_open AND hours_close;

What I need to do now, is to accommodate shops with opening hours that go past midnight, like 9pm - 3am. Additionally, the shops can have different opening hours on different days, eg: Mon-Thurs: 9pm-3am, Fri-Sun: 9pm-5am.

Now I am stumped. How should I proceed? I can have a pair of 'hours_open' and 'hours_close' for each day, eg: 'mon_hours_open', 'mon_hours_close', 'tue_hours_open', 'tue_hours_close'...

But how do I handle opening hours that go past midnight like 9pm-5am?

Any insights? I'm using PHP with Codeigniter framework if that helps.

It would be easier to check the hours if they were stored in a separate table, something like

shop_working_hours (
  shop_id int FK,
  day_of_week int,
  hours_open time,
  hours close time,
  PK (shop_id, day_of_week)
)

That way the query might be like this:

SELECT s.*
FROM shop s
  INNER JOIN shop_working_hours h ON s.id = h.shop_id
WHERE
  CASE
    WHEN h.hours_close > h.hours_open THEN
      h.day_of_week = DAYOFWEEK(NOW()) AND
      CAST(NOW() AS time) >= h.hours_open AND CAST(NOW() AS time) < h.hours_close
    ELSE
      h.day_of_week = DAYOFWEEK(NOW() - 1) AND
      CAST(NOW() AS time) >= h.hours_open OR CAST(NOW() AS time) < h.hours_close
  END

If, however, you would rather stick with having multiple columns like *_hours_open and *_hours_close for every day of week in the same table, ie the shop table, then the filter condition would most probably have to be more complex, maybe something like this:

SELECT s.*
FROM shop s
  CROSS JOIN (
    SELECT 1 AS day_of_week
    UNION ALL SELECT 2
    UNION ALL SELECT 3
    UNION ALL SELECT 4
    UNION ALL SELECT 5
    UNION ALL SELECT 6
    UNION ALL SELECT 7
  ) h
WHERE
  CASE h.day_of_week
    WHEN 1 THEN
      CASE
        WHEN s.mon_hours_close > s.mon_hours_open THEN
          h.day_of_week = DAYOFWEEK(NOW()) AND
          CAST(NOW() AS time) >= s.mon_hours_open AND
          CAST(NOW() AS time) < s.mon_hours_close
        ELSE
          h.day_of_week = DAYOFWEEK(NOW() - INTERVAL 1 DAY) AND
          CAST(NOW() AS time) >= s.mon_hours_open OR
          CAST(NOW() AS time) < s.mon_hours_close
      END
    WHEN 2 THEN
      CASE
        WHEN s.tue_hours_close > s.tue_hours_open THEN
        …  /* same for Tuesday */
      END
    …  /* and for all the other days of week */
  END

How about two entries in your shop table for such days, one that goes Monday 09:00:00 - 11:59:59 and another that goes Tuesday 12:00:00 - 03:00:00?

You could have three fields in your opening table:

opening
    day_opens       <--- what day the shop opens
    hour_opens      <--- what hour the shop opens
    duration_open   <--- how many hours it stays open

So cheching if it is open now (or at a certain day and time), it would be a condition like:

WHERE (   day_opens = Day(NOW()) 
      AND hourOpens <= Hour(NOW())
      AND Hour(NOW()) < hourOpens + duration_open 
      )
   OR (   day_opens + 1 = Day(NOW())
      AND Hour(NOW()) < hourOpens + duration_open - 24
      )

or even better in one condition:

WHERE day_opens * 24 + hourOpens    <= Day(NOW()) * 24 + Hour(NOW())
  AND Day(NOW()) * 24 + Hour(NOW()) <  day_opens * 24 + hourOpens + hourOpens

There are very good ideas in: Is a given booking period available?
( http://www.artfulsoftware.com/infotree/queries.php#576 ). It helped me in a similar situation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM