tags:

views:

59

answers:

3

Hi

I have a method which parses a string in to a date, but i want to validate that i don't try to parse a non numeric string or a string which dosent represent a date or time format?

how can id o this?

at the moment i have:

if(string=~ /^\D*$/ )
{
return false
else 
do something_else
}

this was fine for a non numeric string like "UNKNOWN" but wouldn't work for "UNKNOWN1"

any idea what i can use to make sure that only date or time formats are parsed?

+1  A: 

DateTime.strptime v ParseDate.parsedate


You would think that you could use either Date.parse or DateTime.parse to check for bad dates (see more on Date.parse here)

d = Date.parse(string) rescue nil

if d 
   do_something
else
   return false
end

because bad values throw an exception which you can catch. However the test strings suggested actually return a Date with Date.parse

For example ..

~\> irb
>> Date.parse '12-UNKN/34/OWN1'
=> #<Date: 4910841/2,0,2299161>
>> 

Date.parse just isn't clever enough to do the job :-(

ParseDate.parsedate does a better job. You can see that it attempts to parse the date but in the test examples, doesn't find a valid year or month. More information here

>> require 'parsedate'
=> true
>> ParseDate.parsedate '2010-09-09'
=> [2010, 9, 9, nil, nil, nil, nil, nil]
>> ParseDate.parsedate 'dsadasd'
=> [nil, nil, nil, nil, nil, nil, nil, nil]
>> ParseDate.parsedate '12-UNKN/34/OWN1'
=> [nil, nil, 12, nil, nil, nil, nil, nil]
>> ParseDate.parsedate '12-UNKN/34/OWN1'
=> [nil, nil, 12, nil, nil, nil, nil, nil]
Chris McCauley
i tried that and wrote some unit tests that all failed. when i passed in the following strings: ' WRONG_STRINGS = ["12-UNKN/34/OWN1", "UNK243N324OWN1", "324asdajkl31"] ' :(
Mo
@Mo - that would be the Date.parse, ParseDate.parsedate should return an array indicating what it had processed.
Chris McCauley
A: 

Ruby's parsers are optimistic, if you can throw out a bunch of garbage and get a result from the input string, Date.parse and DateTime.strptime will try to do it.

You want a pessimistic and strict check, which means instead of assuming acceptance after trying to hunt for garbage with a regex, you should assume rejection and hunt for treasure with your regex.

Your first check: "Is a string numeric" is using a regex to try and find a string which is comprised entirely of non-numeric characters, and rejecting if it finds it. \D (with a capital D) is looking for non-numeric characters, and input strings will only match your regex if it is comprised entirely of 0 or more non-numeric characters.

You'll likely have better luck with the following logic for numerics:

if(string=~ /^\d*$/ )
 something_else
else 
 return false
end

This matches a string comprised entirely of 0 or more numeric characters, does something_else if it finds it, and returns false otherwise.

For times you want to explicitly search for times and reject all other values. For an HH:MM:SSAM format which tolerates omitting leading 0's for each field, with 12 hour times you could use the following:

if (string =~ /^[01]?\d:[0-5]?\d:[0-5]?\d[AP]M$/)
  something_else
else
  return false
end

Likewise for dates you want to explicitly search for dates that are valid, and reject all other values. For MM/DD/YYYY which tolerates omitting leading 0's for everything but years field you could go with:

if (string =~ /^[0-1]\d\/[0-3]?\d\/\d{4}/)
  something_else
else
  return false
end

Ruby's utility functions try to be verbose in what they accept, but for validation that is not a useful trait. Be strict, assume that everything is invalid until it proves otherwise, then accept it.

animal
A: 

I'd advise you to establish a list of date and datetime formats that you expect and intend to support. You can define them using strftime compatible strings, and then use the same strings when parsing dates, using DateTime#strptime. Try to parse your input strings with each supported pattern, the first one which doesn't throw an exception will return parsed date. If each throws an exception, the string is not valid date.

Mladen Jablanović