Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Loop through Months in Ruby on Rails

I need to loop through several months in a Ruby on Rails application. For each month, I need to find the first and last day of that given month. These dates are then used in a separate query to find events occurring during those dates and running calculations on them.

Initially, I tried something like:

(11.months.ago.to_date.month..Date.today.month).each do |m|
  start_date = '01-#{m}-#{Date.today.year}'.to_date.beginning_of_month
  end_date = '01-#{m}-#{Date.today.year}'.to_date.end_of_month
end

Of course, the year isn't updated in this case in the event that 11 months ago involves going back to the previous year. And, I don't think this type of for/each loop works. I also tried mapping the numbers to an array and using the same method, but received an error.

What's the best way to accomplish something like this?

like image 389
Justin Avatar asked Dec 16 '12 05:12

Justin


2 Answers

(start_date..end_date).select{|date| date.day == 1}.map{|date| [date.beginning_of_month, date.end_of_month]}
like image 199
tehtorq Avatar answered Sep 19 '22 11:09

tehtorq


First, count the number of months between two dates (courtesy of Massimiliano Peluso):

start_date = 13.months.ago.to_date
# => Wed, 16 Nov 2011

end_date = Date.today
# => Sun, 16 Dec 2012

number_of_months = (end_date.year*12+end_date.month)-(start_date.year*12+start_date.month)
# => 13

Then from the start month, counting each month thereafter, find the first/last dates and append to an accumulator array.

dates = number_of_months.times.each_with_object([]) do |count, array|
  array << [start_date.beginning_of_month + count.months,
            start_date.end_of_month + count.months]
end
# => ...

Now dates will contain an array with nested Date pairs corresponding to the first and last date of each month. This dates array will be easy to iterate for processing.

dates
# => [[Tue, 01 Nov 2011, Wed, 30 Nov 2011], [Thu, 01 Dec 2011, Fri, 30 Dec 2011], ...

dates[0].first
# => Tue, 01 Nov 2011

dates[0].last
# => Wed, 30 Nov 2011

dates[0].last.class
# => Date

This is tested and working in Rails 3.2.5/Ruby 1.9.3p194

like image 38
Substantial Avatar answered Sep 20 '22 11:09

Substantial