How to check if a string is a valid date

100,990

Solution 1

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

Solution 2

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

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

Solution 4

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

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

Share:
100,990
Salil
Author by

Salil

I'm a software engineering graduate from Nagpur University (BE) with good academic standing and strong professional experience. 12 year experience in Ruby, Ruby on Rails. Profound knowledge of working with dynamic and database-driven websites and services. Very solid understanding of Web 2.0 technologies HTML, XML, CSS, jQuery, jQuery-UI, Ajax and JavaScript. Knowledge of distributed revision control system like Git. Experience working with like PostgreSQL and MySQL database. Experience in Elastic Search Engine. Knowledge of NoSQL such as MongoDB. Experience with Agile, Iterative, and Test-Driven Development methods

Updated on October 04, 2020

Comments

  • Salil
    Salil over 3 years

    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.

    • corroded
      corroded almost 14 years
      why not just make a date_time drop down instead of taking in a string that you have to validate?
    • Salil
      Salil almost 14 years
      client's requirement. i already suggest it but can't do that :)
    • Seanny123
      Seanny123 almost 11 years
      As a general rule, aren't you supposed to do input validation on the front end of an application?
    • n13
      n13 over 10 years
      Much better answers here - this is how to do it. stackoverflow.com/questions/597328/…
    • SparK
      SparK over 8 years
      Always validate on the backend, no matter how good your front-end is, don't trust it!
    • ymoreau
      ymoreau over 5 years
      Possible duplicate of How do I validate a date in rails?
    • Aryeh Beitz
      Aryeh Beitz over 5 years
      I use this: "31-02-2010".to_time.present?
  • hoyhoy
    hoyhoy over 12 years
    Your LOCALE seems off. I get this >> Date.parse('01/31/2001') => Wed, 31 Jan 2001 >> ?> Date.parse('31/01/2001') ArgumentError: invalid date
  • Pier-Olivier Thibault
    Pier-Olivier Thibault almost 12 years
    It's not a Date class feature, it's exception handling.
  • Greconomist
    Greconomist almost 12 years
    Date.parse('2012-08-13== 00:00:00') # => Mon, 13 Aug 2012
  • Arnis Lapsa
    Arnis Lapsa over 11 years
    exceptions should be used only when you don't really expect an error
  • yagooar
    yagooar over 11 years
    This regular expressions only match some fixed formats, without caring about the semantics of a date. Your matcher allows dates such as 32-13-2010 which is wrong.
  • yagooar
    yagooar over 11 years
    Using a "catch-it-all" rescue should be considered an anti-pattern. It can hide out other errors which we don't expect and make the debugging of the code extremely difficult.
  • Matthew Brown
    Matthew Brown about 11 years
    So... if it is indeed an anti-pattern, how would you answer the question?
  • Neil Goodman
    Neil Goodman over 10 years
    Good enough if you want a rough estimate if any arbitrary string could be parsed as a date.
  • mrt
    mrt over 10 years
    As bad as it looks with the exception handling this is how the default Rails validators are written as well: i.e. github.com/rails/rails/blob/…
  • n13
    n13 over 10 years
    Sorry this is a wrong answer. It doesn't check whether or not a string is a valid date, it merely checks of Date.parse can parse the string. Date.parse seems to be very forgiving when it comes to dates, e.g. it will parse "FOOBAR_09_2010" as the date 2012-09-09.
  • n13
    n13 over 10 years
    This is pretty simple and good for enforcing a xx-xx-YYYY format
  • Ashley Raiteri
    Ashley Raiteri over 10 years
    If you want to enforce a strict single format date string option, then this is the best option as it avoids Exceptions and is deterministic. Date.valid_date? is the method to use at least for Ruby 2. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
  • Matstar
    Matstar over 10 years
    What version of Ruby supports is_valid?? Tried 1.8, 2.0 and 2.1. None of them seem to have that one. All seem to have valid_date?, though.
  • Matstar
    Matstar over 10 years
    Excellent, thanks. This one seems to be available at least in 1.8, 2.0 and 2.1. Note that you need to require "date" first or you'll get "undefined method".
  • PJP
    PJP almost 10 years
    Where "rough estimate" means values like '00/00/0000' and '99-99/9999' are possible candidates.
  • cesoid
    cesoid over 9 years
    Not sure if I'm being daft here, but this seems to explain only how to parse a date, not how to validate it. The accepted answer uses the ArgumentError exception, but that doesn't seem like a good idea, for reasons noted in a comment there.
  • PJP
    PJP over 9 years
    There is a known situation where you can't validate a date. It's where the day and month values are ambiguous, and you have to know the format of the incoming date string. And, that is what the answer is saying. If it looks like 01/01/2014, which is the month and which is the day? In the US month would be first, the rest of the world it'd be second.
  • Tom Lord
    Tom Lord about 9 years
    A brilliant example of when custom regex is a BAD IDEA!
  • vincentp
    vincentp almost 9 years
    This method can raise an exception 'ArgumentError: wrong number of arguments' instead of returning false. For example if your string contains slashes instead of dashes
  • vincentp
    vincentp almost 9 years
    Maybe we can do something like this, for handling bad formatted date : Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.rev‌​erse.map(&:to_i)
  • Ankita.P
    Ankita.P almost 9 years
    is there any valid_date? method for above solution because it gives error when I do Date.valid_date?(Date.parse('2015-07-13')) since parsed object is in the different date format
  • cesoid
    cesoid almost 9 years
    The ambiguity that prevents you from validating the date only does so to the extent that it also prevents you from parsing the date, but you've made a good attempt at parsing it with known information. It would be nice to use some of what you have to try to validate as best as we can. Can you think of how to do that without using exceptions that Date.parse and Date.strptime create?
  • PJP
    PJP almost 9 years
    Date parsing in "mm/dd/yyyy" or "dd/mm/yyyy" format REQUIRES foreknowledge of the date's format. Without that we can't determine which it is unless we have a sampling from one source/user, then parse using both forms and then see which form generated the most exceptions and use the other. This breaks down when parsing dates from multiple sources, especially when they're global or they've been entered by hand. The only accurate way to deal with dates is to not allow hand-entry, to force users to use date-pickers, or only allow ISO date formats that are unambiguous.
  • Mike Bethany
    Mike Bethany over 7 years
    Using error trapping as logic is really, really bad design.
  • baash05
    baash05 about 7 years
    DateTime.parse "123" rescue nil . This returns a real date.. May 3 2017
  • bonafernando
    bonafernando almost 7 years
    Date.strptime(date, '%d/%m/%Y') rescue nil
  • smoyth
    smoyth about 6 years
    Use of inline rescue is discouraged.
  • Lucas Caton
    Lucas Caton about 6 years
    That's exactly what I was looking for. Thank you!
  • oneWorkingHeadphone
    oneWorkingHeadphone about 5 years
    @n13 points out, this one will bite you. It says explicitly in the docs that this is not a validator[1]. Try typing Date.parse('Monterey') into IRB, you will get a valid date. 1: ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/…
  • Qwertie
    Qwertie about 5 years
    Arguably 01/31/2001 is an invalid date and parse is working as intended.
  • saGii
    saGii almost 5 years
    this fails for the cases like "2019-1-1" which is a valid date but the strftime makes it 2019-01-01
  • Nathan Long
    Nathan Long almost 5 years
    @saGii that doesn't fit the format specified (iso8601 - see Date.today().iso8601); %m is zero-padded. If you want something different, see ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
  • csalvato
    csalvato over 3 years
    I like this method the best, personally. In my app, I opted to override String: stackoverflow.com/a/64190303/987115
  • Sunny
    Sunny over 3 years
    Building on top of that, you can simplify this to: Date.valid_date?(*Date._parse(date).values)