Get person's age in Ruby

2020-01-24 19:08发布

I'd like to get a person's age from its birthday. now - birthday / 365 doesn't work, because some years have 366 days. I came up with the following code:

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

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

Is there a more Ruby'ish way to calculate age?

24条回答
仙女界的扛把子
2楼-- · 2020-01-24 19:42

One liner in Ruby on Rails (ActiveSupport). Handles leap years, leap seconds and all.

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

Logic from here - Calculate age in C#

Assuming both dates are in same timezone, if not call utc() before to_s() on both.

查看更多
beautiful°
3楼-- · 2020-01-24 19:42

Because Ruby on Rails is tagged, the dotiw gem overrides the Rails built-in distance_of_times_in_words and provides distance_of_times_in_words_hash which can be used to determine the age. Leap years are handled fine for the years portion although be aware that Feb 29 does have an effect on the days portion that warrants understanding if that level of detail is needed. Also, if you don't like how dotiw changes the format of distance_of_time_in_words, use the :vague option to revert to the original format.

Add dotiw to the Gemfile:

gem 'dotiw'

On the command line:

bundle

Include the DateHelper in the appropriate model to gain access to distance_of_time_in_words and distance_of_time_in_words_hash. In this example the model is 'User' and the birthday field is 'birthday.

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

Add this method to that same model.

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

Usage:

u = User.new("birthday(1i)" => "2011", "birthday(2i)" => "10", "birthday(3i)" => "23")
u.age
查看更多
forever°为你锁心
4楼-- · 2020-01-24 19:43

The answers so far are kinda weird. Your original attempt was pretty close to the right way to do this:

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

You will get a fractional result, so feel free to convert the result to an integer with to_i. This is a better solution because it correctly treats the date difference as a time period measured in days (or seconds in the case of the related Time class) since the event. Then a simple division by the number of days in a year gives you the age. When calculating age in years this way, as long as you retain the original DOB value, no allowance needs to be made for leap years.

查看更多
We Are One
5楼-- · 2020-01-24 19:43
  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
查看更多
我只想做你的唯一
6楼-- · 2020-01-24 19:44
Time.now.year - self.birthdate.year - (birthdate.to_date.change(:year => Time.now.year) > Time.now.to_date ? 1 : 0)
查看更多
Melony?
7楼-- · 2020-01-24 19:44

To account for leap years (and assuming activesupport presence):

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 will correctly modify the date to take into account non-leap years (when birthday is 02-29).

查看更多
登录 后发表回答