23

Example I have:

  range = start.to_date..(end.to_date + 1.day)

end and start are dates.

How do I create a month array based on this range?

Example:

I have the dates 23/1/2012 and 15/3/2012

The months are Januar, Februar and Marts.

I want to get a array like ["1/1/2012", "1/2/2012", "1/3/2012"]

and if the range was betweeen 25/6/2012 to the 10/10/2012

the array would be: ["1/6/2012", "1/7/2012", "1/8/2012", "1/9/2012", "1/10/2012"]

Rails beginner
  • 14,321
  • 35
  • 137
  • 257

4 Answers4

37
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"] 
Lars Haugseth
  • 14,721
  • 2
  • 45
  • 49
10

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.

rossta
  • 11,394
  • 1
  • 43
  • 47
  • I have tried this without luck. I need to use it for my loop, see more here: http://stackoverflow.com/questions/12544533/rails-how-to-create-chart-data-for-each-month-between-2-dates – Rails beginner Sep 22 '12 at 14:20
  • It would help to see the result you're getting. – rossta Sep 22 '12 at 14:24
5

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

Jared
  • 2,885
  • 2
  • 28
  • 31
0

Similar to one of the solutions above using beginning_of_month .. but taking less space (by using Set) and is neater for using inject.

(start_month..end_month).inject(Set.new) { |s, i| s << i.beginning_of_month; s }.to_a
saGii
  • 447
  • 5
  • 14