Forum: Ruby on Rails How to validate a date

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
91308e9bc88cb069fd1bcf88e910d042?d=identicon&s=25 Nick Snels (nicksnels)
on 2005-12-03 23:09
Hi,

I would like to validate a date (29 feb 2005 is invalid) and if it is an
invalid date display an error to the user, using the default way of
displaying errors. In my view I have a simple date_select:

<%= date_select (:listing, :start_date,
                 {:order => [:day, :month, :year],
                  :start_year => Time.now.year,
                  :end_year => Time.now.year+1}) %>

When I save data to the db, the field start_date should be validated and
if their is an error display it. So in my model I have tried:

def validate_on_create
  year = start_date(1i)
  month = start_date(2i)
  day = start_date(3i)
  unless Date::valid_date?(year.to_i, month.to_i, day.to_i)
    errors.add(:start_date, "De startdatum moet een geldige datum
zijn.")
  end
end

But this doesn't work, because I can't access the values inside
start_date in the model. I get errors like that start_date is
multiparameter . I know that, but I can't get to the values of the
parameters. Hope somebody can help.

Kind regards,

Nick
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 mrj (Guest)
on 2005-12-03 23:44
(Received via mailing list)
Nick Snels wrote:
>
>   end
> end
>
> But this doesn't work, because I can't access the values inside
> start_date in the model. I get errors like that start_date is
> multiparameter . I know that, but I can't get to the values of the
> parameters. Hope somebody can help.

Nick, applying posted parameters to your model using new, attributes=,
update_attributes, etc., will automatically set start_date to an
object of type Date based on the three component parameters.  If the
date is invalid I think start_date will be set to nil, for which you
can check.  If that's not enough control, you'll have to either arrange
to give validate_on_create access to the params hash so you can check
the values of the start_date(<n>i) keys, or do the validation in a
separate date validation method called from the controller.

I wonder what the value of start_date.before_type_cast would be?
It'd be nice if this was a three-element array for easy validation.


--
We develop, watch us RoR, in numbers too big to ignore.
6e672922c21a5298f2a666ecb1c11d7c?d=identicon&s=25 phil (Guest)
on 2005-12-03 23:52
(Received via mailing list)
On Dec 3, 2005, at 2:09 PM, Nick Snels wrote:

>                   :end_year => Time.now.year+1}) %>
>     errors.add(:start_date, "De startdatum moet een geldige datum
>
> Nick

I don't know if this helps, but we're validating the expiration date
of a credit card this way:

require 'Date'

   validates_each :payment_mm do |m, a, month|
     if ((month.to_i < DateTime.now.month) && (m.payment_yyyy.to_i ==
DateTime.now.year))
       m.errors.add(a, "Your credit card seems to be expired.  Please
enter valid payment information.")
     end
   end

Also, remember that if you operate on an instance's values, you need
to refer to 'self'.  E.g., in the same model we send the order email
and then clear the credit card number before saving:

def before_save
    OrderMailer.deliver_order(self)
    self.payment_account="(not recorded)"
end


Phil
91308e9bc88cb069fd1bcf88e910d042?d=identicon&s=25 Nick Snels (Guest)
on 2005-12-04 14:23
Hi,

thank you both for the command. Helped to clear some issues up. I have
simplified my model code to, just to see the effect of the date:


  def validate
  	logger.warn ("Start-- " + start_date.to_s)
  end

I get a date back when I enter a valid date. But when I enter an invalid
date I get the error:

1 error(s) on assignment of multiparameter attributes

I have tried to catch the error like this:

  def validate
  	begin
  	unless self.start_date
  	  errors.add(:start_date, "De startdatum moet een geldige datum
zijn.")
  	end
  	rescue
  	errors.add(:start_date, "De startdatum moet een geldige datum zijn.")
  	end
end

But I keep getting the error. The error obviously means the date I
entered cann't get converted to a valid date. So if I can catch this
error I'm done. But ...

Kind regards,

Nick
91308e9bc88cb069fd1bcf88e910d042?d=identicon&s=25 Nick Snels (Guest)
on 2005-12-04 18:17
Getting tired of all the error messages, I thought I would throw the
date_select away in favor of:

<%= select_day (Time.now.day, :prefix => "listing") %>
<%= select_month (Time.now.month, :prefix => "listing") %>
<%= select_year (Time.now.year, :prefix => "listing", :start_year =>
Time.now.year,
                  :end_year => Time.now.year+1) %>

Generates the same HTML code and now I get three separate params, not
related to each other, so no more multiparameter stuff.

I my model I added:

attr_accessor :day, :month, :year

  def validate
    if Date::valid_civil?(year.to_i, month.to_i, day.to_i) == nil
      errors.add(:year, "geen geldige datum.")
    else
      self.start_date = year.to_s + "-" + month.to_s + "-" + day.to_s
    end
  end

So now I should also be able to check if the date is before today. Only
problem I have right now is I can't get Rails to draw a red box around
my three select fields (day, month and year). But if that is all, at
least my visitors are no longer able to enter an invalid date.

My advice stay away from date_select if you want to do some serious date
checking, at least until they introduce a decent date validator, like
validate_date_of or something like that.

Kind regards,

Nick
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-04 20:15
(Received via mailing list)
On Sat, 3 Dec 2005, Nick Snels wrote:

> their is an error display it. So in my model I have tried:
>
> But this doesn't work, because I can't access the values inside start_date
> in the model. I get errors like that start_date is multiparameter . I know
> that, but I can't get to the values of the parameters. Hope somebody can
> help.

say you have a table like:

   create table moments (
       id serial,
       moment timestamp
   );

and a controller like this:

   [ahoward@localhost date_validation]$ cat
app/controllers/moment_controller.rb
   class MomentController < ApplicationController
     scaffold :moment
   end

then a model like this will solve your problem

   [ahoward@localhost date_validation]$ cat app/models/moment.rb
   class ::Time
     class << self
       def normalized? t, values
         tvalues = %w(year month day hour min sec usec).map{|m| t.send
m}
         values.zip(tvalues){|vin,vout| return true if vin != vout}
         return false
       end
       %w( local mktime gm utc ).each do |method|
         eval <<-code
           alias __org_#{ method }__ #{ method }
           def #{ method }(*a, &b)
             t = __org_#{ method }__(*a, &b)
             t.instance_eval{@original_arguments = a}
             t.instance_eval{@normalized = self.class.normalized? self,
a}
             t
           end
         code
       end
     end
     attr_reader 'normalized'
     alias_method 'normalized?', 'normalized'
     attr_reader 'original_arguments'
   end

   class Moment < ActiveRecord::Base
     protected
       def validate *a, &b
         t = moment
         if t.nil? or t.normalized?
           values = t.original_arguments
           errors.add self.class.to_s, "bad time from
<#{values.inspect}>"
         end
         super
       end
   end


though you'd want to put the Time code somewhere else in practice...


an attempt to enter Feb 31st on my system results in an error page that
looks like

   <snip>

     Editing moment
     1 error prohibited this moment from being saved

     There were problems with the following fields:

         * Moment bad time from <[2005, 2, 31, 11, 48]>


i've posted this fix in the past but, for some reason, there was not
much
interest.  read this thread for more info:

   http://wrath.rubyonrails.org/pipermail/rails/2005-...

this technique could easily be used to make a nice time/date validator
and is,
in fact, extensible for any multi-param rails object: the approach is
always
the same - try ctor, munge inputs if required, remember the original
arguments
for later access in validation.

kind regards.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy.  all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
417e50c1bb8f3958b58e9b1f593a9e0b?d=identicon&s=25 shawn_garbett (Guest)
on 2005-12-04 20:48
(Received via mailing list)
--- Nick Snels <nick.snels@gmail.com> wrote:

>                  {:order => [:day, :month, :year],
>   month = start_date(2i)
> values inside
> start_date in the model. I get errors like that
> start_date is
> multiparameter . I know that, but I can't get to the
> values of the

I made a date validator:
http://www.garbett.org/?q=node/27, as well as several
other helpers. This code is free for the taking. No
license just pure public domain. I relinquish all
copyrights and all that other legal stuff on this
code. May it help you.

With this one can just put
  validates_date :birthdate

in the model, and the problem is solved.

Code from site:

require 'parsedate'

ActiveRecord::Base.class_eval do

  def self.validate_date(string)
    data = ParseDate.parsedate(string)
    0.upto(2) { |i| raise "Bogus date [#{string}]
encountered" if data[i].nil? }
    raise "Pre-1800 date [#{string}] encountered" if
data[0] < 1800
    raise "Invalid date [#{string}] encountered" if
nil==Date.new(data[0],data[1],data[2])
    return true
  rescue Exception => e
    logger.error("Date format exception [#{e}]")
    return false
  end


  # Configuration options:
  # * message - A custom error message (default is:
"can't be blank")
  # * on - Specifies when this validation is active
(default is :save, other options :create, :update)
  # * if - Specifies a method, proc or string to call
to determine if the validation should
  # occur (e.g. :if => :allow_validation, or :if =>
Proc.new { |user| user.signup_step > 2 }).  The
  # method, proc or string should return or evaluate
to a true or false value.
  def self.validates_date(*attr_names)
    configuration = { :message => 'Invalid date, use
(MM/DD/YYYY)', :on => :save }
    configuration.update(attr_names.pop) if
attr_names.last.is_a?(Hash)
    attr_names.each do |attr_name|
      send(validation_method(configuration[:on])) do
|record|
        unless configuration[:if] &&
!evaluate_condition(configuration[:if], record)
          date =
record.attributes_before_type_cast[attr_name.to_s]
          if not date.nil? and not date.class == Date
and date.length > 0 and date.class == String and not
self.validate_date(date)

record.errors.add(attr_name,configuration[:message])
          end
        end
      end
    end
  end
end

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
Eea7ad39737b0dbf3de38874e0a6c7d8?d=identicon&s=25 justin (Guest)
on 2005-12-05 01:48
(Received via mailing list)
ara.t.howard@noaa.gov wrote:

> On Sat, 3 Dec 2005, Nick Snels wrote:
>
>> I would like to validate a date (29 feb 2005 is invalid) and if it is an
>> invalid date display an error to the user, using the default way of
>> displaying errors. In my view I have a simple date_select:

[snip]

> app/controllers/moment_controller.rb
>   class MomentController < ApplicationController
>     scaffold :moment
>   end
>
> then a model like this will solve your problem

[snip]

> arguments
> for later access in validation.
>
> kind regards.
>
> -a

Hi, Ara - having discussed this with you over eight months ago, I would
never have believed that Rails would reach 1.0 without sorting it out.
Maybe date_select, like scaffolding, isn't intended for production use.

regards

   Justin
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-05 01:52
(Received via mailing list)
On Mon, 5 Dec 2005, Justin Forder wrote:

> Hi, Ara - having discussed this with you over eight months ago, I would
> never have believed that Rails would reach 1.0 without sorting it out. Maybe
> date_select, like scaffolding, isn't intended for production use.

indeed.  particularly when there are several viable alternatives that
address
the issue floating around.  i'm not going to admit in public that i only
remember i'd solved this once before until __after__ searching the
archives
and stubmling on the thread between you and i ;-)

cheers.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy.  all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
This topic is locked and can not be replied to.