Как рассчитать, сколько лет прошло с указанной даты в Ruby?

Этот вопрос был здесь для других языков, так что давайте здесь один для Руби.

Как рассчитать количество полных лет, прошедших с данной даты? Как вы, наверное, догадались, это рассчитывать возраст человека автоматически. Самый близкий distance_of_time_in_words Rails helper, поэтому следующий шаблон

Jack is <%= distance_of_time_in_words (Time.now, Time.local(1950,03,22)) %> old.

доходность

Jack is over 59 years old.

Но мне нужна более точная функция, которая дает только число. Есть один?

Если для этого существует какая-то вспомогательная функция Ruby on Rails, это нормально, хотя чистое решение на Ruby было бы лучше.

Изменить: суть вопроса заключается в том, что необходимо не приблизительное решение. 2 марта Джеку должно быть 59 лет, а на следующий день ему должно быть 60 лет. Високосные годы и тому подобное следует учитывать.

12 ответов

Решение

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

def age_in_completed_years (bd, d)
    # Difference in years, less one if you have not had a birthday this year.
    a = d.year - bd.year
    a = a - 1 if (
         bd.month >  d.month or 
        (bd.month >= d.month and bd.day > d.day)
    )
    a
end

birthdate = Date.new(2000, 12, 15)
today     = Date.new(2009, 12, 14)

puts age_in_completed_years(birthdate, today)
require 'date'

def years_since(dt)
    delta = (Date.today - Date.parse(dt)) / 365
    delta.to_i
end

У меня есть драгоценный камень / плагин под названием Dotiw, который имеет distance_of_time_in_words_hash это вернет хеш как: { :years => 59, :months => 11, :days => 27 }, Из этого вы могли бы работать, если он близок к определенному пределу.

Я придумал следующее, основываясь на том же рассуждении, что и @FMc First, вычислить разницу между сегодняшним годом и годом рождения. Затем суммируйте его с днем ​​рождения и проверьте полученную дату: если она больше, чем сегодня, уменьшите diff на 1. Для использования в приложениях Rails, так как он полагается на ActiveSupport"s years метод

def age(birthday, today)
  diff = today.year - birthday.year
  (birthday + diff.years > today ) ? (diff - 1) : diff
end

Подход, который обращается с високосными годами

Всякий раз, когда вы рассчитываете прошедшие годы с даты, вы должны решить, как обрабатывать високосный год. Вот мой подход, который, на мой взгляд, очень удобен для восприятия, и он может идти на високосные годы без использования логики "особого случая".

def years_completed_since(start_date, end_date)

  if end_date < start_date
    raise ArgumentError.new(
      "End date supplied (#{end_date}) is before start date (#{start_date})"
    )
  end

  years_completed = end_date.year - start_date.year

  unless reached_anniversary_in_year_of(start_date, end_date)
    years_completed -= 1
  end

  years_completed
end

# No special logic required for leap day; its anniversary in a non-leap
# year is considered to have been reached on March 1.
def reached_anniversary_in_year_of(original_date, new_date)
  if new_date.month == original_date.month
    new_date.day >= original_date.day
  else
    new_date.month > original_date.month
  end
end

Та же идея, что и у FM, но с упрощенным оператором if. Очевидно, вы могли бы добавить второй аргумент вместо использования текущего времени.

def age(birthdate)
  now = DateTime.now
  age = now.year - birthdate.year
  age -= 1 if(now.yday < birthdate.yday)
  age
end

В рамках http://github.com/radar/dotiw

Jack is <%= distance_of_time_in_words (Time.now, Time.local(1950,03,22)) %> old.

производить

Jack is 60 years old

Вы можете использовать рубиновый камень adroit-age

Это работает для високосных лет также..

age = AdroitAge.find_age("23/01/1990")

Обновить

require 'adroit-age'

dob =  Date.new(1990,1,23)
or
dob = "23/01/1990".to_date

age = dob.find_age
#=> 23

Как насчет этого:

def age_in_years(date)
  # Difference in years, less one if you have not had a birthday this year.
  today = Date.today
  age = today.year - date.year
  age = age - 1 if [date.day, date.month, today.year].join('/').to_date > Date.today
end

d2.year - d1.year - (d2.month > d1.month || (d2.month == d1.month && d2.day >= d1.day) ? 0 : 1)

Я думаю, что это всегда будет работать, даже для кого-то с днем ​​рождения около високосного дня:

require 'date'

def calculate_age(start_date, end_date)
  end_date.year - start_date.year - ((end_date.month > start_date.month || (end_date.month == start_date.month && end_date.day >= start_date.day)) ? 0 : 1)
end

puts calculate_age( Date.strptime('03/02/1968', '%m/%d/%Y'), Date.strptime('03/02/2010', '%m/%d/%Y'))

Расчетный возраст для этого метода в приведенном выше примере вызова составляет 42, что является правильным, несмотря на то, что 1968 год был високосным, а день рождения близок к високосному.

Кроме того, в этом случае нет необходимости создавать локальную переменную.

Чтобы рассчитать количество лет и месяцев между двумя датами, я использовал эту функцию

  def calculate_year_month(from_date, to_date)
    num_days = (from_date - to_date).to_i
    num_months = (num_days / 30) 
    num_years = (num_months / 12) 
    num_months = (num_months % 12) 
    return num_years.to_s + " year(s) and " + num_months.to_s + " month(s)" 
  end 

Как насчет чего-то вроде:

def years_diff(from_time,to_time)
  (((to_time - from_time).abs)/ (365 * 24 * 60 * 60)).to_i
end

years_diff(Time.now,Time.local(1950,03,22)) #=> 59
years_diff(Time.now,Time.local(2009,03,22)) #=> 0
years_diff(Time.now,Time.local(2008,03,22)) #=> 1
Другие вопросы по тегам