Rails how to create an array of months based on a range of dates
require 'date'
date_from = Date.parse('2011-10-14')
date_to = Date.parse('2012-04-30')
date_range = date_from..date_to
date_months = date_range.map {|d| Date.new(d.year, d.month, 1) }.uniq
date_months.map {|d| d.strftime "%d/%m/%Y" }
# => ["01/10/2011", "01/11/2011", "01/12/2011", "01/01/2012",
# "01/02/2012", "01/03/2012", "01/04/2012"]
Rails ActiveSupport core extensions includes a method for Date: beginning_of_month. Your function could be written as follows:
def beginning_of_month_date_list(start, finish)
(start.to_date..finish.to_date).map(&:beginning_of_month).uniq.map(&:to_s)
end
Caveats: this could be written more efficiently, assumes start and finish are in the expected order, but otherwise should give you the months you're looking for. You could also rewrite to pass a format symbol to the #to_s method to get the expected month format.
I was curious about performance here so I tested some variations. Here's a solution better optimized for performance (about 8x faster in my benchmark than the accepted solution). By incrementing by a month at a time we can remove the call to uniq
which cuts quite a bit of time.
start_date = 1.year.ago.to_date
end_date = Date.today
dates = []
date = start_date.beginning_of_month
while date <= end_date.beginning_of_month
dates << date.to_date.to_s
date += 1.month
end
dates
#=> ["2019-02-01", "2019-03-01", "2019-04-01", "2019-05-01", "2019-06-01", "2019-07-01", "2019-08-01", "2019-09-01", "2019-10-01", "2019-11-01", "2019-12-01", "2020-01-01", "2020-02-01"]
Benchmark Results:
Comparison:
month increment loop: 17788.3 i/s
accepted solution: 2140.1 i/s - 8.31x slower
gist of the benchmark code