В Rails время отображения между двумя датами на английском языке

В проекте Rails я хочу найти разницу между двумя датами, а затем отобразить ее на естественном языке. Что-то вроде

>> (date1 - date2).to_natural_language 
"3 years, 2 months, 1 week, 6 days"

В основном this для рубина.

Google и Rails API ничего не обнаружили. Я нашел некоторые вещи, которые дадут вам разницу в одной единице (т.е. Сколько недель между двумя датами), но не то, что точно рассчитает годы, месяцы, недели и дни вместе.

Ответ 1

Другие ответы могут не дать того типа выходных данных, который вы ищете, потому что вместо строки лет, месяцев и т.д. Помощники Rails просто показывают наибольшую единицу. Если вы ищете что-то более сломанное, здесь есть еще один вариант. Вставьте этот метод в помощника:

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

И тогда в представлении вы можете сказать что-то вроде:

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

Данный метод сводится только к дням, но при желании его можно легко расширить, добавив меньшие единицы.

Ответ 3

Я попробовал решение Daniel и нашел несколько неправильных результатов для нескольких тестовых примеров из-за того, что он неправильно обрабатывает переменное число дней, найденных в месяцы:

> 30.days < 1.month
   => false

Итак, например:

> 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" 

Следующее даст вам правильное количество {лет, месяцев, дней, часов, минут, секунд}:

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] 

Ответ 4

distance_of_time_in_words является наиболее точным здесь. Ответ Даниэля неправдоподобен: 2,5 года назад он должен был производить ровно 2 года, 6 месяцев. Проблема заключается в том, что месяцы содержат 28-31 день, а годы могут быть прыжками.

Мне хотелось бы знать, как это исправить: (

Ответ 6

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