views:

6492

answers:

5

I want to be able to validate a date in my model in ruby on rails. However, the day, month and year values are already converted into an incorrect date by the time they reach my model.

For example, if I enter February 31st 2009 in my view, when I do Model.new(params[:model]) in my controller, it converts it to March 3rd 2009. Which my model then sees as a valid date, which it is, but it is the incorrect date.

I would like to be able to do this validation in my model. Is there any way that I can, or am I going about this completely wrong?

I found this post that discusses the problem but never has a resolution.

+2  A: 

Since you need to handle the date string before it is converted to a date in your model, I'd override the accessor for that field

Lets say your date field is "published_date". Add this to you model object:

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
Daniel Von Fange
I don't know if I'll use this for this purpose, but it's a great suggestion.
Yar
+1  A: 

Have you tried the validates_date_time plug-in?

Ryan Duffield
+5  A: 

I'm guessing you're using the date_select helper to generate the tags for the date. Another way you could do it is to use select form helper for the day, month, year fields. Like this (example I used is the created_at date field):

<%= 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 %>

And in the model, you validate the date:

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

If you're looking for a plugin solution, I'd checkout the validates_timeliness plugin. It works like this (from the github page):

class Person < ActiveRecord::Base
  validates_date :date_of_birth
end

The list of validation methods available are as follows:

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
Jack Chu
The suggested solution does not work for me and I am using Rails 2.3.5
Roger
I rewrote it to be cleaner, and tested it against Rails 2.3.5 and this does work for me.
Jack Chu
+4  A: 

Using the chronic gem:

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
Roger
+2  A: 

If you want Rails 3 or ruby 1.9 compatibility try date_validator gem!

Txus