Method 1: EXTRACT
You can use EXTRACT
to get the hour from order_date
:
Order.where('EXTRACT(hour FROM order_date) BETWEEN ? AND ?', 7, 20)
Note that I specified 20
(8 pm) as the upper range. If I used 21
then order_date
with a time component of 21:30 would match which is not what we want.
You can wrap the query above in a scope:
class Order < ActiveRecord::Base
scope :between, -> (start_hour, end_hour) {
where('EXTRACT(hour FROM order_date) BETWEEN ? AND ?', start_hour, end_hour - 1)
}
end
There are also some other concerns you may like to address, for example checking start_hour
and end_hour
for valid values (e.g an hour of -1 is invalid).
This method is simple but is limited to full hours (you can add minutes and seconds but the method would become convoluted).
Method 2: Explicit cast to time
Time
in Ruby stores both a date and time. For example:
Time.parse('15:30:45') # => 2017-01-02 15:30:45 +0100
You can pass Time
to PostgreSQL and cast it to Postgres time
type explicitly:
class Order < ActiveRecord::Base
scope :between, -> (start_time, end_time) {
where('order_date::time BETWEEN ?::time AND ?::time', start_time, end_time)
}
end
The advantage of this method is flexibility. The downside is a bit more complexity in handling the time in the UI. The scope expects a pair of Time
objects. There are many ways to obtain them, for example: Time.parse('12:17')
.