Trejkaz
2009-Aug-31 02:24 UTC
Fwd: Behaviour of ActiveSupport::Duration when added together
Hi all. What''s up with this?>> Time.now=> Mon Aug 31 12:18:00 +1000 2009>> 1.month + 1.month=> 2 months>> (1.month + 1.month).from_now=> Fri Oct 30 12:19:51 +1100 2009>> 2.months.from_now=> Sat Oct 31 12:20:33 +1100 2009 Intended behaviour? Quirk due to implementation details in Duration + Duration? Rails gem version is 2.3.2. TX
Christos Zisopoulos
2009-Aug-31 12:55 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
(apologies if this might be becoming off-list; feel free to shut me up at any time) Thanks for the explanation Rick. I see the ''why'' now. The ''how'' still bothers me. 1.month + 1.month is internally distinct from 2.months... (1.month + 1.month).parts # => [[:months, 1], [:months, 1]] 2.months.parts # => [[:months, 2]] ...although everything else points to the contrary: (1.month + 1.month) === 2.months # => true (1.month + 1.month) == 2.months # => true (1.month + 1.month).to_i # => 5184000 2.months.to_1 # => 5184000 2.months.inspect # => "2 months" (1.month + 1.month).inspect # => "2 months" (1.month + 1.month).class # => Fixnum 2.months.class # => Fixnum If the above tests behaved as expected and returned a Duration, I wouldn''t be so surprised by the behaviour of the + operator, or the == and === for that matter. As it stands, it is not very intuitive, but Time never is, I guess. -christos On 6 Sep 2009, at 01:45, Rick DeNatale wrote:> > On Wed, Sep 2, 2009 at 4:19 AM, Christos > Zisopoulos<christos@42linesofcode.com> wrote: >> >> > Are you sure you''re running this on August 31st? Looks like >> you''re >>> running it on November 1st to me. >> >> Was running on September 1st. Wasn''t clear from your original message >> that the exact date was a requirement for the bug to occur. >> >> Changing the date to August 31 I can reproduce the anomaly. >> >> Furthermore it seems like calling to_i on the returned Duration >> calculates correctly (incorrectly?) >> >> >> (1.month + 1.month).to_i.from_now >> => Fri Oct 30 08:59:17 0100 2009 >> >> (2.months).to_i.from_now >> => Fri Oct 30 08:59:25 0100 2009 >> >> What is strange is (after having read Rick DeNatale''s explanation) >> that the ''+'' operator doesn''t add parts even if they are of the same >> type (months) >> >> Any reason for that? > > Because a duration in fact represents a series of steps to increment a > date/time/datetime and it''s useful to differentiate between > > (x.months + y.months) > > and > > (x+y).months > > Think of the + when adding durations as if it were ''then'' > > > -- > Rick DeNatale > > Blog: http://talklikeaduck.denhaven2.com/ > Twitter: http://twitter.com/RickDeNatale > WWR: http://www.workingwithrails.com/person/9021-rick-denatale > LinkedIn: http://www.linkedin.com/in/rickdenatale > > --~--~---------~--~----~------------~-------~--~----~ > You received this message because you are subscribed to the Google > Groups "Ruby on Rails: Core" group. > To post to this group, send email to rubyonrails-core@googlegroups.com > To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com > For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en > -~----------~----~----~----~------~----~------~--~--- >
Rick DeNatale
2009-Aug-31 15:00 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
On Sun, Aug 30, 2009 at 10:24 PM, Trejkaz<trejkaz@gmail.com> wrote:> > Hi all. > > What''s up with this? > >>> Time.now > => Mon Aug 31 12:18:00 +1000 2009 >>> 1.month + 1.month > => 2 months >>> (1.month + 1.month).from_now > => Fri Oct 30 12:19:51 +1100 2009 >>> 2.months.from_now > => Sat Oct 31 12:20:33 +1100 2009 > > Intended behaviour? Quirk due to implementation details in Duration + > Duration? Rails gem version is 2.3.2.I''m pretty sure that this is intentional. A duration contains a list of parts which represent the time increments, it also contains an integer value which is the number of seconds in the duration. But that value for things like month and year isn''t what''s used when adding a duration to a Time, since the number of seconds in a month or a year depends on the particular month( whichc could be 28, 29, 30, or 31 days) or year (365 or 366 days). When you add two durations, it combines the list of parts, so: 2.months has one part [:months, 2] but (1.month + 1.month) has two parts [:months, 1], [:months, 1] Now duration.from_now gets Time.now and then iterates over the parts effectively (simplified the code to get the gist) parts.inject(Time.now) do |t,(type,number)| t.advance(type => sign * number) end So, 2.months.from_now when Time.now is during August 31, ends up as Time.now.advance(:months => 2) and since October has 31 days you get there. but (1.months + 1.months).from_now ends up as: Time.now.advance(:months => 1).advance(:months => 1) The first gets us to September 30 since September only has 30 days, and that''s how time.advance is defined, then the next advance gets us to October 30. You need to look at the code to understand this, Duration is a bit tricky since it acts as a proxy to it''s value, it''s implemented as a subclass of BasicObject and forwards anything it doesn''t have a method for to the value, so it reports it''s class as Fixnum, or BigInteger, and it also defines == to return true if the two values are equal ignoring the parts. -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale
Geoff Buesing
2009-Sep-01 13:48 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
Great explanation, thanks. Also note that this behavior is consistent with the behavior of Ruby''s Date#>> (which Time#advance relies on):>> (d=Date.new(2009,8,31)).to_s=> "2009-08-31">> (d >> 1).to_s=> "2009-09-30">> (d >> 2).to_s=> "2009-10-31">> (d >> 1 >> 1).to_s=> "2009-10-30" On Aug 31, 10:00 am, Rick DeNatale <rick.denat...@gmail.com> wrote:> On Sun, Aug 30, 2009 at 10:24 PM, Trejkaz<trej...@gmail.com> wrote: > > > Hi all. > > > What''s up with this? > > >>> Time.now > > => Mon Aug 31 12:18:00 +1000 2009 > >>> 1.month + 1.month > > => 2 months > >>> (1.month + 1.month).from_now > > => Fri Oct 30 12:19:51 +1100 2009 > >>> 2.months.from_now > > => Sat Oct 31 12:20:33 +1100 2009 > > > Intended behaviour? Quirk due to implementation details in Duration + > > Duration? Rails gem version is 2.3.2. > > I''m pretty sure that this is intentional. A duration contains a list > of parts which represent the time increments, it also contains an > integer value which is the number of seconds in the duration. But that > value for things like month and year isn''t what''s used when adding a > duration to a Time, since the number of seconds in a month or a year > depends on the particular month( whichc could be 28, 29, 30, or 31 > days) or year (365 or 366 days). > > When you add two durations, it combines the list of parts, so: > > 2.months has one part [:months, 2] > but > (1.month + 1.month) has two parts [:months, 1], [:months, 1] > > Now duration.from_now gets Time.now and then iterates over the parts > effectively (simplified the code to get the gist) > > parts.inject(Time.now) do |t,(type,number)| > t.advance(type => sign * number) > end > > So, > > 2.months.from_now when Time.now is during August 31, ends up as > > Time.now.advance(:months => 2) and since October has 31 days you get there. > > but > > (1.months + 1.months).from_now ends up as: > > Time.now.advance(:months => 1).advance(:months => 1) > > The first gets us to September 30 since September only has 30 days, > and that''s how time.advance is defined, then the next advance gets us > to October 30. > > You need to look at the code to understand this, Duration is a bit > tricky since it acts as a proxy to it''s value, it''s implemented as a > subclass of BasicObject and forwards anything it doesn''t have a method > for to the value, so it reports it''s class as Fixnum, or BigInteger, > and it also defines == to return true if the two values are equal > ignoring the parts. > > -- > Rick DeNatale > > Blog:http://talklikeaduck.denhaven2.com/ > Twitter:http://twitter.com/RickDeNatale > WWR:http://www.workingwithrails.com/person/9021-rick-denatale > LinkedIn:http://www.linkedin.com/in/rickdenatale
Trejkaz
2009-Sep-01 14:10 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
On Tue, Sep 1, 2009 at 11:48 PM, Geoff Buesing<gbuesing@gmail.com> wrote:> > Great explanation, thanks. > > Also note that this behavior is consistent with the behavior of Ruby''s > Date#>> (which Time#advance relies on): > >>> (d=Date.new(2009,8,31)).to_s > => "2009-08-31" >>> (d >> 1).to_s > => "2009-09-30" >>> (d >> 2).to_s > => "2009-10-31" >>> (d >> 1 >> 1).to_s > => "2009-10-30"I''m going to object here because this particular example is not analogous to mine. I see this as okay: (after all, it demands an intermediate state)>> d=Date.new(2009,8,31)=> Mon, 31 Aug 2009>> 1.month.since(1.month.since(d))=> Fri, 30 Oct 2009 But this is confusing:>> (1.month + 1.month).since(d)=> Fri, 30 Oct 2009 The reason it is confusing:>> 1.month + 1.month=> 2 months>> (1.month + 1.month).since(d)=> Fri, 30 Oct 2009>> 2.months.since(d)=> Sat, 31 Oct 2009>> (1.month + 1.month) == 2.months=> true Clearly it isn''t equal because applying the two has different results. I think that multiple hops of the same unit should coalesce such that 1.month + 1.month actually is 2.months (as it claims to be via nearly any other API call.) TX
Christos Zisopoulos
2009-Sep-01 14:48 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
I can''t reproduce your problem. On a new Rails project: Loading development environment (Rails 2.3.3)>> (1.month + 1.month).from_now=> Sun, 01 Nov 2009 14:47:26 UTC 00:00>> 2.months.from_now=> Sun, 01 Nov 2009 14:47:32 UTC 00:00 Are you sure you haven''t installed a plugin that ''enhances'' Time? -christos On Aug 31, 4:24 am, Trejkaz <trej...@gmail.com> wrote:> Hi all. > > What''s up with this? > > >> Time.now > > => Mon Aug 31 12:18:00 +1000 2009>> 1.month + 1.month > => 2 months > >> (1.month + 1.month).from_now > > => Fri Oct 30 12:19:51 +1100 2009>> 2.months.from_now > > => Sat Oct 31 12:20:33 +1100 2009 > > Intended behaviour? Quirk due to implementation details in Duration + > Duration? Rails gem version is 2.3.2. > > TX
Trejkaz
2009-Sep-02 06:18 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
On Wed, Sep 2, 2009 at 12:48 AM, Christos Zisopoulos<christos@42linesofcode.com> wrote:> > I can''t reproduce your problem. On a new Rails project: > > Loading development environment (Rails 2.3.3) >>> (1.month + 1.month).from_now > => Sun, 01 Nov 2009 14:47:26 UTC 00:00 >>> 2.months.from_now > => Sun, 01 Nov 2009 14:47:32 UTC 00:00 > > Are you sure you haven''t installed a plugin that ''enhances'' Time?Are you sure you''re running this on August 31st? Looks like you''re running it on November 1st to me. TX
Christos Zisopoulos
2009-Sep-02 08:19 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
> Are you sure you''re running this on August 31st? Looks like you''re > running it on November 1st to me.Was running on September 1st. Wasn''t clear from your original message that the exact date was a requirement for the bug to occur. Changing the date to August 31 I can reproduce the anomaly. Furthermore it seems like calling to_i on the returned Duration calculates correctly (incorrectly?) >> (1.month + 1.month).to_i.from_now => Fri Oct 30 08:59:17 0100 2009 >> (2.months).to_i.from_now => Fri Oct 30 08:59:25 0100 2009 What is strange is (after having read Rick DeNatale''s explanation) that the ''+'' operator doesn''t add parts even if they are of the same type (months) Any reason for that? -christos
Rick DeNatale
2009-Sep-05 23:45 UTC
Re: Fwd: Behaviour of ActiveSupport::Duration when added together
On Wed, Sep 2, 2009 at 4:19 AM, Christos Zisopoulos<christos@42linesofcode.com> wrote:> > > Are you sure you''re running this on August 31st? Looks like you''re >> running it on November 1st to me. > > Was running on September 1st. Wasn''t clear from your original message > that the exact date was a requirement for the bug to occur. > > Changing the date to August 31 I can reproduce the anomaly. > > Furthermore it seems like calling to_i on the returned Duration > calculates correctly (incorrectly?) > > >> (1.month + 1.month).to_i.from_now > => Fri Oct 30 08:59:17 0100 2009 > >> (2.months).to_i.from_now > => Fri Oct 30 08:59:25 0100 2009 > > What is strange is (after having read Rick DeNatale''s explanation) > that the ''+'' operator doesn''t add parts even if they are of the same > type (months) > > Any reason for that?Because a duration in fact represents a series of steps to increment a date/time/datetime and it''s useful to differentiate between (x.months + y.months) and (x+y).months Think of the + when adding durations as if it were ''then'' -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale