Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correctly handling opening times with NodaTime

I'm currently writing a fairly simple app handling opening/closing times of businesses and running into serious difficulties trying to figure out how to properly store the info.

Most of our critical functionality is heavily dependent on getting times absolutely perfect, so obviously I want to get things done the best way possible to start off with!

Additionally, the data will be inputted by users, so if the underlying representation is slightly more complex (e.g. using TimeSpans to account for opening past midnight), this needs to be invisible to the user.

I need to store firstly, the business's opening hours, by day of week, with a timezone associated with them, e.g:

- M:  1000 - 2330
- T:  1000 - 0030
- W:  1900 - 0300
- Th: 2000 - 0300
- F:  2000 - 0800
- Sa: 1000 - 0500
- Su: 1000 - 2300

I'm currently thinking that the best way to store this is using a class like this:

public class OpeningHours
{
    ZonedDateTime OpeningTime { get; set; }
    Period durationOpen { get; set; }

    // TODO: add a method to calculate ClosingTime as a ZonedDateTime
}

However, there's 2 main complications here:

  • I don't want to store the Year, Month, or Date part of the ZonedDateTime - I just care about the DayOfWeek.

    Sure, I could just store each value as the first Monday/Tuesday etc after Jan 1 1970, but this seems hacky and pretty much plain wrong - as the author of NodaTime, very correctly, explains here when talking about the limitations of the BCL DateTime implementation. I also have a feeling this would probably end up with weird quirky bugs if later on we try and do any arithmetic with the dates.

  • The user is going to have to input the ClosingTime anyway. Client side I suppose I could do something simple like always assume the ClosingTime is the next day if it's before the OpeningTime, but again, it's not perfect, and also doesn't account for places that might be open for more than 24 hours (e.g. supermarkets)

Another thing I've considered is using a table with hours/days and letting people highlight the hours of the week to pick opening times, but you still run into the same problem with only wanting to store the DayOfWeek part of the OpeningTime.

Any suggestions would be appreciated, spending the last 6 hours reading about the hilariously silly ways we humans represent time has burnt me out a bit!

like image 419
Josh P Avatar asked Mar 31 '13 22:03

Josh P


1 Answers

I would strongly consider using LocalTime instead of ZonedDateTime, for a couple of reasons:

  • You're not trying to represent a single instant in time; these are naturally recurring patterns (there's no associated date)
  • You're not trying to cope with the situation where the store is in different time zones for different opening hours; you probably want to associate a time zone with each store once, and then you can apply that time zone whenever you want

So I would have something like this (showing just the data members; how you sort out the behaviour is a separate matter):

public class StoreOpeningPeriod
{
    IsoDayOfWeek openingDayOfWeek;
    LocalTime openingTime;
    LocalTime closingTime;
}

Note that this exactly follows your original data as you've shown it, which is always a good sign - you're neither adding nor losing information, and it's presumably in a convenient form.

If the closing time is earlier than the opening time, it's assumed that this crossed midnight - you might want to add a confirmation box for the user if this is relatively uncommon, but it's certainly easy to spot and handle in code.

like image 172
Jon Skeet Avatar answered Oct 19 '22 23:10

Jon Skeet