How do I validate a date in rails?

112

Solution 1

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, on_or_before: lambda { Date.current }
  # or
  validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date }
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
validates          - use the :timeliness key and set the type in the hash.

Solution 2

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

Solution 3

If you want Rails 3 or Ruby 1.9 compatibility try the date_validator gem.

Solution 4

Active Record gives you _before_type_cast attributes which contain the raw attribute data before typecasting. This can be useful for returning error messages with pre-typecast values or just doing validations that aren't possible after typecast.

I would shy away from Daniel Von Fange's suggestion of overriding the accessor, because doing validation in an accessor changes the accessor contract slightly. Active Record has a feature explicitly for this situation. Use it.

Solution 5

A bit late here, but thanks to "How do I validate a date in rails?" I managed to write this validator, hope is useful to somebody:

Inside your 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
Share:
112
jessica
Author by

jessica

Updated on August 18, 2021

Comments

  • jessica
    jessica almost 3 years

    I created the following cli in order to delete the logs with date format that oldest then 500 min

    date format is:

     data-node.log.xxxx-xx-xx-[1-10]
    

    the cli that should removed the logs

    find /var/log/test/  -type f -mmin +500 -regextype sed -regex '.*\.log\.[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2,10\}$' -delete
    

    as we can see the logs still exists

    ls -l /var/log/test/
    
    
    
    -rw-r--r-- 1 root root 0 10:02 data-node.log.2019-12-14
    -rw-r--r-- 1 root root 0 10:02 data-node.log.2019-12-15
    -rw-r--r-- 1 root root 0 10:02 data-node.log.2019-06-16
    -rw-r--r-- 1 root root 0 10:02 data-node.log.2020-01-17
    -rw-r--r-- 1 root root 0 10:05 data-node.log.2020-01-1723
    -rw-r--r-- 1 root root 0 10:05 data-node.log.2020-01-172334
    -rw-r--r-- 1 root root 0 10:05 data-node.log.2020-01-17233434
    -rw-r--r-- 1 root root 0 10:05 data-node.log.2020-01-1723343434
    

    where I am wrong?

  • Dan Rosenstark
    Dan Rosenstark almost 15 years
    I don't know if I'll use this for this purpose, but it's a great suggestion.
  • Roger
    Roger over 14 years
    The suggested solution does not work for me and I am using Rails 2.3.5
  • Jack Chu
    Jack Chu about 14 years
    I rewrote it to be cleaner, and tested it against Rails 2.3.5 and this does work for me.
  • benoitr
    benoitr about 13 years
    Hi Jack, I've tried your solution, it works for me adding attr_accessible :day, :month, :year. Which doesn't make sense for me... Thanks if you have any idea!
  • Calvin
    Calvin almost 12 years
    In this specific example, I had to use Chronic.parse(from_date_before_type_cast) in order to get the string value input. Otherwise Rails would typecast the string with to_date since the field was a datetime field.
  • Jason Swett
    Jason Swett almost 12 years
    In Rails 3, I'm using github.com/adzap/validates_timeliness and it's awesome.
  • mjnissim
    mjnissim almost 12 years
    The validates_timeliness plugin/gem is really nice to use. Works a charm and keeps my code smaller. Thanks for the tip.
  • jflores
    jflores over 11 years
    I just tried this. Took all of 2 minutes to install and add validation!
  • Mohamad
    Mohamad about 11 years
    This always returns false: "1/1/2013".is_a?(Date) - The date comes from user input, so it's being fed as a string. I'd have to parse it as a date first?
  • Trip
    Trip about 11 years
    Correct. Date.parse("1/1/2013").is_a?(Date), but you could see that if it doesn't parse at all, its also probably not a date.
  • Mohamad
    Mohamad about 11 years
    Need to rescue argument error... ahh, it would have been awesome if it just parsed the string automatically... oh well, there are gem's for that.
  • Pinal
    Pinal almost 10 years
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.
  • Ryan Duffield
    Ryan Duffield almost 10 years
    I like my answer better. Two people agree.
  • Wayne Conrad
    Wayne Conrad over 9 years
    @Pinal The link is indeed dead now.
  • fatfrog
    fatfrog about 3 years
    Better late than never, and in time for me!