2009-02-28 113 views
82

我想在我的模型中验证Ruby on Rails中的日期,但是,日期,月份和年份值在到达我的模型时已经转换为不正确的日期。如何在导轨中验证日期?

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

我希望能够在我的模型中进行此验证。有什么方法可以,或者我是否完全错误?

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

+1

这个问题是目前的一个Rails日期验证的最新Google结果。您可能会像第一次那样错过它:所选答案的重要部分是validates_timeliness宝石。我甚至没有阅读答案的其余部分,因为我发现了其他地方的validates_timeliness,并且没有我必须编写任何自定义代码,那么gem可以完成我所需要的一切。 – 2012-08-06 18:50:58

回答

55

我猜你正在使用date_select帮手来生成日期的标签。你可以做的另一种方法是在日,月,年字段中使用select form helper。像这样(比如我用的是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. 
+0

建议的解决方案不适用于我,我正在使用Rails 2.3.5 – Roger 2010-01-05 17:24:39

+0

我重写了它更清洁,并针对Rails 2.3.5进行了测试,这对我很有用。 – 2010-03-18 20:44:44

2

既然你需要处理的日期字符串之前它被转换成模型中的约会,我会重写访问该字段

比方说,您的日期字段是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 
+0

我不知道我是否会将此用于此目的,但这是一个很好的建议。 – 2009-08-10 14:22:25

-3

您是否试过validates_date_time插件?

+0

尽管这个链接可能回答这个问题,但最好在这里包含答案的重要部分,并提供供参考的链接。如果链接页面更改,则仅链接答案可能会失效。 – Pinal 2014-06-12 19:57:17

+0

我更喜欢我的回答。两个人同意。 – 2014-06-12 20:20:03

+3

@Pinal现在链接确实死了。 – 2014-10-27 15:29:47

19

使用慢性宝石:

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 
9

活动记录为您提供_before_type_cast属性,其中包含原始属性数据之前的类型转换。这对于返回具有pre-typecast值的错误消息或者只是进行类型转换后不可能的验证很有用。

我会回避Daniel Von Fange的重写访问者的建议,因为在访问者中进行验证会稍微改变访问者合约。 Active Record有一个明确的特征用于这种情况。用它。

1

这里有一个非慢性答案..

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 
0

您可以(如果你使用自定义选择与访问某处的方法在你的控制器到您的PARAMS)验证,像这样的日期和时间.. 。

# 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 
0

有点晚了这里,但由于“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