distance_of_time_in_words fails in test

I have a DateTime object (from DateTime.now), and an ActiveSupport::TimeWithZone object (comes from a date in the DB). I'm passing these values to the helper method distance_of_time_in_words:

  <%= distance_of_time_in_words(DateTime.now, @myinstance.mydate) %>

In the implementation of this helper is the following:

  distance_in_minutes = (((to_time - from_time).abs)/60).round

In production, this works as expected. In testing, this results in:

  ActionView::TemplateError: undefined method `abs' for Sun Oct 26 19:49:44 UTC 1980:Time

Here's where it gets strange. I opened this up in the debugger in both environments. In test, a DateTime minus a TimeWithZone gives me a Time. Time doesn't have an abs method, so this fails with a NoMethod exception. In production a DateTime minus a TimeWithZone results in a Rational, which does have abs!

Why the different behavior in these two environments? I don't see the DateTime minus operator being redefined by rails anywhere, but maybe I'm not looking in the right place. I'm using Rails version 2.2.2.

Actually, I was looking at the parameters in the wrong order. I need the - operator from the to_date wich is the TimeWithZone. That's defined here:

    def -(other)       # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,       # otherwise move backwards #utc, for accuracy when moving across DST boundaries       if other.acts_like?(:time)         utc - other       elsif duration_of_variable_length?(other)         method_missing(:-, other)       else         result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)         result.in_time_zone(time_zone)       end     end

The rails console tells me I should hit the first condition, resulting in a Time (which doesn't support abs). I still haven't found the magic that makes this work in production.

Loading development environment (Rails 2.2.2)

a = DateTime.now

=> Sun, 05 Jul 2009 01:55:13 -0400

a.class

=> DateTime

b = Time.zone.now

=> Sun, 05 Jul 2009 05:55:31 UTC +00:00

b.class

=> ActiveSupport::TimeWithZone

c=b-a

=> Thu Jan 01 00:00:18 UTC 1970

c.class

=> Time

A little more information...

Not all ActiveSupport::TimeWithZone instances are alike. In the definition of the minus operator (above) I consistantly see that other.acts_like?(:time) so it always returns "utc-other". The difference is, the type of utc! When the TimeWithZone is created from the DB, utc is a Date (and a Date - Date = rational). When the TimeWithZone is created from a test fixture, utc is a Time (and a Time - Date = Time).

Now, at least, I understand how its possible that my subtraction is returning a different type in each case. Unfortunately for me this means the real answer is buried somewhere inside ActiceRecord code and I'm still not smart enough to proceed. :slight_smile:

Two questions: 1) Why isn't anyone else hitting this? The combination of using test fixtures + calling distance_of_time_in_words can't be THAT rare, can it? 2) Any suggestions for a workaround?

Resolved!

Sometimes I'm too stubborn for my own good. Updating rails to 2.3.2 made this problem go away. The helper now works as I expect it to for both DB dates and test fixture dates.