Let's take a step back. A restaurant has different opening/closing times for each day of the week. So on Monday it might open at 10, on Tuesday at 8 but on Sunday it closes early at 16.
It also might be closed on special days, like on holidays or whatever the reason. These should be specified in advance.
Then I need to get a list of all restaurants and find out which is closed and which is open. For those closed I also need to show the next day it will be open and the interval.
This is both a database design and business logic question. The open/closed filtering can be done client side, as can figuring out the next day. These depend on how the info is stored in the database.
Oh yeah and the next open time has to be calculated as a Datetime, for localized formatting as well as timezone conversion.
I would assume the time would always be presented in the frame of the restaurant, correct. Knowing that its open midnight to 2pm my time is kind of useless.
Interesting topic. Thanks for starting this thread.
Oh, but of course 'is it open now' depends on utc.
When you say client side, one thought I have is that it can be calculated on the app server as opposed to being stored in the db.
I realized recently that my mind had started to try and fit everything in either the db or in js/whatever client code the ui runs leaving the server as a dumb pass through layer. Why? I have no answer for myself. That might alleviate some map reduce complexity right there. ;-)
Welcome to my world. :) And good use case for why UTC is not always the answer.
Each restaurant should have a time zone associated with its location. All times for that restaurant should be stored as local time.
For the specific days they are closed or have special hours, use a DateTime. A DateTimeOffset would also work, but it's not required.
For the weekly schedule, use an array of seven days associate with each weekday. You can use the DayOfWeek enum if you like. Each element can have a pair of open/close times, as TimeSpan values. You can think of the TimeSpan like the time without the date, but really it represents duration, so when you add it to midnight of a DateTime, you'll get the full date and time of a particular day.
You might want to expand the schedule to support multiple times on each day. Perhaps IDictionary<DayOfWeek,IList<Tuple<TimeSpan,TimeSpan>>> if you want it all in one property. Or use your own classes.
When asking "is the restaurant open at 8:00 tomorrow" - make sure you know the timezone of the person who is asking. Get their time normalized to utc, then convert it to the restaurant's local time before querying.
As far as database design goes, I would just have the restaurant's schedule and list of exception dates as properties of the Restaurant entity, stored in the same document. There's not much benefit here to splitting it out into one or more separate docs IMHO.
I think you will be able to express everything your asking for in simple range queries. Stick with an inclusive/exclusive range here. [OpeningTime,ClosingTime) or in other words, OpeningTime <= timeToCheck && ClosingTime > timeToCheckYou should also have a strategy for dealing with DST if your restaurants could be open late or 24/7. You still want local time, but on fall-back transitions, do you close before or after the DST change? A simple bool setting or fixed value would cover it.
inline
On Jan 5, 2:10 am, Mircea Chirea <chirea.mir...@gmail.com> wrote:
> Alright. First of all, all time data will be stored in UTC+2/+3 (depending
> on DST) - so I can't really use UTC unless I want to complicate everything
> for no good reason, as I only have a single timezone to worry about.
That's fine that there's just a single timezone. But my advice would
be to put the timezone id as a property on the restaurant instead of
hardcoding it. You never know when in the future you might take your
code elsewhere. Use the timezone id for this, not the offset - i.e.,
the string from TimeZoneInfo.Local.Id.
>I need
> to answer the question "is it open now", as well as the question "if not,
> when will it be" (well, for the next week anyway; we assume a restaurant
> won't be closed for a full week).
>
These should both be easy enough in linq-to-objects with any structure
you can come up with. The harder question would be "give me ALL
restaurants that are open now" as a query against a raven index. It's
still quite doable. Hard to give you code examples until you lock
down the structure of the data.
> Client-side means on the server. You can't really do date computations on
> the database; I'm not sure if Lucene can even do that, but it doesn't
> matter as it all depends on DateTime.Now. Besides I don't need to filter
> based on "is open now", I need to split the results, to show the open and
> closed ones separately. This is fine on the server really.
You actually *can* do date computations and timezone conversions on
the database. You just do them while indexing in Raven. Lucene just
works with the output.
> > For the specific days they are closed or have special hours, use a
> > DateTime. A DateTimeOffset would also work, but it's not required.
>
> Right. That would translate to two lists, one for closed with just
> DateTime, another for special hours with DateTime and the start/end times.
I would try to compact this to a single list of overrides to the
recurring schedule. Maybe IList<Tuple<DateTime,DateTime,bool>> or the
equivalent. If you are closed for a whole day, then it's just another
range of special hours, but with the bool set to false instead of
true. The math will be easier than having to check two lists.
>
> > For the weekly schedule, use an array of seven days associate with each
> > weekday. You can use the DayOfWeek enum if you like. Each element can
> > have a pair of open/close times, as TimeSpan values. You can think of
> > the TimeSpan like the time without the date, but really it represents
> > duration, so when you add it to midnight of a DateTime, you'll get the full
> > date and time of a particular day.
>
> That's what I thought too, it's what I'm currently using. Of course, I hit
> a wall when I wanted to store DateTime for the close dates; if it's UTC,
> how do I "convert" the DateTime to the local time? Heck, the time specified
> there depends on DST, too!
TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, "UTC",
restaurantTimeZone)
or one of the many other conversion methods. get to know TimeZoneInfo
well if you are going to use windows timezones.
> The question is "when is it open", not "is it open tomorrow at 8"? Not
> exactly sure how to traverse the week array to leap past the end of the
> week.
Use it as a dictionary lookup against the DayOfWeek that you are
comparing. If today is Tuesday, and I want to know if the restaurant
is open, I look at Tuesday's hours.
This only works if you allow for multiple schedules on the same day
and force a cutoff at midnight. So if the restaurant is open from
11AM Friday to 2AM Saturday, store 11:00-24:00 for Friday and store
00:00-02:00 for Saturday. Since Saturday probably has a schedule
also, that's why you need to allow multiple on the same day.
The alternative would be to store 11:00-26:00 on Friday - but then you
may find it tricky to pick which day of week to evaluate.
if (hours.Start <= hours.End){return time >= hours.Start && time < hours.End;}else{// The opening time pass into the next day.return !(time >= hours.End && time < hours.Start);}
TimeInterval[] OpenHours // Start & End as TimeSpanList<DateTime> ClosedDaysList<Tuple<DateTime, TimeInterval>> SpecialHours