Получите возраст человека в Ruby

Я хотел бы получить возраст человека с момента его рождения. now - birthday / 365 не работает, потому что несколько лет имеют 366 дней. Я придумал следующий код:

now = Date.today
year = now.year - birth_date.year

if (date+year.year) > now
  year = year - 1
end

Есть ли более рубинский способ расчета возраста?

Ответ 1

Я знаю, что опаздываю на вечеринку здесь, но принятый ответ будет ужасно нарушаться при попытке выработать возраст кого-то, родившегося 29 февраля в високосном году. Это связано с тем, что вызов birthday.to_date.change(:year => now.year) создает недопустимую дату.

Вместо этого я использовал следующий код (в проекте Rails):

def age(dob)
  now = Time.now.utc.to_date
  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end

Ответ 2

Я нашел, что это решение работает хорошо и доступно для других:

    age = Date.today.year - birthday.year
    age -= 1 if Date.today < birthday + age.years #for days before birthday

Легко, и вам не нужно беспокоиться о работе с високосным годом и т.д.

Ответ 3

Используйте это:

def age
  now = Time.now.utc.to_date
  now.year - birthday.year - (birthday.to_date.change(:year => now.year) > now ? 1 : 0)
end

Ответ 4

Один лайнер в Ruby on Rails (ActiveSupport). Обрабатывает високосные годы, прыжки секунд и все.

def age(birthday)
  (Time.now.to_s(:number).to_i - birthday.to_time.to_s(:number).to_i)/10e9.to_i
end

Логика отсюда - Вычислить возраст на С#

Предполагая, что обе даты находятся в одном и том же часовом поясе, если не называть utc() до to_s() на обоих.

Ответ 5

(Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000

Ответ 6

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

birthday = DateTime.new(1900, 1, 1)
age = (DateTime.now - birthday) / 365.25 # or (1.year / 1.day)

Вы получите дробный результат, поэтому не стесняйтесь преобразовывать результат в целое число с to_i. Это лучшее решение, потому что оно правильно рассматривает разницу дат как период времени, измеренный в днях (или секундах в случае соответствующего класса времени) с момента события. Тогда простое деление на количество дней в году дает вам возраст. При расчете возраста в годах таким образом, пока вы сохраняете исходное значение DOB, никакие учетные данные не должны учитываться для високосных лет.

Ответ 7

Мое предложение:

def age(birthday)
    ((Time.now - birthday.to_time)/(60*60*24*365)).floor
end

Фокус в том, что минусовая операция с Time возвращает секунды

Ответ 8

Мне нравится этот:

now = Date.current
age = now.year - dob.year
age -= 1 if now.yday < dob.yday

Ответ 9

Этот ответ является наилучшим, вместо этого переверните его.


Мне нравится решение @philnash, но условное может быть compacter. То, что это логическое выражение делает, сравнивает пары [month, day], используя лексикографический порядок, поэтому можно просто использовать сравнение строк ruby ​​вместо:

def age(dob)
  now = Date.today
  now.year - dob.year - (now.strftime('%m%d') < dob.strftime('%m%d') ? 1 : 0)
end

Ответ 10

Это преобразование этого ответа (оно получило много голосов):

# convert dates to yyyymmdd format
today = (Date.current.year * 100 + Date.current.month) * 100 + Date.today.day
dob = (dob.year * 100 + dob.month) * 100 + dob.day
# NOTE: could also use `.strftime('%Y%m%d').to_i`

# convert to age in years
years_old = (today - dob) / 10000

Он определенно уникален в своем подходе, но имеет смысл, когда вы понимаете, что он делает:

today = 20140702 # 2 July 2014

# person born this time last year is a 1 year old
years = (today - 20130702) / 10000

# person born a year ago tomorrow is still only 0 years old
years = (today - 20130703) / 10000

# person born today is 0
years = (today - 20140702) / 10000  # person born today is 0 years old

# person born in a leap year (eg. 1984) comparing with non-leap year
years = (20140228 - 19840229) / 10000 # 29 - a full year hasn't yet elapsed even though some leap year babies think it has, technically this is the last day of the previous year
years = (20140301 - 19840229) / 10000 # 30

# person born in a leap year (eg. 1984) comparing with leap year (eg. 2016)
years = (20160229 - 19840229) / 10000 # 32

Ответ 11

Поскольку Ruby on Rails помечен, dotiw gem переопределяет встроенный Rails distance_of_times_in_words и предоставляет distance_of_times_in_words_hash, который можно использовать для определения возраста. Високосные годы обрабатываются штрафом за годы, хотя имейте в виду, что 29 февраля имеет влияние на часть дней, которая требует понимания того, нужен ли этот уровень детализации. Кроме того, если вам не нравится, как dotiw изменяет формат distance_of_time_in_words, используйте параметр: неопределенный, чтобы вернуться к исходному формату.

Добавить dotiw в Gemfile:

gem 'dotiw'

В командной строке:

bundle

Включите DateHelper в соответствующую модель, чтобы получить доступ к distance_of_time_in_words и distance_of_time_in_words_hash. В этом примере модель представляет собой "Пользователь", а поле дня рождения - "День рождения".

class User < ActiveRecord::Base
  include ActionView::Helpers::DateHelper

Добавьте этот метод к той же модели.

def age
  return nil if self.birthday.nil?
  date_today = Date.today
  age = distance_of_time_in_words_hash(date_today, self.birthday).fetch("years", 0)
  age *= -1 if self.birthday > date_today
  return age
end

Использование:

u = User.new("birthday(1i)" => "2011", "birthday(2i)" => "10", "birthday(3i)" => "23")
u.age

Ответ 12

Я считаю, что это функционально эквивалентно ответу @philnash, но IMO легче понять.

class BirthDate
  def initialize(birth_date)
    @birth_date = birth_date
    @now = Time.now.utc.to_date
  end

  def time_ago_in_years
    if today_is_before_birthday_in_same_year?
      age_based_on_years - 1
    else
      age_based_on_years
    end
  end

  private

  def age_based_on_years
    @now.year - @birth_date.year
  end

  def today_is_before_birthday_in_same_year?
    (@now.month < @birth_date.month) || ((@now.month == @birth_date.month) && (@now.day < @birth_date.day))
  end
end

Использование:

> BirthDate.new(Date.parse('1988-02-29')).time_ago_in_years
 => 31 

Ответ 13

Кажется, что работает (но я был бы признателен, если бы он был проверен).

age = now.year - bday.year
age -= 1 if now.to_a[7] < bday.to_a[7]

Ответ 14

Если вам не нужен день или два, это будет короче и красиво самоочевидно.

(Time.now - Time.gm(1986, 1, 27).to_i).year - 1970

Ответ 15

Хорошо, что об этом:

def age
  return unless dob
  t = Date.today
  age = t.year - dob.year
  b4bday = t.strftime('%m%d') < dob.strftime('%m%d')
  age - (b4bday ? 1 : 0)
end

Предполагается, что мы используем рельсы, вызывая метод age на модели, а модель имеет столбец базы данных даты dob. Это отличается от других ответов, потому что этот метод использует строки, чтобы определить, есть ли мы до этого дня рождения.

Например, если dob - 2004/2/28, а today - 2014/2/28, age будет 2014 - 2004 или 10. Поплавки будут 0228 и 0229. b4bday будет "0228" < "0229" или true. Наконец, мы вычтем 1 из age и получим 9.

Это был бы обычный способ сравнить два раза.

def age
  return unless dob
  t = Date.today
  age = today.year - dob.year
  b4bday = Date.new(2016, t.month, t.day) < Date.new(2016, dob.month, dob.day)
  age - (b4bday ? 1 : 0)
end

Это работает так же, но строка b4bday слишком длинная. 2016 год также не нужен. Результатом было сравнение строк в начале.

Вы также можете сделать это

Date::DATE_FORMATS[:md] = '%m%d'

def age
  return unless dob
  t = Date.today
  age = t.year - dob.year
  b4bday = t.to_s(:md) < dob.to_s(:md)
  age - (b4bday ? 1 : 0)
end

Если вы не используете рельсы, попробуйте это

def age(dob)
  t = Time.now
  age = t.year - dob.year
  b4bday = t.strftime('%m%d') < dob.strftime('%m%d')
  age - (b4bday ? 1 : 0)
end

👍🏼

Ответ 16

Я думаю, что гораздо лучше не считать месяцы, потому что вы можете получить точный день года, используя Time.zone.now.yday.

def age
  years  = Time.zone.now.year - birthday.year
  y_days = Time.zone.now.yday - birthday.yday

  y_days < 0 ? years - 1 : years
end

Ответ 17

Придумал вариант этого решения для Rails

def age(dob)
    now = Date.today
    age = now.year - dob.year
    age -= 1 if dob > now.years_ago(age)
    age
end

Ответ 18

DateHelper может использоваться только для получения лет

puts time_ago_in_words '1999-08-22'

почти 20 лет

Ответ 19

  def birthday(user)
    today = Date.today
    new = user.birthday.to_date.change(:year => today.year)
    user = user.birthday
    if Date.civil_to_jd(today.year, today.month, today.day) >= Date.civil_to_jd(new.year, new.month, new.day)
      age = today.year - user.year
    else
      age = (today.year - user.year) -1
    end
    age
  end

Ответ 20

Time.now.year - self.birthdate.year - (birthdate.to_date.change(:year => Time.now.year) > Time.now.to_date ? 1 : 0)

Ответ 21

Чтобы учитывать високосные годы (и предполагая наличие активных активов):

def age
  return unless birthday
  now = Time.now.utc.to_date
  years = now.year - birthday.year
  years - (birthday.years_since(years) > now ? 1 : 0)
end

years_since будет правильно изменять дату, чтобы принять во внимание непиковые годы (когда день рождения 02-29).

Ответ 22

Вот мое решение, которое также позволяет рассчитать возраст на определенную дату:

def age on = Date.today
  (_ = on.year - birthday.year) - (on < birthday.since(_.years) ? 1 : 0)
end

Ответ 23

Я тоже должен был заниматься этим, но месяцами. Стало слишком сложным. Самый простой способ, о котором я мог думать, заключался в следующем:

def month_number(today = Date.today)
  n = 0
  while (dob >> n+1) <= today
    n += 1
  end
  n
end

Вы можете сделать то же самое с 12 месяцами:

def age(today = Date.today)
  n = 0
  while (dob >> n+12) <= today
    n += 1
  end
  n
end

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