How to Loop through Months in Ruby on Rails

2020-03-01 10:30发布

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?

4条回答
萌系小妹纸
2楼-- · 2020-03-01 10:50

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

查看更多
▲ chillily
3楼-- · 2020-03-01 10:53
time_start = 13.months.ago
time_end = 0.seconds.ago
time = time_start
while time < time_end
  puts("m:#{time.month} y:#{time.year}")
  time = time.advance(months: 1)
end
查看更多
可以哭但决不认输i
4楼-- · 2020-03-01 10:58

Here's one way to do it:

number_of_months = 0..11
number_of_months.to_a.reverse.each do |month_offset|
  start_date = month_offset.months.ago.beginning_of_month
  end_date = month_offset.months.ago.end_of_month
  puts "Start date : #{start_date} End date : #{end_date}"
end

=>

Start date : 2012-01-01 00:00:00 -0700 End date : 2012-01-31 23:59:59 -0700
Start date : 2012-02-01 00:00:00 -0700 End date : 2012-02-29 23:59:59 -0700
Start date : 2012-03-01 00:00:00 -0700 End date : 2012-03-31 23:59:59 -0600
Start date : 2012-04-01 00:00:00 -0600 End date : 2012-04-30 23:59:59 -0600
Start date : 2012-05-01 00:00:00 -0600 End date : 2012-05-31 23:59:59 -0600
Start date : 2012-06-01 00:00:00 -0600 End date : 2012-06-30 23:59:59 -0600
Start date : 2012-07-01 00:00:00 -0600 End date : 2012-07-31 23:59:59 -0600
Start date : 2012-08-01 00:00:00 -0600 End date : 2012-08-31 23:59:59 -0600
Start date : 2012-09-01 00:00:00 -0600 End date : 2012-09-30 23:59:59 -0600
Start date : 2012-10-01 00:00:00 -0600 End date : 2012-10-31 23:59:59 -0600
Start date : 2012-11-01 00:00:00 -0600 End date : 2012-11-30 23:59:59 -0700
Start date : 2012-12-01 00:00:00 -0700 End date : 2012-12-31 23:59:59 -0700 
查看更多
姐就是有狂的资本
5楼-- · 2020-03-01 11:13
(start_date..end_date).select{|date| date.day == 1}.map{|date| [date.beginning_of_month, date.end_of_month]}
查看更多
登录 后发表回答