Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you store a business's hours in the db/model of a Rails app?

I'm creating a Rails app that will store the opening and closing hours for a business. Originally, I thought of simply using a text data type and letting it be free-form:

"Monday to Friday 9am to 5pm
Saturday 11am to 4pm
Closed Sundays"

But, requirements have changed and I need to check the hours against the current date & time and display an "Open" or "Closed" in the view. Something like:

class Business < ActiveRecord::Base

  def open?
    # Something like ... 
    Time.now > open_time && Time.now < close_time
  end

end

So what would be the best way to tackle this in terms of storing the hours for each day of the week? Should the Business simply has_many :open_blocks (or whatever) that have open and close times? Should I just store the day as a string?

like image 205
Callmeed Avatar asked Jul 28 '10 00:07

Callmeed


3 Answers

I'm currently setting up a directory listing for a client and we want give the user more flexibility. So: Let the user set up blocks for days:

We have a day (integer 1-7), opens (time), closes (time) and some shops or sights have summer and winter opening times, or they make vacations. According to schema.org you have to add a valid_from (datetime) and a valid_through (datetime).

With this setup the user can create whatever he wants:

# migration
class CreateOpeningHours < ActiveRecord::Migration
  def change
    create_table :opening_hours do |t|
      t.integer :entry_id # your model reference
      t.integer :day
      t.time :closes
      t.time :opens
      t.datetime :valid_from
      t.datetime :valid_through
    end
  end
end

Example for the model:

class OpeningHour < ActiveRecord::Base

  belongs_to :entry

  validates_presence_of :day, :closes, :opens, :entry_id
  validates_inclusion_of :day, :in => 1..7
  validate :opens_before_closes 
  validate :valid_from_before_valid_through 

  # sample validation for better user feedback
  validates_uniqueness_of :opens, scope: [:entry_id, :day]
  validates_uniqueness_of :closes, scope: [:entry_id, :day]

  protected
  def opens_before_closes
    errors.add(:closes, I18n.t('errors.opens_before_closes')) if opens && closes && opens >= closes
  end

  def valid_from_before_valid_through
    errors.add(:valid_through, I18n.t('errors.valid_from_before_valid_through')) if valid_from && valid_through && valid_from >= valid_through
  end

end

With that setup you can create easily a is_open? method in your model. Currently I did not setup the is_open? method, but if somebody needs, give me a hit! I think I will finish it in the next days.

like image 187
Simon Franzen Avatar answered Oct 19 '22 03:10

Simon Franzen


In this case, I would probably do something relational, perhaps with STI if you wanted to have certain days where the business is closed (e.g. non-recurring closings). Here's a basic STI example:

class Business < ActiveRecord::Base
  has_many :open_time_blocks
  has_many :closed_time_blocks

  def open?(time)
    # false if time is "inside" any of the closed_time_blocks
    # else is true if inside any of the open_time_blocks
    # else is false
  end

  def closed?(time)
    !open?
  end
end

# == Schema Information
#
# Table name: time_blocks
#
#  id          :integer         not null, primary key
#  business_id :integer
#  type        :string(255)
#  start_at    :datetime
#  end_at      :datetime
#  created_at  :datetime
#  updated_at  :datetime
class TimeBlock < ActiveRecord::Base
  belongs_to :business
end

class OpenTimeBlock < TimeBlock; end
class ClosedTimeBlock < TimeBlock; end
like image 21
Michelle Tilley Avatar answered Oct 19 '22 03:10

Michelle Tilley


I would say that Operating Hours belong to a Party Address, and should be represented by an RFC 5455 RRULE and optionally an EXRULE or EXDATEs.

Let's say you have a Party:

"Acme, Inc."

They have one PhysicalAddress:

"123 Main Street, Vancouver"

Their address plays two roles:

"Sales" and "Service"

Sales is open Mo-Sa 10-8 and Service is open Mo-Fr 9-5

These are the RRULES:

(Sales)

startTime:'10:00:00'
endTime:'20:00:00'
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA

(Service)

startTime:'09:00:00'
endTime:'17:00:00'
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
like image 40
Neil McGuigan Avatar answered Oct 19 '22 02:10

Neil McGuigan