Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Rails way to define tomorrow's date?

In a Rails 3.2 app, I have a query defined to return all Event items with :due_date equal to today.

@due_today = Event.where(:due_date => Time.now.beginning_of_day..Time.now.end_of_day)

I want to modify this to return all events due today and tomorrow.

What is the best way to do this?

I know there are several options available to me:

Date.tomorrow
Date.current.tomorrow
Date.now.tomorrow
DateTime.now.tomorrow.to_date
Time.now.tomorrow.to_date
Date.current+1

I'm sure there are others. Are all of these interchangeable? Are there any differences in performance? Are there any issues associated with different approaches? I'd welcome any suggestions as to which is the best way to do this.

For extra kudos: I also want to display the :due_date as Today HH:MM or Tomorrow HH:MM, where HH:MM is the time. Is there a method baked in to Rails to display dates as Today or Tomorrow? Or will I need to define my own scope?

Many thanks!

like image 313
Andy Harvey Avatar asked Mar 02 '12 08:03

Andy Harvey


2 Answers

Benchmarking these, I get:

N = 100000
Benchmark.bmbm do |test|
  test.report("Date.tomorrow") do
    N.times do
      x = Date.tomorrow
    end
  end
  test.report("Date.current.tomorrow") do
    N.times do
      x = Date.current.tomorrow
    end
  end
  # test.report("Date.now.tomorrow") # => Coughs up an exception, Date.now doesn't exist!
  test.report("DateTime.now.tomorrow.to_date") do
    N.times do 
      x = DateTime.now.tomorrow.to_date
    end    
  end
  test.report("Time.now.tomorrow.to_date") do
    N.times do
      x = Time.now.tomorrow.to_date
    end
  end
  test.report("Date.current+1") do
    N.times do
      x = Date.current+1
    end
  end
  test.report("DateTime.tomorrow.to_date") do
    N.times do 
      x = DateTime.tomorrow.to_date
    end    
  end
end

Results:

Rehearsal -----------------------------------------------------------------
Date.tomorrow                   1.640000   0.010000   1.650000 (  1.662668)
Date.current.tomorrow           1.580000   0.000000   1.580000 (  1.587714)
DateTime.now.tomorrow.to_date   0.360000   0.010000   0.370000 (  0.363281)
Time.now.tomorrow.to_date       4.270000   0.010000   4.280000 (  4.303273)
Date.current+1                  1.580000   0.010000   1.590000 (  1.590406)
DateTime.tomorrow               0.160000   0.000000   0.160000 (  0.164075)
-------------------------------------------------------- total: 9.630000sec

                                    user     system      total        real
Date.tomorrow                   1.590000   0.000000   1.590000 (  1.601091)
Date.current.tomorrow           1.610000   0.010000   1.620000 (  1.622415)
DateTime.now.tomorrow.to_date   0.310000   0.000000   0.310000 (  0.319628)
Time.now.tomorrow.to_date       4.120000   0.010000   4.130000 (  4.145556)
Date.current+1                  1.590000   0.000000   1.590000 (  1.596724)
DateTime.tomorrow               0.140000   0.000000   0.140000 (  0.137487)

From your list of suggestions, DateTime.now.tomorrow.to_date is faster.

Check out the last option I've added though, it returns a Date object and is the fastest of the bunch by a country mile. It's also one of the most human-readable from the list.

Assuming you're using MYSQL, your query might be faster if you use MySQL's BETWEEN() function:

@due_today = Event.where("due_date BETWEEN ? AND ?", DateTime.today, DateTime.tomorrow)

Although I'm not sure if you have indexes on events.due_date or if BETWEEN will still use these. You'll have to benchmark both to see which is quicker with a large DATA set.

Hope that helps?

like image 67
bodacious Avatar answered Sep 19 '22 18:09

bodacious


How about this?

1.day.from_now
2.days.from_now
3.days.from_now

If you want to increment given time..

1.day.from_now(start_time) # gives a day after the start time 
like image 23
Harish Shetty Avatar answered Sep 19 '22 18:09

Harish Shetty