Ruby中的年龄计算

时间:2012-05-05 15:42:14

标签: ruby-on-rails ruby datetime

我正在尝试编写一个rails应用程序,但目前显示的出生日期显示为正常的日期格式,但我很乐意在视图中显示年龄而不是

我在控制器中的方法如下,我的数据库中有一个用于出生日期的列DOb

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

它显示像DOB:23/5/2011,但我希望它可以在几年内成为年龄。

如何使用验证器检查年龄是否低于18岁?

6 个答案:

答案 0 :(得分:7)

对于验证者,您可以使用custom method

validate :over_18

def over_18
  if dob + 18.years >= Date.today
    errors.add(:dob, "can't be under 18")
  end
end

答案 1 :(得分:3)

我遇到了这个问题并且认为我发布了一个更现代的答案,它利用了Rails便捷方法语法和自定义验证器。

自定义年龄验证器

此验证程序将为您要验证的字段名称和最低年龄要求选择哈希值。

# Include somewhere such as the top of user.rb to make sure it gets loaded by Rails.
class AgeValidator < ActiveModel::Validator

  def initialize(options)
    super
    @field = options[:field] || :birthday
    @min_age = options[:min_age] || 18
    @allow_nil = options[:allow_nil] || false
  end

  def validate(record)
    date = record.send(@field)
    return if date.nil? || @allow_nil

    unless date <= @min_age.years.ago.to_date
      record.errors[@field] << "must be over #{@min_age} years ago."
    end
  end

end

使用示例

class User < ActiveRecord::Base
  validates_with AgeValidator, { min_age: 18, field: :dob }
end

User#age便捷方法

要计算用户的展示年龄,您需要小心计算闰年。

class User < ActiveRecord::Base
  def age

    return nil unless dob.present?
    # We use Time.current because each user might be viewing from a
    # different location hours before or after their birthday.
    today = Time.current.to_date

    # If we haven't gotten to their birthday yet this year.
    # We use this method of calculation to catch leapyear issues as
    # Ruby's Date class is aware of valid dates.
    if today.month < dob.month || (today.month == dob.month && dob.day > today.day)
      today.year - dob.year - 1
    else
      today.year - dob.year
    end

  end
end

和规格!

require 'rails_helper'
describe User do
  describe :age do
    let(:user) { subject.new(dob: 30.years.ago) }
    it "has the proper age" do
      expect(user.age).to eql(30)

      user.birthday += 1.day
      expect(user.age).to eql(29)
    end
  end
end

答案 2 :(得分:1)

这里有几个不同的问题。

  1. 年龄计算属于哪里?

    年龄计算应该是辅助方法或模型方法。我总是把它做成一个模型方法,但最近看到了在装饰器甚至辅助方法中使用这些显示元素的优点。在您的情况下,首先将其放入模型,然后从那里开始:

    def age
        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. 您如何确认此人已超过18岁?

    如果他们未满18岁,你是否真的限制某人被保存到数据库中?或者你是否限制观看能力?

    def is_over_18?
        age >= 18
    end
    
  3. 这会写一个自定义每个验证器或使用Proc,但我真的质疑以这种方式验证的决定。

答案 3 :(得分:1)

计算年龄时应该小心。这是一个正确的方法:

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

之后,根据@Baldrick:

validate :check_over_18

def check_over_18
  errors.add(:dob, "can't be under 18") if age < 18
end

答案 4 :(得分:1)

要查找年龄,您可以使用gem adroit-age

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

答案 5 :(得分:0)

我也必须处理这个,但是几个月。变得太复杂了。我能想到的最简单的方法是:

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天和闰年等。