Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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:

"YYYY-MM-DD" or "YYYY-MM" or "YYYY"

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.

share|improve this question
add comment

3 Answers

up vote 3 down vote accepted

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

"YYYY-MM-DD" or "YYYY-MM" or "YYYY"

You may simply use:

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

Select[{"1967-12-07", "2012-04", "1492", "p72h"}, test]
{"1967-12-07", "2012-04", "1492"}
share|improve this answer
    
"2012-m-dd" matches too? –  belisarius Jul 9 '12 at 15:53
    
@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. –  Mr.Wizard Jul 9 '12 at 16:46
add comment

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}
*)

Edit

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:

ClearAll@f;
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
share|improve this answer
1  
Thanks. Regular Expression notation to me is like Chinese –  alancalvitti Jul 9 '12 at 0:54
add comment
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]]

or

Pick[dta2, StringMatchQ[dta2, Alternatives @@ 
DatePattern /@ 
{{"Year", "Month", "Day"}, 
 {"Year","Month"}, 
 {"Year"}, 
 {"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]

and

 Select[dta2, test2]

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

share|improve this answer
    
Very nice, thank you. –  alancalvitti Jul 9 '12 at 2:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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