Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

distance_of_time_in_words calculates wrong no of years #410

Merged
merged 2 commits into from

2 participants

@asanghi

distance_of_time_in_words does not take leap years into account accurately. The bug was raised on Lighthouse 6074.

Patch with test provided. Feel free to comment/review/suggest changes.

@josevalim
Owner

Can we measure how much this new implementation will affect performance? In general, the Date class is not very fast.

@asanghi

Having a quick look at Date.leap? reveals that it instantiates 1st March on the year and checks if the previous day was a 29th? This implementation would do it for each year given in the range.

How much of a performance hit can we bargain for accuracy? :)

Will measure and post gist.

@josevalim
Owner

I am worried how this will affect common cases like minutes, hours and 1 to 3 years. Maybe we could have a short circuit logic that simply skip this calculus if < 2 years. If you are working with old dates, this accuracy is indeed needed.

@asanghi

@josevalim shorter distances of time are handled in the distance_of_time_in_words method already to some extent.

I think 525599 is the number of minutes in 365 days, so upto 1 year is already handled.

The whole method seems prime for refactoring but will now concoct to handle for < 2 years.

@josevalim
Owner

Ah, great, no performance worries then! I am merging this in. A refactoring is indeed welcome, at least the leap year part.

@josevalim josevalim merged commit be9857c into rails:master
@jake3030 jake3030 referenced this pull request from a commit in jake3030/rails
@fcheung fcheung Make constantize look into ancestors
[#410 state:resolved]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
262fef7
@jake3030 jake3030 referenced this pull request from a commit in jake3030/rails
@jeremy jeremy Revert "Make constantize look into ancestors"
[#410 state:open]

This reverts commit 262fef7.
19be3d3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 6, 2011
  1. @asanghi

    Take leap years into account more seriously when calculating year dis…

    asanghi authored
    …tance [#6074 state:resolved]
  2. @asanghi

    strip space

    asanghi authored
This page is out of date. Refresh to see the latest.
View
17 actionpack/lib/action_view/helpers/date_helper.rb
@@ -94,9 +94,20 @@ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, o
when 43200..86399 then locale.t :about_x_months, :count => 1
when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
else
- distance_in_years = distance_in_minutes / 525600
- minute_offset_for_leap_year = (distance_in_years / 4) * 1440
- remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
+ fyear = from_time.year
+ fyear += 1 if from_time.month >= 3
+ tyear = to_time.year
+ tyear -= 1 if to_time.month < 3
+ leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
+ minute_offset_for_leap_year = leap_years * 1440
+ # Discount the leap year days when calculating year distance.
+ # e.g. if there are 20 leap year days between 2 dates having the same day
+ # and month then the based on 365 days calculation
+ # the distance in years will come out to over 80 years when in written
+ # english it would read better as about 80 years.
+ minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
+ remainder = (minutes_with_offset % 525600)
+ distance_in_years = (minutes_with_offset / 525600)
if remainder < 131400
locale.t(:about_x_years, :count => distance_in_years)
elsif remainder < 394200
View
4 actionpack/test/template/date_helper_test.rb
@@ -121,6 +121,10 @@ def test_distance_in_words_with_dates
start_date = Date.new 1975, 1, 31
end_date = Date.new 1977, 1, 31
assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date))
+
+ start_date = Date.new 1982, 12, 3
+ end_date = Date.new 2010, 11, 30
+ assert_equal("almost 28 years", distance_of_time_in_words(start_date, end_date))
end
def test_distance_in_words_with_integers
Something went wrong with that request. Please try again.