如何在 Rails 中验证日期?

Posted

技术标签:

【中文标题】如何在 Rails 中验证日期?【英文标题】:How do I validate a date in rails? 【发布时间】:2010-10-10 11:39:41 【问题描述】:

我想在 Ruby on Rails 中验证我的模型中的日期,但是,当日、月和年值到达我的模型时,它们已经转换为不正确的日期。

例如,如果我在视图中输入 2009 年 2 月 31 日,当我在控制器中使用 Model.new(params[:model]) 时,它会将其转换为“2009 年 3 月 3 日”,然后我的模型将其视为有效日期,它是,但这是不正确的。

我希望能够在我的模型中进行此验证。有什么办法可以吗,还是我完全错了?

我发现这个“Date validation”讨论了这个问题,但它从未得到解决。

【问题讨论】:

这个问题目前是 Rails 日期验证的最佳 Google 搜索结果之一。您可能会像我一样在第一次通过时错过它:所选答案的重要部分是 validates_timeliness gem。我什至没有阅读其余的答案,因为我在其他地方发现了关于 validates_timeliness 的信息,并且该 gem 可以完成我需要的一切,而无需我编写任何自定义代码。 【参考方案1】:

我猜您正在使用 date_select 帮助程序来生成日期标签。另一种方法是对日、月、年字段使用选择表单助手。像这样(我使用的示例是 created_at 日期字段):

<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %>
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %>
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %>

在模型中,您验证日期:

attr_accessor :month, :day, :year
validate :validate_created_at

private

def convert_created_at
  begin
    self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i)
  rescue ArgumentError
    false
  end
end

def validate_created_at
  errors.add("Created at date", "is invalid.") unless convert_created_at
end

如果您正在寻找插件解决方案,我会查看 validates_timeliness 插件。它的工作原理是这样的(来自 github 页面):

class Person < ActiveRecord::Base
  validates_date :date_of_birth, on_or_before: lambda  Date.current 
  # or
  validates :date_of_birth, timeliness:  on_or_before: lambda  Date.current , type: :date 
end 

可用的验证方法列表如下:

validates_date     - validate value as date
validates_time     - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates          - use the :timeliness key and set the type in the hash.

【讨论】:

建议的解决方案对我不起作用,我使用的是 Rails 2.3.5 我将其重写为更简洁,并针对 Rails 2.3.5 进行了测试,这对我来说确实有效。 嗨,杰克,我已经尝试过您的解决方案,它适用于我添加 attr_accessible :day, :month, :year。这对我来说没有意义......如果你有任何想法,谢谢! 在 Rails 3 中,我使用的是 github.com/adzap/validates_timeliness,它很棒。 validates_timeliness 插件/gem 非常好用。很有魅力,让我的代码更小。感谢您的提示。【参考方案2】:

使用慢性宝石:

class MyModel < ActiveRecord::Base
  validate :valid_date?

  def valid_date?
    unless Chronic.parse(from_date)
      errors.add(:from_date, "is missing or invalid")
    end
  end

end

【讨论】:

在这个具体的例子中,我必须使用Chronic.parse(from_date_before_type_cast) 来获取字符串值输入。否则 Rails 将使用 to_date 对字符串进行类型转换,因为该字段是 datetime 字段。【参考方案3】:

如果您希望兼容 Rails 3 或 Ruby 1.9,请尝试 date_validator gem。

【讨论】:

我刚试过这个。总共花了 2 分钟来安装和添加验证!【参考方案4】:

Active Record 为您提供_before_type_cast 属性,其中包含类型转换之前的原始属性数据。这对于返回带有预类型转换值的错误消息或仅进行类型转换后不可能的验证很有用。

我会回避 Daniel Von Fange 关于覆盖访问器的建议,因为在访问器中进行验证会稍微改变访问器契约。 Active Record 有一个专门针对这种情况的特性。使用它。

【讨论】:

【参考方案5】:

这里有点晚了,但感谢“How do I validate a date in rails?”我设法编写了这个验证器,希望对某人有用:

在你的model.rb里面

validate :date_field_must_be_a_date_or_blank

# If your field is called :date_field, use :date_field_before_type_cast
def date_field_must_be_a_date_or_blank
  date_field_before_type_cast.to_date
rescue ArgumentError
  errors.add(:birthday, :invalid)
end

【讨论】:

迟到总比不到好,及时赶上我!【参考方案6】:

由于您需要在将日期字符串转换为模型中的日期之前对其进行处理,因此我将覆盖该字段的访问器

假设您的日期字段是published_date。将此添加到您的模型对象中:

def published_date=(value)
    # do sanity checking here
    # then hand it back to rails to convert and store
    self.write_attribute(:published_date, value) 
end

【讨论】:

我不知道我是否会将此用于此目的,但这是一个很好的建议。【参考方案7】:

这是一个非慢性的答案..

class Pimping < ActiveRecord::Base

validate :valid_date?

def valid_date?
  if scheduled_on.present?
    unless scheduled_on.is_a?(Time)
      errors.add(:scheduled_on, "Is an invalid date.")
    end
  end
end

【讨论】:

这总是返回 false:"1/1/2013".is_a?(Date) - 日期来自用户输入,因此它作为字符串提供。我必须先将其解析为日期? 正确。 Date.parse("1/1/2013").is_a?(Date),但你可以看到,如果它根本不解析,它也可能不是日期。 需要挽救参数错误......啊,如果它只是自动解析字符串会很棒......哦,好吧,有宝石。【参考方案8】:

您可以像这样验证日期和时间(如果您使用自定义选择,则在控制器中某个可以访问参数的方法中)...

# Set parameters
year = params[:date][:year].to_i
month = params[:date][:month].to_i
mday = params[:date][:mday].to_i
hour = params[:date][:hour].to_i
minute = params[:date][:minute].to_i

# Validate date, time and hour
valid_date    = Date.valid_date? year, month, mday
valid_hour    = (0..23).to_a.include? hour
valid_minute  = (0..59).to_a.include? minute
valid_time    = valid_hour && valid_minute

# Check if parameters are valid and generate appropriate date
if valid_date && valid_time
  second = 0
  offset = '0'
  DateTime.civil(year, month, mday, hour, minute, second, offset)
else
  # Some fallback if you want like ...
  DateTime.current.utc
end

【讨论】:

【参考方案9】:

我推荐这个方法

  validate :birthday_must_be_a_date

  def birthday_must_be_a_date
    birthday&.to_date || errors.add(:birthday, :blank)
  rescue Date::Error
    errors.add(:birthday, :invalid)
  end

【讨论】:

【参考方案10】:

你试过validates_date_time插件吗?

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。 我更喜欢我的回答。两个人同意。 @Pinal 现在链接确实失效了。

以上是关于如何在 Rails 中验证日期?的主要内容,如果未能解决你的问题,请参考以下文章

在 Rails 模型中一起验证日期和时间字段

在Rails 5模型中验证毕业日期

在 Rails 中,如何编写一个带有“where”的查找器来比较两个日期?

如何在 Rails 验证错误中突出显示字段

如何使用 RSpec 在 Rails 中测试:包含验证

如何在 Rails 应用程序中使用用户密码验证帐户终止?