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?
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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With