How to Loop through Months in Ruby on Rails
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
Rails: How to loop through month?
With the best_of_the_month
scope defined to take month and year as params, the following code should work:
date = Date.new(2014,1,1)
@monthly_videos = []
while true
videos = Video.best_of_the_month(date.year, date.month)
@monthly_videos << videos
date += 1.month
break if date == Date.today.beginning_of_month
end
Loop through years + months
If you want to compute the upcoming months/years you could always use advance
:
start = @user.created_at.to_date.beginning_of_month
Hash[(0..6).collect { |n| [ start.advance(months: n).month, 0 ] }]
That should properly step through days/months. You may want to just stick in dates instead of just the month number.
If you want to do "up to today" then try this:
date = @user.created_at.to_date.beginning_of_month
stop = Date.today.beginning_of_month
hash = { }
while (date <= stop)
hash[date] = 0
date = date.advance(months: 1)
end
Ruby/Rails how to iterate months over a DateTime range?
Use groupdate gem. For example (modified example from the docs):
visible_products = Product.where("created_at > ?", 1.week.ago).group_by_day
# {
# 2015-07-29 00:00:00 UTC => 50,
# 2013-07-30 00:00:00 UTC => 100,
# 2013-08-02 00:00:00 UTC => 34
# }
Also, this will be much faster, because your grouping/counting will be done by database itself, without the need to pass all the records via Product.all
call to your Rails code, and without the need to create ActiveRecord object for each one (even irrelevant).
Loop through years + months
If you want to compute the upcoming months/years you could always use advance
:
start = @user.created_at.to_date.beginning_of_month
Hash[(0..6).collect { |n| [ start.advance(months: n).month, 0 ] }]
That should properly step through days/months. You may want to just stick in dates instead of just the month number.
If you want to do "up to today" then try this:
date = @user.created_at.to_date.beginning_of_month
stop = Date.today.beginning_of_month
hash = { }
while (date <= stop)
hash[date] = 0
date = date.advance(months: 1)
end
Iterate every month with date objects
I have added following method to Date class:
class Date
def all_months_until to
from = self
from, to = to, from if from > to
m = Date.new from.year, from.month
result = []
while m <= to
result << m
m >>= 1
end
result
end
end
You use it like:
>> t = Date.today
=> #<Date: 2009-11-12 (4910295/2,0,2299161)>
>> t.all_months_until(t+100)
=> [#<Date: 2009-11-01 (4910273/2,0,2299161)>, #<Date: 2009-12-01 (4910333/2,0,2299161)>, #<Date: 2010-01-01 (4910395/2,0,2299161)>, #<Date: 2010-02-01 (4910457/2,0,2299161)>]
Ok, so, more rubyish approach IMHO would be something along:
class Month<Date
def succ
self >> 1
end
end
and
>> t = Month.today
=> #<Month: 2009-11-13 (4910297/2,0,2299161)>
>> (t..t+100).to_a
=> [#<Month: 2009-11-13 (4910297/2,0,2299161)>, #<Month: 2009-12-13 (4910357/2,0,2299161)>, #<Month: 2010-01-13 (4910419/2,0,2299161)>, #<Month: 2010-02-13 (4910481/2,0,2299161)>]
But you would need to be careful to use first days of month (or implement such logic in Month)...
Loop through years and months Ruby On Rails
month, year = nil, nil
Post.order("created_at desc") do |post|
if year != post.created_at.year
year = post.created_at.year
puts year
end
if month != post.created_at.month
month = post.created_at.month
puts "\t#{post.created_at.strftime("%B")}"
end
puts "\t\t#{post.name}"
end
Iterating months using ruby
ActiveSupport
has beginning_of_month and end_of_month:
d = Date.today
#=> Fri, 13 Jul 2012
d.beginning_of_month
#=> Sun, 01 Jul 2012
d.end_of_month
#=> Tue, 31 Jul 2012
You can use Date#>> to shift dates forward monthwise:
(d>>2).end_of_month
#=> Sun, 30 Sep 2012
(d>>4).beginning_of_month
#=> Thu, 01 Nov 2012
Rails Iteration by month given range of dates
For anyone else facing the same problem, the solution is outlined below:
def self.chart_data(start = 1.year.ago)
total_count = total_count_by_month(start)
##############################################
start = start.to_date.beginning_of_month
today = Date.today.beginning_of_month
range = (start..today).select {|d| d.day == 1}
##############################################
range.map do |month|
{
created_at: month,
total_enquiries: total_count[] || 0
}
end
end
The chart's x-axis now iterates by month.
The solution is found here.
I'm still looking for solutions on how the chart dates might display (%b %Y) as opposed to the current format of (yyyy-mm-dd).
Ruby: Iterate over days in previous month
require 'date'
d = Date.today << 1 # << 1 is one month earlier
(Date.new(d.year, d.month, 1)..Date.new(d.year, d.month,-1)).each{|date| p date}
Related Topics
You Don't Have Write Permissions into the /Var/Lib/Gems/1.9.1 Directory
Rails - How to Check for Online Users
Case Expression Different in Ruby 1.9
/Usr/Bin/Env Ruby_Noexec_Wrapper Fails with No File or Directory
How to Integrate 'Premailer' with Rails
Need to Use Add_Index on Migration for Belongs_To/Has_Many Relationship? (Rails 3.2, Active Record)
How to Inherit from Nilclass or How to Simulate Similar Function
How to Remove the Zone from a Datetime Value
How to Express 'Infinite Time'
Why Does Hash.New({}) Hide Hash Members
Ruby Incorrectly Parses 2 Digit Year
How to Use Activesupport::Configurable with Rails Engine
How to Destroy a Record Without an Id Column in Ruby Activerecord
Rails 5.0.0 When Installing "Nio4R":Failed to Build Gem Native Extension
How to Get the Number of Elements Having Same Attribute in HTML in Watir