DateList "aggressively" interprets strings, such as "p72h":

DateList["p72h"] gives {1972, 1, 1, 0, 0, 0.}

Strings like "p72h" represent not DATE type but DURATION type values ("72 hours") in TimeML's TIMEX3 specification.

In a perfect world this would not matter since Cases or similar pattern matching can be used to select TIMEX tags based on the type element. Unfortunately, coding is not perfect; some fraction of DURATION tags are misclassified as DATES.

I don't see an Option to force DateList to be more literal (is there?). Since all TIMEX3 DATE values normalized as one of the following three forms, where Y, M, D are individual digit characters corresponding to year, month, day:


What's the most succinct StringExpression to match only these cases and reject DURATION strings? I'm guessing it may involve Alternatives and DigitCharacter but I'm not overly familiar with the subtleties of string expressions. Any help appreciated.


3 Answers 3


I did not familiarize myself with the format, but going by your declaration that all valid dates are in one of the forms:


You may simply use:

test = StringMatchQ[#, Repeated[DigitCharacter, {4}] ~~ ___] &;

Select[{"1967-12-07", "2012-04", "1492", "p72h"}, test]
{"1967-12-07", "2012-04", "1492"}
  • 1
    $\begingroup$ "2012-m-dd" matches too? $\endgroup$ Jul 9, 2012 at 15:53
  • 1
    $\begingroup$ @belisarius the question is how restrictive the pattern needs to be. One could also complain about cases like "2012-46-00" etc. Based on the OP's question, and the Accept, I think this one is sufficient. $\endgroup$
    – Mr.Wizard
    Jul 9, 2012 at 16:46

Probably not the most succinct:

f[x_String] := StringMatchQ[x, RegularExpression["\\d{4}(\-\\d{2}){0,2}"]]

f /@ {"p72h", "1234-11-11", "1234", "1234-11", "1234-1"}
->{False, True, True, True, False}


The following is different: it will interpret your DateList[] desired formats (iff DateList is able to understand it) and it will return an empty list if not:

f[x_] := Quiet[DateList[{x, #}] & /@ (Take[{"Year", "Month", "Day"}, #] & /@ Range@3) //. 
               {a___, b__DateList, c___} -> {a, c}]

f /@ {"p72h", "1234-11-11", "1234", "1234-11", "1234-1"}

 -> {{}, {{1234, 11, 11, 0, 0, 0.}}, {{1234, 1, 1, 0, 0, 0.}}, 
         {{1234, 11,  1, 0, 0, 0.}}, {{1234, 1, 1, 0, 0, 0.}}}

Note that "1234-1" is identified as a correct "Year-Month" date.

Edit 2

Re: Regular expressions. Regexes are a powerful (although not complete) way to match grammars. In fact, any Regular Language can be expressed as a Regex.

I recommend learning at least the basics, as they may solve many filtering problems very easy. But also being very careful in abusing them, as they tend to become illegible exponentially (as a function of the time elapsed since you write them).

In this particular case

\\d{4}  matches 4 digits
(       open match block (I will use to match things like "-99")
\-           matches "-"
\\d{2}       matches 2 digits
){0,2}  close match block, and allow it to repeat from 0 to 2 times
  • 1
    $\begingroup$ Thanks. Regular Expression notation to me is like Chinese $\endgroup$ Jul 9, 2012 at 0:54
dta = {"1967-12-07", "2012-04", "1492", "p72h"};
Pick[dta, StringFreeQ[dta, "p" ~~ __, IgnoreCase -> True]]
Pick[dta, StringFreeQ[dta, LetterCharacter ~~ __]]
Pick[dta, StringMatchQ[dta, DigitCharacter ~~ __]]
Pick[dta, StringMatchQ[dta, NumberString ~~ __]]
Pick[dta, StringMatchQ[dta, Alternatives @@ 
   DatePattern /@ {{"Year", "Month", "Day"}, {"Year","Month"}, {"Year"}}]]
(* {"1967-12-07","2012-04","1492"} *)

EDIT: The methods above fail for more general input data such as:

dta2 = {"1967-12-07", "2012-04", "69/01", "1492", "69-March","March/2011", "p72h"}

To deal with the general case, you need to use more specific patterns:

Pick[dta2, StringFreeQ[dta2, LetterCharacter ~~ DigitCharacter .. ~~ LetterCharacter]]


Pick[dta2, StringMatchQ[dta2, Alternatives @@ 
DatePattern /@ 
{{"Year", "Month", "Day"}, 
 {"Year", "MonthName"}, 
 {"MonthName", "Year"}}]]

to get

 {"1967-12-07", "2012-04", "69/01", "1492", "69-March", "March/2011"}

EDIT 2: The second argument of Select in Mr.Wizards's answer can be modified to get the same result:

 test1=StringMatchQ[#, Alternatives @@ DatePattern /@ {{"Year", "Month", "Day"}, {"Year", "Month"}, {"Year"}, {"Year", "MonthName"}, {"MonthName", "Year"}}] &;
 test2=StringFreeQ[#, LetterCharacter ~~ DigitCharacter .. ~~ LetterCharacter] &;

With these criterion functions,

 Select[dta2, test1]


 Select[dta2, test2]

give the same result. However, Pick is usually faster than Select.

  • $\begingroup$ Very nice, thank you. $\endgroup$ Jul 9, 2012 at 2:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.