tags:

views:

2939

answers:

11

I'm trying to write a regular expression that validates a date. The regex needs to match the following

  • M/D/YYYY
  • MM/DD/YYYY
  • Single digit months can start with a leading zero (eg: 03/12/2008)
  • Single digit days can start with a leading zero (eg: 3/02/2008)
  • CANNOT include February 30 or February 31 (eg: 2/31/2008)

So far I have ^(([1-9]|1[012])-/.-/.\d\d)|((1[012]|0[1-9])(3[01]|2\d|1\d|0[1-9])(19|20)\d\d)|((1[012]|0[1-9])-/.-/.\d\d)$

This matches properly EXCEPT it still includes 2/30/2008 & 2/31/2008.

Does anyone have a better suggestion?

Edit: I found the answer on RegExLib

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

It matches all valid months that follow the MM/DD/YYYY format.

Thanks everyone for the help.

+4  A: 

Sounds like you're overextending regex for this purpose. What I would do is use a regex to match a few date formats and then use a separate function to validate the values of the date fields so extracted.

Wedge
A: 

Regex was not meant to validate number ranges(this number must be from 1 to 5 when the number preceding it happens to be a 2 and the number preceding that happens to be below 6). Just look for the pattern of placement of numbers in regex. If you need to validate is qualities of a date, put it in a date object js/c#/vb, and interogate the numbers there.

DevelopingChris
A: 

Which language do you use? I suggest to not use regex for such task.

aku
+22  A: 

This is not an appropriate use of regular expressions. You'd be better off using

[0-9]{2}/[0-9]{2}/[0-9]{4}

and then checking ranges in a higher-level language.

Chris Conway
+2  A: 

I'd love to see a version of that regex that took leap years into account. =P

Blorgbeard
+2  A: 

I know this does not answer your question, but why don't you use a date handling routine to check if it's a valid date? Even if you modify the regexp with a negative lookahead assertion like (?!31/0?2) (ie, do not match 31/2 or 31/02) you'll still have the problem of accepting 29 02 on non leap years and about a single separator date format.

The problem is not easy if you want to really validate a date, check this forum thread.

For an example or a better way, in C#, check this link

If you are using another platform/language, let us know

Vinko Vrsalovic
A: 

If you're going to insist on doing this with a regular expression, I'd recommend something like:

( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
  0?2 / (0?1| <...> |28|29) ) 
/ (19|20)[0-9]{2}

This might make it possible to read and understand.

Chris Conway
+2  A: 

Perl expanded version

Note use of /x modifier.

/^(
      (
        ( # 31 day months
            (0[13578])
          | ([13578])
          | (1[02])
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (3[01])
        )
      )
    | (
        ( # 30 day months
            (0[469])
          | ([469])
          | (11)
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (30)
        )
      )
    | ( # 29 day month (Feb)
        (2|02)
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
        )
      )
    )
    [\/]
    # year
    \d{4}$

  | ^\d{4}$ # year only
/x

Original

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$
Brad Gilbert
+5  A: 

Maintainable Perl 5.10 version

/
  (?:
      (?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
    | (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
    | (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
  )
  [\/]
  (?<year> \d{4})

  (?(DEFINE)
    (?<day_29> [0-2]?\d )
    (?<day_30> [0-2]?\d | 30 )
    (?<day_31> [0-2]?\d | 3[01] )

    (?<mon_29> 0?2 )
    (?<mon_30> 0?[469]   | (11) )
    (?<mon_31> 0?[13578] | 1[02] )
  )
/x

You can retrieve the elements by name in this version.

say "Month=$+{month} Day=$+{day} Year=$+{year}";
Brad Gilbert
A: 

Perl 6 version

rx{
  ^
  $month := (\d{1,2})
  { $^month <= 12 or fail }
  <[\/]>
  $day := (\d{1,2})
  {
    given( +$^month ){
      when any(qw'1 3 5 7 8 10 12') {
        $day <= 31 or fail
      }
      when any(qw'4 6 9 11') {
        $day <= 30 or fail
      }
      when 2{
        $day <= 29 or fail
      }
      default { fail }
    }
  }
  <[\/]>
  $year := (\d{4})
  $
}

Note this was quickly made and probably has a few bugs.

Brad Gilbert
A: 

A slightly different approach that may or may not be useful for you.

I'm in php.

The project this relates to will never have a date prior to the 1st of January 2008. So, I take the 'date' inputed and use strtotime(). If the answer is >= 1199167200 then I have a date that is useful to me. If something that doesn't look like a date is entered -1 is returned. If null is entered it does return today's date number so you do need a check for a non-null entry first.

Works for my situation, perhaps yours too?

Humpton