tags:

views:

94

answers:

3

I have a Time object and would like to find the next/previous month. Adding subtracting days does not work as the days per month vary.

time = Time.parse('21-12-2008 10:51 UTC')
next_month = time + 31 * 24 * 60 * 60

Incrementing the month also falls down as one would have to take care of the rolling

time = Time.parse('21-12-2008 10:51 UTC')
next_month = Time.utc(time.year, time.month+1)

time = Time.parse('01-12-2008 10:51 UTC')
previous_month = Time.utc(time.year, time.month-1)

The only thing I found working was

time = Time.parse('21-12-2008 10:51 UTC')
d = Date.new(time.year, time.month, time.day)
d >>= 1
next_month = Time.utc(d.year, d.month, d.day, time.hour, time.min, time.sec, time.usec)

Is there a more elegant way of doing this that I am not seeing? How would you do it?

+2  A: 

This works in Rails only (Thanks Steve!) but I'm keeping it here in case others make the same mistake:

Super simple - thank you Ruby on Rails!

DateTime.now + 1.month

DateTime.now - 1.month

Josh

Josh Pinter
The `#month` method is a Rails extension. It also assumes 30 days in a month, and so will return the wrong answer on the 31st.
Steve Madsen
Fairplay, I was using the script/console - guess I'm too ROR'd for my own good.
Josh Pinter
@Steve it doesn't assume 30 days when doing date arithmetic. It's actually pretty magical. try it! (see ActiveSupport::Duration, Date#plus_with_duration, and integer/time.rb in active_support). e.g. >> Time.utc(2010, 2, 1) + 1.months. => Mon Mar 01 00:00:00 UTC 2010>> Time.utc(2010, 3, 1) + 1.months=> Thu Apr 01 00:00:00 UTC 2010>> Time.utc(2010, 4, 1) + 1.months=> Sat May 01 00:00:00 UTC 2010Or for an extreme example: >> Time.utc(2010, 1, 31) + 1.months=> Sun Feb 28 00:00:00 UTC 2010
John Douthat
>> Time.utc(2010, 1, 31) + 1.months=> Sun Feb 28 00:00:00 UTC 2010>> Time.utc(2008, 1, 31) + 1.months=> Fri Feb 29 00:00:00 UTC 2008
John Douthat
@John: That's new, isn't it? Do you know what version of Rails added that?
Steve Madsen
@Steve 2007ish? http://github.com/rails/rails/commit/276c9f29cde80fafa23814b0039f67504255e0fd
John Douthat
+2  A: 

There are no built-in methods on Time to do what you want in Ruby. I suggest you write methods to do this work in a module and extend the Time class to make their use simple in the rest of your code.

You can use DateTime, but the methods (<< and >>) are not named in a way that makes their purpose obvious to someone that hasn't used them before.

Steve Madsen
+1  A: 

you can use standard class DateTime

require 'date'

dt = Time.new().to_datetime
=> #<DateTime: 2010-04-23T22:31:39+03:00 (424277622199937/172800000,1/8,2299161)>

dt2 = dt >> 1
=> #<DateTime: 2010-05-23T22:31:39+03:00 (424282806199937/172800000,1/8,2299161)>

t = dt2.to_time
=> 2010-05-23 22:31:39 +0200
aaz