Skip to main content

How do I manage business hours for a store?

Databases • Asked by Taylor Cooney

I've taken a few stabs at this over the past two months and haven't found a great solution for what seems to be a straightforward feature seen in applications such as SkipTheDishes and UberEats.

Given a Store model, what are the associations needed to manage their business hours and how is that form rendered? Ideally, when a user creates a Store, they should be prompted to add their business hours. Maybe default hours are generated via callback?

Looking for some insight if anyone has experience with this.


Hey Taylor, I just built this for a project actually so I have some timely experience for ya. :)

Postgres has a Time column you can use that doesn't include a date. This works perfect.

The TimeOfDay gem you can use to serialize those time columns into a TimeOfDay object which is a time without a date. That comes in handy since that's what business hours really are.

We decided to setup 14 columns, a start and end time for each day of the week. You could also save 14 attributes to a jsonb column instead of 14 separate columns. Those two approaches would work about the same and you could query against either one.

As for the form, same as usual. You build a select field that selects a time, and submits it. Make sure the attributes are permitted and the TimeOfDay serialization will make sure it gets cast to a time in the database.

I made a little helper to generate a select options from 12am to 11:45pm every 15 minutes to make selecting times easier.


Ah, this is great insight. To clarify, you setup 14 columns on the model, leaving a Store model with a number of columns around their operating hours? I need to implement both business_hours and let's say, delivery_hours (hours that delivery is available) so that double the number of colums which makes the jsonb column a great option - thanks for outlining that.

I'm going to take a stab and then I'll try to share a gist if I can get it to work for others.


This still is stumping me; @ChrisOliver could you share a gist possible to shed some more light?


I'm doing this in a table in the form:

 <% I18n.t('date.day_names').each_with_index do |day, wday| %>
            <td class="day">
              <div class="form-group">
                <%= form.select :"#{day.downcase}_opens_at", time_select_options, label: "Open Time", include_blank: "Closed" %>
              </div>

              <div class="form-group">
                <%= form.select :"#{day.downcase}_closes_at", time_select_options, label: "Close Time", include_blank: "Closed" %>
              </div>
            </td>
          <% end %>

That creates selects for each time of day. I have this helper for those times.

module ApplicationHelper
  def time_select_options(step=15)
    tod = Tod::TimeOfDay.new 0
    times = []
    96.times do
      times << [tod.strftime("%l:%M %P"), tod]
      tod = tod + step.minutes
    end
    times
  end

Then it gets serialized by Time Of Day gem in the model.

class Business
  serialize :sunday_opens_at, Tod::TimeOfDay
  serialize :monday_opens_at, Tod::TimeOfDay
  serialize :tuesday_opens_at, Tod::TimeOfDay
  serialize :wednesday_opens_at, Tod::TimeOfDay
  serialize :thursday_opens_at, Tod::TimeOfDay
  serialize :friday_opens_at, Tod::TimeOfDay
  serialize :saturday_opens_at, Tod::TimeOfDay
end

Thanks @ChrisOliver. I got it working with your help! I really appreciate it, it seems rather trivial but a core function of my app - also, it seems like between the two of us we've provided the most insight to setting up store hours. I could find limited information around this, again, for what seems to be a trivial feature.

Here is the full gist of the commit...I also added an index to the column using GIN (not listed in the gist).

Let me know if you have any feedback


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 22,346+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.