Skip to content

Instantly share code, notes, and snippets.

@rauhs
Last active December 15, 2017 14:52
Show Gist options
  • Save rauhs/bd3aa598f8d818bcc076 to your computer and use it in GitHub Desktop.
Save rauhs/bd3aa598f8d818bcc076 to your computer and use it in GitHub Desktop.
Clojure named caputred Regex with application for duration parsing "4 weeks 8d 20h 2mins"
(defn re-named-groups
"Returns the groups from the most recent match/find. If there are no
nested groups, returns a string of the entire match. If there are
nested groups, returns a vector of the groups, the first element
being the entire match."
[^java.util.regex.Matcher m kw]
(let [gc (. m (groupCount))]
(if (zero? gc)
(. m (group))
(into {} (map (fn[x] [x (. m (group (name x)))]) kw)))))
(defn re-named-matches
"Returns the match, if any, of string to pattern, using
java.util.regex.Matcher.matches(). Uses re-groups to return the
groups."
[^java.util.regex.Pattern re s kw]
(let [m (re-matcher re s)]
(when (. m (matches))
(re-named-groups m kw))))
(def ^:const PERIOD-RE
#"(?ix) # Allow comments & case insensitive
############## WEEKS #############
(
\s* # Opt whitespace
(?<weeks>\d*.?\d+) # digit
\s*
(w) # w for week
(?:eeks?|) # but optional non-caputring group which might be empty
\s*(?:and|,|) # optional `,' and/or `and'
)?
############## DAYS #############
(
\s*
(?<days>\d*.?\d+)
\s*
(d)
(?:ays?|)
\s*(?:and|,|)
)?
############## HOURS #############
(
\s*
(?<hours>\d*.?\d+)
\s*
(h)
(?:ours?|)
\s*(?:and|,|)
)?
############## MINUTES #############
(
\s*
(?<mins>\d*.?\d+)
\s*
(m)
(?:ins?|inutes?|)
\s*(?:and|,|)\s*
)?
############## Seconds #############
(
\s*
(?<seconds>\d*.?\d+)
\s*
(s)
(?:ecs?|econds?|)
\s*
)?
\s* # Allow some final whitespace
")
(defn parse-period
"Parses a period string to a map which gives the :weeks :days :hours and :mins.
Return nil if it failed to parse.
(parse-duration \"5w 4 days 8mins\")"
[s]
(map-vals
#(if % (Double/parseDouble %) 0)
(re-named-matches PERIOD-RE s #{:days :hours :mins :weeks :seconds})))
(defn period->secs
"Converts the period map from parse-period to seconds."
[s]
(if-let [{:keys [weeks days hours mins seconds]} (parse-period s)]
(+
seconds
(* mins 60.)
(* hours 60. 60.)
(* days 60. 60. 24.)
(* weeks 60. 60. 24. 7.))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment