Reputation: 48453
I am trying to loop through years and months. If @user.created_at
is November 2015
, then I'd like to get this output:
November 2015
December 2015
January 2016
February 2016
March 2016
I tried the following:
Hash[(@user.created_at.month..Date.today.month).map { |month| [ month, 0 ] }].merge(...)
But the above code returns
stack level too deep
because it loops 11..3
.
How can I figure out this issue?
Upvotes: 3
Views: 2331
Reputation: 1278
Adding months to a date object in Rails using the Integer#month()
method will take care of all the wrapping of years for you. It's also smart enough to not skip a month with less than 31 days should the user's join date fall on the 31st of some month. If you add a month to January 31st, the return value will be February 28th (or 29th in the case of a leap year).
date_joined = @user.created_at
now = Time.now
until date_joined.month > now.month && date_joined.year == now.year
puts date_joined.strftime("%B %Y")
date_joined += 1.month
end
Upvotes: 0
Reputation: 110685
This is a pure-Ruby solution.
require 'date'
If given
prev_date = "November 2015"
m = Date.strptime(prev_date,"%B %Y")
#=> #<Date: 2015-11-01 ((2457328j,0s,0n),+0s,2299161j)>
then
n = Date.today
n -= n.day - 1
#=> #<Date: 2016-03-01 ((2457449j,0s,0n),+0s,2299161j)>
while m <= n
puts "#{Date::MONTHNAMES[m.month].ljust(8)} #{m.year}"
m = m >> 1
end
November 2015
December 2015
January 2016
February 2016
March 2016
Upvotes: 0
Reputation: 80065
require 'date'
from_date = Date.new(2015, 11)
to_date = Date.today
until from_date > to_date do
puts from_date.strftime("%B %Y")
from_date = from_date.next_month
end
#November 2015
#December 2015
#January 2016
#February 2016
#March 2016
Upvotes: 2
Reputation: 106027
One thing you can do is turn each date into a "month number" that's the year multiplied by 12 plus the zero-based month, and then iterate over them, e.g.:
from_date = Date.new(2015, 11) # Nov. 1, 2015
to_date = Date.today # Mar. 22, 2016
nov2015 = from_date.year * 12 + (from_date.month - 1)
# => 24190
nov2015.divmod(12)
# => [2015, 10]
mar2016 = to_date.year * 12 + (to_date.month - 1)
# => 24194
mar2016.divmod(12)
# => [2016, 2]
Remember that the months are zero-based, so 2
is March and 10
is November. As expected, nov2015
and mar2016
have a difference of four. Now we can iterate from one to the other:
nov2015.upto(mar2016) do |month_num|
year, month = month_num.divmod(12)
puts Date.new(year, month + 1).strftime("%B %Y")
end
# => November 2015
# December 2015
# January 2016
# February 2016
# March 2016
Perfect! But it's better if we can put this into a method that returns an Enumerator so we can use any of the Enumerable methods (each
, map
, each_cons
, you name it) on it:
def months_enum(from_date, to_date)
from = from_date.year * 12 + from_date.month - 1
to = to_date.year * 12 + to_date.month - 1
Enumerator.new do |y|
from.upto(to) do |n|
year, month = n.divmod(12)
y << Date.new(year, month + 1)
end
end
end
Then:
from = Date.new(2015, 11, 1)
to = Date.today
months_enum(from, to).each do |date|
puts date.strftime("%Y-%m")
end
# -> 2015-11
# 2015-12
# 2016-01
# 2016-02
# 2016-03
p months_enum(from, to).map {|date| date.strftime("%B %Y") }
# => [ "November 2015",
# "December 2015",
# "January 2016",
# "February 2016",
# "March 2016" ]
Upvotes: 2
Reputation: 211590
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
Upvotes: 4
Reputation: 999
I would begin with the user created_at date and loop through until it has caught up to today:
start = @user.created_at
months = []
while start <= Time.zone.now
months << [start.strftime('%B'), start.year]
start += 1.month
end
months
Result:
=> [["November", 2015], ["December", 2015], ["January", 2016], ["February", 2016], ["March", 2016]]
This way you don't have to calculate the difference in months between created_at and now.
Upvotes: 0