Identifying combinations of dates in text using regex

For the upcoming app I’m working on I need a way to easily detect dates the user may have typed at the end of a line. It was time to re-visit old faithful regex

Ewrsit7pbw671

After lots of readings and tests I ended up settling on this beauty: 

     “.*[0-9]{2}/[0-9]{2}”

Here’s what it does:

The first section means take any character from the beginning of a line:

     .  Any character except new line

     * Zero or more charecters

provided it ends in the second part…

     [0-9]{2} Two numbers between 0 to 9

     /  This specific seperator

     [0-9]{2} Two numbers between 0 to 9

So in essence, it will take any line that ends in xx/xx where xx is any combination of two digits. That’s it, clear and simple!

***

Now, all we have to do is expand the number of cases to account for the user typing only one digit for the month or day.  

Here’s a swift array containing each case: [“.*[0-9]{2}/[0-9]{2}”,”.*[0-9]{1}/[0-9]{2}”,”.*[0-9]{2}/[0-9]{1}”,”.*[0-9]{1}/[0-9]{1}”]

One thing I’d point out is that the order of the regex pattern matters, it should go from most complex to most simple as regex will stop once it finds a suitable match. 

Here’s an example of all of it working together (in this case I’m also adding a year pattern):

func dateMatches(text: String) -> [String] {

    let regexPatternMonth = [“.*[0-9]{2}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{2}”,“.*[0-9]{2}/[0-9]{1}”,“.*[0-9]{1}/[0-9]{1}”]

    let regexPatternYear = [“.*[0-9]{2}/[0-9]{2}/[0-9]{4}”,“.*[0-9]{1}/[0-9]{2}/[0-9]{4}”,“.*[0-9]{2}/[0-9]{1}/[0-9]{4}”,“.*[0-9]{1}/[0-9]{1}/[0-9]{4}”,“.*[0-9]{2}/[0-9]{2}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{2}/[0-9]{2}”,“.*[0-9]{2}/[0-9]{1}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{1}/[0-9]{2}”]

    

    

    let regexPatternsToMatchArray = regexPatternMonth + regexPatternYear

    do {

        let regex = try NSRegularExpression(pattern: “(\(regexPatternsToMatchArray.joined(separator:“|”)))”)

        let results = regex.matches(in: text,

                                range: NSRange(text.startIndex…, in: text))

        let finalResult = results.map {

            String(text[Range($0.range, in: text)!])

        }

        return finalResult

    } catch let error {

        print(“invalid regex: \(error.localizedDescription))

        return []

       }

}

That’s it, you can call this function and it will return the array of matching sub-strings.

From there you can process convert them into dates (make sure to account for localization as some countries use day/month and others month/day). 

Hope this helps, if you’re new to regex here are some useful references: regex cheatsheet, validatormatching valid dates and ios regex.

Comments, questions I’m at @MarcMasVi on Twitter

Marc