How to check if a string is a valid date

Ruby on-RailsRubyStringDate

Ruby on-Rails Problem Overview


I have a string: "31-02-2010" and want to check whether or not it is a valid date. What is the best way to do it?

I need a method which which returns true if the string is a valid date and false if it is not.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end

Solution 2 - Ruby on-Rails

Here is a simple one liner:

DateTime.parse date rescue nil

I probably wouldn't recommend doing exactly this in every situation in real life as you force the caller to check for nil, eg. particularly when formatting. If you return a default date|error it may be friendlier.

Solution 3 - Ruby on-Rails

d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

Solution 4 - Ruby on-Rails

Parsing dates can run into some gotcha's, especially when they are in a MM/DD/YYYY or DD/MM/YYYY format, such as short dates used in U.S. or Europe.

Date#parse attempts to figure out which to use, but there are many days in a month throughout the year when ambiguity between the formats can cause parsing problems.

I'd recommend finding out what the LOCALE of the user is, then, based on that, you'll know how to parse intelligently using Date.strptime. The best way to find where a user is located is to ask them during sign-up, and then provide a setting in their preferences to change it. Assuming you can dig it out by some clever heuristic and not bother the user for that information, is prone to failure so just ask.

This is a test using Date.parse. I'm in the U.S.:

>> Date.parse('01/31/2001')
ArgumentError: invalid date

>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>

The first was the correct format for the U.S.: mm/dd/yyyy, but Date didn't like it. The second was correct for Europe, but if your customers are predominately U.S.-based, you'll get a lot of badly parsed dates.

Ruby's Date.strptime is used like:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>

Solution 5 - Ruby on-Rails

Date.valid_date? *date_string.split('-').reverse.map(&:to_i)

Solution 6 - Ruby on-Rails

I'd like to extend Date class.

class Date
  def self.parsable?(string)
    begin
      parse(string)
      true
    rescue ArgumentError
      false
    end
  end
end
example
Date.parsable?("10-10-2010")
# => true
Date.parse("10-10-2010")
# => Sun, 10 Oct 2010
Date.parsable?("1")
# => false
Date.parse("1")
# ArgumentError: invalid date from (pry):106:in `parse'

Solution 7 - Ruby on-Rails

Another way to validate date:

date_hash = Date._parse(date.to_s)
Date.valid_date?(date_hash[:year].to_i,
                 date_hash[:mon].to_i,
                 date_hash[:mday].to_i)

Solution 8 - Ruby on-Rails

Try regex for all dates:

/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")

For only your format with leading zeroes, year last and dashes:

/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")

the [-/] means either - or /, the forward slash must be escaped. You can test this on http://gskinner.com/RegExr/

add the following lines, they will all be highlighted if you use the first regex, without the first and last / (they are for use in ruby code).

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1

Solution 9 - Ruby on-Rails

A stricter solution

It's easier to verify the correctness of a date if you specify the date format you expect. However, even then, Ruby is a bit too tolerant for my use case:

Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?

Clearly, at least one of these strings specifies the wrong weekday, but Ruby happily ignores that.

Here's a method that doesn't; it validates that date.strftime(format) converts back to the same input string that it parsed with Date.strptime according to format.

module StrictDateParsing
  # If given "Tue, 2017-01-17" and "%a, %Y-%m-%d", will return the parsed date.
  # If given "Wed, 2017-01-17" and "%a, %Y-%m-%d", will error because that's not
  # a Wednesday.
  def self.parse(input_string, format)
    date = Date.strptime(input_string, format)
    confirmation = date.strftime(format)
    if confirmation == input_string
      date
    else
      fail InvalidDate.new(
        "'#{input_string}' parsed as '#{format}' is inconsistent (eg, weekday doesn't match date)"
      )
    end
  end

  InvalidDate = Class.new(RuntimeError)
end

Solution 10 - Ruby on-Rails

Posting this because it might be of use to someone later. No clue if this is a "good" way to do it or not, but it works for me and is extendible.

class String
      
  def is_date?
  temp = self.gsub(/[-.\/]/, '')
  ['%m%d%Y','%m%d%y','%M%D%Y','%M%D%y'].each do |f|
  begin
    return true if Date.strptime(temp, f)
      rescue
       #do nothing
    end
  end
        
  return false
 end
end

This add-on for String class lets you specify your list of delimiters in line 4 and then your list of valid formats in line 5. Not rocket science, but makes it really easy to extend and lets you simply check a string like so:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.

Solution 11 - Ruby on-Rails

You can try the following, which is the simple way:

"31-02-2010".try(:to_date)

But you need to handle the exception.

Solution 12 - Ruby on-Rails

Similar to the solution by @ironsand, I prefer to create an overridden instance method on String:

class String
  def valid_datetime?
    to_datetime
    true
  rescue ArgumentError
    false
  end
end

Solution 13 - Ruby on-Rails

require 'date'
#new_date and old_date should be String
# Note we need a ()
def time_between(new_date, old_date)
  new_date = (Date.parse new_date rescue nil)
  old_date = (Date.parse old_date rescue nil)
  return nil if new_date.nil? || old_date.nil?
  (new_date - old_date).to_i
end

puts time_between(1,2).nil?
#=> true
puts time_between(Time.now.to_s,Time.now.to_s).nil?
#=> false

Solution 14 - Ruby on-Rails

Date.parse not raised exception for this examples:

Date.parse("12!12*2012")
=> Thu, 12 Apr 2018

Date.parse("12!12&2012")
=> Thu, 12 Apr 2018

I prefer this solution:

Date.parse("12!12*2012".gsub(/[^\d,\.,\-]/, ''))
=> ArgumentError: invalid date

Date.parse("12-12-2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

Date.parse("12.12.2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

Solution 15 - Ruby on-Rails

Method:

require 'date'
def is_date_valid?(d)
  Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end

Usage:

config[:dates].split(",").all? { |x| is_date_valid?(x)}

This returns true or false if config[:dates] = "12/10/2012,05/09/1520"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSalilView Question on Stackoverflow
Solution 1 - Ruby on-RailsmpdView Answer on Stackoverflow
Solution 2 - Ruby on-Railsrobert_murrayView Answer on Stackoverflow
Solution 3 - Ruby on-RailsSohanView Answer on Stackoverflow
Solution 4 - Ruby on-Railsthe Tin ManView Answer on Stackoverflow
Solution 5 - Ruby on-RailsSamer BunaView Answer on Stackoverflow
Solution 6 - Ruby on-RailsironsandView Answer on Stackoverflow
Solution 7 - Ruby on-RailsSlava ZharkovView Answer on Stackoverflow
Solution 8 - Ruby on-RailsMrFoxView Answer on Stackoverflow
Solution 9 - Ruby on-RailsNathan LongView Answer on Stackoverflow
Solution 10 - Ruby on-RailsDave SandersView Answer on Stackoverflow
Solution 11 - Ruby on-RailsBruno SilvaView Answer on Stackoverflow
Solution 12 - Ruby on-RailscsalvatoView Answer on Stackoverflow
Solution 13 - Ruby on-RailssanderView Answer on Stackoverflow
Solution 14 - Ruby on-RailsIgor BiryukovView Answer on Stackoverflow
Solution 15 - Ruby on-RailsTemplum IovisView Answer on Stackoverflow