So I have two ruby Date objects, and I want to iterate them every month. For example if I have Date.new(2008, 12) and Date.new(2009, 3), it would yield me 2008-12, 2009-1, 2009-2, 2009-3 (as Date objects of course). I tried using range, but it yields every day. I saw step method for Date however it only allows me to pass number of days (and each month has different number of those). Anyone have any ideas?
+4
A:
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)...
Mladen Jablanović
2009-11-12 19:26:28
Was hoping for something more rubyish...
Andrius
2009-11-12 22:51:40
Well, the only "rubyish" approach that comes to my mind would be defining "Month" class (by inheriting Date), defining succ method and using Range on it.
Mladen Jablanović
2009-11-13 09:29:51
Thanks Mladen! This all_months_until method was exactly what I was looking for.
jspooner
2010-09-16 23:11:42
+1
A:
I find that I need to do this sometimes when generating select lists of months. The key is the >>
operator on Date, which advances the Date forward one month.
def months_between(start_month, end_month)
months = []
ptr = start_month
while ptr <= end_month do
months << ptr
ptr = ptr >> 1
end
months
end
results = months_between(Date.new(2008,12), Date.new(2009,3))
Of course, you can format the results however you like in the loop.
months << "#{Date::MONTHNAMES[ptr.month]} #{ptr.year}"
Will return the month name and year ("March 2009"), instead of the Date object. Note that the Date objects returned will be set on the 1st of the month.
Jonathan Julian
2009-11-12 19:53:41