Well, I will throw it at what it costs.
I need to handle a lot of things.
- Fast / Running Request
- Any time increments, 9:01 pm, 12:14, etc.
- International (?) - I’m not sure that this is a problem even with time zones, at least in my case, but someone more knowledgeable here does not hesitate to call
- Open - close the next day (open at noon, close at 2:00 in the morning).
- Several time slots / days
- Ability to redefine certain days (holidays, regardless)
- Ability to re-enable overrides
- The ability to request at any time and start a business (now, in the future, in the past)
- The ability to easily exclude the results of a business closure in the near future (the filter closes in 30 minutes, you do not want your users to show this guy 5 minutes before closing in the food / drink industry).
I like the many approaches presented, and I borrow some of them. On my website, a project, no matter what I need to consider, I can have millions of businesses, and some of the approaches here don't seem to work well for me personally.
Here is what I propose for the algorithm and structure.
We must make some specific assumptions, around the world, anywhere and anytime: There are 7 days a week. There are 1440 minutes in one day. There are a finite number of permutations of open / closed minutes that are possible.
Not specific, but worthy assumptions: Many open / closed minute permutations will be distributed between enterprises, which will lead to the actual storage of all permutations. There was a time in my life, I could easily calculate the actual possible combinations of this approach, but if someone could help / think that this would be useful, that would be great.
I suggest 3 tables: Before you stop reading, consider in the real world 2 of these tables will be a fairly neat cache. This approach will not be for everyone either because of the complex complexity of the code that is required to interpret the user interface for the data model, and, if necessary, vice versa. Your mileage and needs may vary. This is an attempt at a reasonable "enterprise level", no matter what it means.
Opening hours table
ID | OPEN (minute of the day) | CLOSE (minute of the day)
1 | 360 | 1020 (example: from 9:00 to 17:00)
2 | 365 | 1021 (example: extreme case 9:05 AM - 5:01 PM (weirdos))
and etc.
HoursOfOperations does not care about which days, just open, close and uniqueness. In combination with open / closed, there can be only one entry. Now, depending on your environment, this entire table may be cached or may be cached during the current hour of the day, etc. In any case, you do not need to query this table for each operation. Depending on your storage solution, I render each column in this table indexed for performance. Over time, this table probably has an exponentially inverse probability of INSERT (s). In fact, when using this table, there should mainly be an Operational Operation (RAM).
Business2HoursMap
Note. In my example, I save "Day" as the field / column of the bit flag. This has a lot to do with my needs and the promotion of LINQ / Flags Enums in C #. There is nothing stopping you from expanding it to 7 bit fields. Both approaches should be relatively similar in both storage logic and query.
Another note. I do not enter into the semantics argument “each table needs a PC identifier column”, please find another forum for this.
BusinessID | HoursID | Day (or, if you prefer to divide by: BIT Monday, BIT Tuesday, ...)
1 | 1 | 1111111 (this business is open 9-5 every day of the week)
2 | 2 | 1111110 (this business is open 9:05 - 5:01 M-Sat (Monday = day 1)
The reason it’s easy to get a request is because we can always easily determine the MOTD (minute of the day) we encountered. If I want to know what will be open at 17:00, I will capture all the HoursOfOperations IDS WHERE Close> = 1020. If I am not looking for a time range, Open becomes insignificant. If you don't want businesses to close in the next half hour, just adjust your inbound time accordingly (find 5:30 PM (1050), not 5:00 PM (1020)). The second request, of course, was to "give me the whole thing with HoursID IN (1, 2, 3, 4, 5), etc. This should probably raise the red flag, since there are limitations to this approach. However, if anyone this might answer the question about the actual permutations above, we can pull out the red flag, consider that we only need the possible permutations on either side of the equation at a time, both open and closed.
Given that we have the first cache table, this is a quick operation. The second operation queries this potentially large table of rows, but we are looking for very small (SMALLINT) reliable indexed columns.
Now you can see the complexity on the code side of things. I am targeting most of the bars in my specific project, so it will be very safe to assume that I will have a significant number of businesses with hours such as "11:00 - 2:00 (the next day)." It really will be 2 entries in the HoursOfOperations table, as well as in the Business2HoursMap table. For instance. The bar, which is open from 11:00 to 02:00, will have 2 links to the HoursOfOperations 660-1440 table (11:00 - midnight) and 0 - 120 (midnight - 2:00 in the morning). These links will be reflected in actual days in the Business2HoursMap table as 2 entries in our simplified case, 1 entry = all days Hours reference # 1, another link to all days # 2. I hope this makes sense, it was a long day.
Override on special days / holidays / whatever. Overrides are inherently based on a date, not a day of the week. I think it is here that some of the approaches try to stick a round pin into a square hole. We need another table.
HoursID | BusinessID | Day | Month | Year
1 | 2 | 1 | 1 | Null
This, of course, can become more complicated if you need something like "every second Tuesday, this company fishes for 4 hours." However, what this will allow us to do quite easily allows 1 - to redefine, 2 - reasonable repeated overrides. EG. if the year is IS NULL, then every year on New Year's Eve this bar is a weirdo open from 9:00 to 17:00 in accordance with our data examples above. That is - If the year was set, this is only for 2013. If the month is zero, then every first day of the month. Again, this will not process each planning scenario with only NULL columns, but theoretically you can process almost everything by relying on a long sequence of absolute dates if necessary.
Again, I will cache this table for a day. I just can't really see the rows of this table in a one-day snapshot, which is very large, at least for my needs. I would check this table first, as it’s good to override and save the query for the much larger Business2HoursMap table on the storage side.
An interesting problem. I am really surprised that this is the first time I really needed to think this through. As always, they are very interested in different views, approaches or shortcomings in my approach.