ISO Date Validation RegEx
I needed a regular expression to correctly parse ISO 8601 format dates and times. The standard includes many alternative representations, but I was particularly concerned about the subset of ISO 8601 formats allowed in XML.
Edit 26 May 2009: If you want full ISO compliance, check out the expression Cameron Brooks lists in his comment below.
Paul Ward had posted a date parsing expression on the RegExLib site. I extended it a bit to handle times and time zones. I’m pretty sure they work, though it can be hard to tell sometimes. (See Jeff Atwood’s Now You Have Two Problems.)
ISO date
^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$
ISO time
^([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3})?$
ISO offset
^([zZ]|([\+-])([01]\d|2[0-3])\D?([0-5]\d)?)?$
ISO date and time
^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])(\D?([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3})?)?$
ISO date, time, and offset (the works)
^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])(\D?([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3})?([zZ]|([\+-])([01]\d|2[0-3])\D?([0-5]\d)?)?)?$
If you find it useful — or find a bug — let me know.
Edit 21 Jan 2008: Stan James sent us some enhancements to the validation routines. Here’s what he had to say:
I needed a RegExp that could detect ISO dates with varying precision. (e.g. “1945″, “1945-12″, “1945-12-01″, “1945-12-01T12:15″ etc..)
For future readers, here’s what I came up with:
Date only
^[0-9][0-9][0-9][0-9](-[0-1][0-9](-[0-3][0-9])?)?$Date and Time
^[0-9][0-9][0-9][0-9](-[0-1][0-9](-[0-3][0-9](T[0-9][0-9](:[0-9][0-9](:[0-9][0-9])?)?)?)?)?$
Thanks, Stan!
[...] – bookmarked by 2 members originally found by VissyLinch09 on 2008-11-13 ISO Date Validation RegEx http://underground.infovark.com/2008/07/22/iso-date-validation-regex/ – bookmarked by 5 members [...]
I was in the same situation as you (needing ISO 8601 validation). Your “works” regex is great, but needed a bit more fine tuning to be truly ISO 8601 compliant. Here are some strings it matches on that should not be matched on:
2009-05-19T14a39r
2009-05-19T14:3924
2009-0519
2009-05-1914:39
2009-05-19r14:39
2009-05-19 14a39a22
200912-01
And here are some valid ISO 8601 dates that it should match on but doesn’t:
2009
2009-12T12:34
2009123
2009-05
2009-123
2009-222
2009-001
2009-W01-1
2009-W51-1
2009-W511
2009W511
2009-05-19 14
2009-W21-2
2009-139
2007-04-05T24:00
(Note that 24:00 is a valid representation of midnight according to ISO 8601, so I changed that.) Obviously, this doesn’t include the duration or time interval aspects of ISO 8601 (I left those out because I didn’t need them).
This is the modified regex I came up with:
^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])-?[1-7]|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s](([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)?(\15([0-5]\d))?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$
I’d love to hear your input.
Quite right, Cameron. The false positives you identified are definite bugs in the regular expression I’d posted.
And the following three false negatives you provided are also bugs:
2009
2009-05
2007-04-05T24:00
The other false negatives you listed are indeed valid ISO 8601 date strings. But the W3C recommendation excludes the day-of-year and week-of-year notation from the XML standard.
http://www.w3.org/TR/NOTE-datetime
So your expression is much better for strict compliance with ISO 8601. For compliance with XML, we’ll need something a little different. But either way, it’s time to update this page!
Cameron Brooks’ solution doesn’t seem to handle fractional sections, FWIW. Other than that it seems to work pretty well.
Hey guys. You’re quite correct that my regex didn’t account for fractional time elements. At the time I wrote it I didn’t require it for my application, and I didn’t realize how many people used them for super-precise ISO times. I’ve updated my regex to account for this.
Here are some strings that should match as valid:
2010-02-18T16:23:48.5
2010-02-18T16:23:48,444
2010-02-18T16:23:48,3-06:00
2010-02-18T16:23.4
2010-02-18T16:23,25
2010-02-18T16:23.33+0600
2010-02-18T16.23334444
2010-02-18T16,2283
2009-05-19 143922.500
2009-05-19 1439,55
And here are some strings that should not match as valid:
2009-05-19 146922.500
2010-02-18T16.5:23.35:48
2010-02-18T16:23.35:48
2010-02-18T16:23.35:48.45
2009-05-19 14.5.44
2010-02-18T16:23.33.600
2010-02-18T16,25:23:48,444
Here’s the updated regex that I was able to come up with:
^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$
It still doesn’t account for the duration or time interval part of the ISO 8601 spec, so I guess that’ll have to come at a later date.
Lemme know if you find any bugs.