0

I have two time columns stored in a Postgresql database: open_time and close_time. I'm trying to find out if the current time, ignoring the date, is between the two times, ignoring the dates.

This code compares the dates as well as the time:

current_time = Time.now
if current_time.between?(store.open_time, store.close_time)
  puts "IN BETWEEN"      
end

It doesn't work, for example, when current_time # => 2018-06-06 23:59:49 -0600 and open_time # => 2000-01-01 22:59:00 UTC.

How do I get it to not include the dates, and just compare the times?

Stefan
  • 109,145
  • 14
  • 143
  • 218
NathanB
  • 105
  • 6
  • That doesn't make sense. Time is a more precise notion than date. You cannot have a particular time without the notion of which date it is. – sawa Jun 07 '18 at 06:14
  • @sawa eh? Postgres [perfectly allows](https://www.postgresql.org/docs/9.1/static/datatype-datetime.html) storing times without a date and the fact that ruby does not provide a handy wrapper for it does not mean “time is more precise.” `DateTime` is. – Aleksei Matiushkin Jun 07 '18 at 06:19
  • [Comparing times only, without dates?](https://stackoverflow.com/q/14329198/477037) is a similar question, although not Postgres specific. – Stefan Jun 07 '18 at 07:54
  • 1
    @Stefan, a comment at the link you gave suggests that the earlier question is in fact Postgres-related, but it would be a shame to regard this question a dup because I do not believe any of the earlier answers are correct. – Cary Swoveland Jun 07 '18 at 18:56
  • I really appreciate all the answers! I ended up taking ideas from all the answers, so I'm not really sure which of them to mark. – NathanB Jun 07 '18 at 21:36

4 Answers4

5
require 'time'

TIME_FMT = "%H%M%S"

def store_open_now?(open_time, close_time)
  nt = Time.now.strftime(TIME_FMT)
  ot = open_time.strftime(TIME_FMT)
  ct = close_time.strftime(TIME_FMT)
  ot <= ct ? (nt >= ot && nt <= ct) : (nt >= ot || nt <= ct)
end

As I write, the time is now about 32 minutes past midnight.

Time.now.strftime(TIME_FMT)
  #=> "003252"

Suppose

open_time  = DateTime.parse("09:00")
  #=> #<DateTime: 2018-06-07T09:00:00+00:00 ((2458277j,32400s,0n),
  #               +0s,2299161j)>
close_time = DateTime.parse("17:00")
  #=> #<DateTime: 2018-06-07T17:00:00+00:00 ((2458277j,61200s,0n),
  #               +0s,2299161j)>

Then

open_time.strftime(TIME_FMT)
  #=> "090000"
close_time.strftime(TIME_FMT)
  #=> "170000"
store_open_now?(open_time, close_time)
  #=> false

Now suppose the open time is the same, but the close time is later.

close_time = DateTime.parse("01:00")
  #=> #<DateTime: 2018-06-07T01:00:00+00:00 ((2458277j,3600s,0n),
  #               +0s,2299161j)>

Then

close_time.strftime(TIME_FMT)
  #=> "010000"
store_open_now?(open_time, close_time)
  #=> true
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
4

Perhaps you want something like this:

current_time = Time.now
open_time = store.open_time
close_time = store.close_time
current_time -= current_time.beginning_of_day
open_time -= open_time.beginning_of_day
close_time -= close_time.beginning_of_day
if current_time.between?(open_time, close_time)
  puts "IN BETWEEN"      
end

or

current_time = Time.now
open_time = store.open_time
close_time = store.close_time
current_time = [current_time.hour, current_time.min, current_time.sec]
open_time = [open_time.hour, open_time.min, open_time.sec]
close_time = [close_time.hour, close_time.min, close_time.sec]
if open_time <=> current_time == -1 and current_time <=> close_time == -1
  puts "IN BETWEEN"      
end
sawa
  • 165,429
  • 45
  • 277
  • 381
2

You could CAST() your datetime to time by using,

cast(tbl_store.open_time as time) as SomeVariable

cast(tbl_store.close_time as time) as SomeOtherVariable

That would give you the time only instead of the full datetime value that you had to begin with, which is what you wanted.

You can then use the same logic with your curtime() between to the get value that you were looking for.

Example:

SELECT
  CAST(tbl_store.open_time as TIME) as open_time,
  CAST(tbl_store.close_time as TIME) as close_time,
  CURTIME() BETWEEN (cast(tbl_store.open_time as TIME)) AND (cast(tbl_store.close_time as TIME)) as time_between
FROM
  tbl_store

Working SQL Fiddle

You can change the schema build in the fiddle to test the datetime values you desire.

Note that if you ever have a logic that will include midnight time, you will have to make a CASE WHEN logic against that, else it will fail and return 0, whereas it should return 1.

Martin
  • 2,326
  • 1
  • 12
  • 22
0

You can take advantage of ranges and how numeric strings are compared

r = Range.new('09:00', '18:00')

r.include?('08:59') # => false
r.include?('09:01') # => true
r.include?('18:01') # => false

Then we could use

open_hours_range = Range.new(open_time.strftime('%R'), close_time.strftime('%R'))
shop_open? = open_hours_range.include?(Time.now.strftime('%R'))
lunohodov
  • 5,179
  • 2
  • 25
  • 17