Вопрос по ruby-on-rails, ruby – В Rails показывать время между двумя датами на английском

13

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

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

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

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

Ваш Ответ

6   ответов
23

который вы ищете, потому что вместо указания лет, месяцев и т. Д. Помощники 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 

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

Это выглядит действительно многообещающе. К сожалению, я получаю "неопределенный метод` множественное число "для # <Object: 0x389a0 @controller = # <ApplicationController: 0x117f3bc >>" (в консоли). Eric Wright
Это потому, что множественное число является вспомогательным методом ActionView: Api.rubyonrails.org / классы / ActionView / Помощники / TextHelper.html. Если вы говорите,include ActionView::Helpers::TextHelper в скрипте / консоли, будет определено множественное число. Как упоминалось в ответе, вы должны вставить метод в помощник вида (в app / helpers), а затем обратиться к нему из представления. Daniel Vandersluis
52

ActionViewодуль @ включает два метода, которые могут делать то, что вам нужно:

distance_of_time_in_wordsdistance_of_time_in_words_to_now
+ 1 Обыграй меня. Pesto
+ 1 именно то, что я искал mc.
У меня нет недель ... art-solopov
8

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

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

Я могу подтвердить, что это правильное решение Rizal Yusoff
3

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

Хотел бы я знать, как это исправить:

2
Это действительно хорошо (и я раздражен, что не нашел его раньше), но это все еще дает мне приблизительное приближение. То есть, это даст мне «3 месяца», а не «3 месяца, 4 дня». Eric Wright
0
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

Похожие вопросы