In Rails, display time between two dates in English

前端 未结 6 530
南方客
南方客 2020-12-07 19:15

In a Rails project I want to find the difference between two dates and then display it in natural language. Something like

>> (date1 - date2).to_natur         


        
相关标签:
6条回答
  • 2020-12-07 19:55

    distance_of_time_in_words is the most accurate here. Daniel's answer is actully wrong: 2.5 years ago should produce exactly 2 years, 6 months. The issue is that months contain 28-31 day, and years might be leap.

    I wish I knew how to fix this :(

    0 讨论(0)
  • 2020-12-07 20:00

    I tried Daniel's solution and found some incorrect results for a few test cases, due to the fact that it doesn't correctly handle the variable number of days found in months:

    > 30.days < 1.month
       => false
    

    So, for example:

    > d1 = DateTime.civil(2011,4,4)
    > d2 = d1 + 1.year + 5.months
    > time_diff_in_natural_language(d1,d2)
    => "1 year, 5 months, 3 days" 

    The following will give you the correct number of {years,months,days,hours,minutes,seconds}:

    def time_diff(from_time, to_time)
      %w(year month day hour minute second).map do |interval|
        distance_in_seconds = (to_time.to_i - from_time.to_i).round(1)
        delta = (distance_in_seconds / 1.send(interval)).floor
        delta -= 1 if from_time + delta.send(interval) > to_time
        from_time += delta.send(interval)
        delta
      end
    end
    > time_diff(d1,d2)
     => [1, 5, 0, 0, 0, 0] 

    0 讨论(0)
  • 2020-12-07 20:04

    The Rails' ActionView module includes two methods that may do what you require:

    • distance_of_time_in_words
    • distance_of_time_in_words_to_now
    0 讨论(0)
  • 2020-12-07 20:04
    def date_diff_in_natural_language(date_from, date_to)
      components = []
      %w(years months days).each do |interval_name|
        interval = 1.send(interval_name)
        count_intervals = 0
        while date_from + interval <= date_to
          date_from += interval
          count_intervals += 1
        end
        components << pluralize(count_intervals, interval_name) if count_intervals > 0
      end
      components.join(', ')
    end
    
    0 讨论(0)
  • 2020-12-07 20:05

    The other answers may not give the type of output that you're looking for, because instead of giving a string of years, months, etc., the Rails helpers just show the largest unit. If you're looking for something more broken down, here's another option. Stick this method into a helper:

    def time_diff_in_natural_language(from_time, to_time)
      from_time = from_time.to_time if from_time.respond_to?(:to_time)
      to_time = to_time.to_time if to_time.respond_to?(:to_time)
      distance_in_seconds = ((to_time - from_time).abs).round
      components = []
    
      %w(year month week day).each do |interval|
        # For each interval type, if the amount of time remaining is greater than
        # one unit, calculate how many units fit into the remaining time.
        if distance_in_seconds >= 1.send(interval)
          delta = (distance_in_seconds / 1.send(interval)).floor
          distance_in_seconds -= delta.send(interval)
          components << pluralize(delta, interval)
          # if above line give pain. try below one  
          # components <<  interval.pluralize(delta)  
        end
      end
    
      components.join(", ")
    end
    

    And then in a view you can say something like:

    <%= time_diff_in_natural_language(Time.now, 2.5.years.ago) %>
    => 2 years, 6 months, 2 days 
    

    The given method only goes down to days, but can be easily extended to add in smaller units if desired.

    0 讨论(0)
  • 2020-12-07 20:13

    DateHelper#distance_of_time_in_words

    0 讨论(0)
提交回复
热议问题