Take the 2-minute tour ×

This question already has an answer here:

April 9, 2012 can be written in any of these ways:

4 9 12
4 9 2012
4 09 2012
(I think you get the point)

For those of you that don't understand, the rules are:

1. Dates may or may not have ` `, `-` or `/` between them
2. The year can be written as 2 digits (assumed to be dates in the range of [2000, 2099] inclusive) or 4 digits
3. One digit month/days may or may not have leading zeroes.

How would you go about problem solving this to format the dates into 04/09/12?

I know the dates can be ambiguous, i.e., 12112 can be 12/1/12 or 1/21/12, but assume the smallest month possible.

share|improve this question

marked as duplicate by Sinan Ünür, gpojd, ikegami, AD7six, Jack Maney Mar 13 '13 at 21:10


I presume your question is about how to parse dates that could be in any of the formats you've given? –  lxop Mar 13 '13 at 20:17
No, don't do this without modules. –  mob Mar 13 '13 at 20:25
Would "11113" be 11-1-2013 or 1-11-2013? –  gpojd Mar 13 '13 at 20:28
What date is 12212? –  Sinan Ünür Mar 13 '13 at 20:28
Then you have several questions to consider. 11111111 is unambiguous, and so 8s 111, but what do you want to do with string of between '1' x 4 and '1' x 7? –  Borodin Mar 13 '13 at 21:16

2 Answers 2

up vote 2 down vote accepted

This actually is something that regexes are good at; making an assumption, moving forward with it, then backtracking if necessary to get a successful match.

    ( 1[0-2] | 0?[1-9] )
    [-/ ]?
    ( 3[01] | [12][0-9] | 0?[1-9] )
    [-/ ]?
    ( (?: [0-9]{2} ){1,2} )
    sprintf '%02u/%02u/%04u', $1, $2, ( length $3 == 4 ? $3 : 2000+$3 )

The range checks present, while not determined by the value of the month, should be sufficient to pick a good date from the ambiguous cases (where there is a good date).

Note that it is important to try two digit month and days first; otherwise 111111 becomes 1-1-1111, not the presumably intended 11-11-11. But this means 11111 will prefer to be 11-1-11, not 1-11-11.

If a valid day of month check is needed, it should be performed after reformatting.


s{}{} is a substitution using curly braces instead of / to delimit the parts of the regex to avoid having to escape the /, and also because using paired delimiters allows opening and closing both the pattern and replacement parts, which looks nice to me.

\A matches the start of the string being matched; \z matches the end. ^ and $ are often used for this, but can have slightly different meanings in some cases; I prefer these since they always only mean one thing.

The x flag on the end says this is an extended regex that can have extra whitespace or comments that are ignored, so that it is more readable. (Whitespace inside a character class isn't ignored.) The e flag says the replacement part isn't a string, it is code to execute.

'%02u/%02u/%02u' is a printf format, used for taking values and formatting them in a particular way; see http://perldoc.perl.org/functions/sprintf.html.

share|improve this answer
I'm still quite new to Perl, so could you please comment on what your code does? I don't know what the s bracket is doing or the \A or \z or the xe or the %02u/%02u/%02u. –  dtgee Mar 13 '13 at 21:01
Added explanation; any other questions? –  ysth Mar 13 '13 at 21:10
Great solution! However, I'm guessing I have to check if the year is, for example, 04 and append 20 in the beginning then reconcatenate it with the string? –  dtgee Mar 13 '13 at 23:25
You originally said "format the dates into 04/09/12", but ok... –  ysth Mar 14 '13 at 15:21

Install Date::Calc

On ubuntu libdate-calc-perl

This should be able to read in all those dates ( except 4912, 4 9 2012, 4 09 2012 ) and then output them in a common format

share|improve this answer
