Skip to content

Instantly share code, notes, and snippets.

@paulz
Forked from Odie/gist:e26bc299bfc1f020e636
Last active May 15, 2021 21:29
Show Gist options
  • Save paulz/d98492f63337aee1d4e817d080fe8d31 to your computer and use it in GitHub Desktop.
Save paulz/d98492f63337aee1d4e817d080fe8d31 to your computer and use it in GitHub Desktop.
Parse user friendly time duration string in Swift
extension DateComponents {
subscript(unit: String) -> Int {
get {
switch unit {
case "month":
return self.month!
case "day":
return self.day!
case "hour":
return self.hour!
case "minute":
return self.minute!
case "second":
return self.second!
default:
fatalError("unknown unit \(unit)")
}
}
set(newValue) {
switch unit {
case "month":
self.month = newValue
case "day":
self.day = newValue
case "hour":
self.hour = newValue
case "minute":
self.minute = newValue
case "second":
self.second = newValue
default:
fatalError("unknown unit \(unit)")
}
}
}
}
func timeInterval(parsing input: String) -> TimeInterval {
let components = dateComponentFromNaturalLanguageString(input)
let reference = Date(timeIntervalSinceReferenceDate: 0)
let date = Calendar(identifier: .gregorian).date(byAdding: components, to: reference)
return date!.timeIntervalSinceReferenceDate
}
func dateComponentFromNaturalLanguageString(_ input: String) -> DateComponents {
// Define the time units we can understand
let timeUnits = [
// Unit kind => [Unit aliases...]
"month": ["months", "month", "mon"],
"week": ["weeks", "week", "w"],
"day": ["days", "day", "d"],
"hour": ["hours", "hour", "hrs", "hr", "h"],
"minute": ["minute", "minutes", "min", "m"],
"second": ["seconds", "seconds", "secs", "sec", "s"]
]
// Construct a reverse lookup table to go from unit alias => unit kind
var unitKindTable = [String: String]()
for (kind, aliasArray) in timeUnits {
for alias in aliasArray {
unitKindTable[alias] = kind
}
}
// Break input into individual tokens
// This works well for English and other western languages only
let tokens = input.components(separatedBy: " ")
var duration = DateComponents()
// Process each of the tokens found
for (index, element) in tokens.enumerated() {
// Try to find a unit alias in the list of tokens
var unitKind = unitKindTable[element]
if unitKind == nil || index == 0 {
continue
}
// Try to parse the token before the unit as a number
var value = Int(tokens[index - 1])
if value == nil {
continue
}
// At this point, we've determine the unit type and the quantity
// NSDateComponent doesn't deal with weeks the way we want.
// Remap a week as 7 days
if unitKind == "week" {
unitKind = "day"
value! *= 7
}
// Set the component value through the extension
duration[unitKind!] = value!
}
return duration
}
print(timeInterval(parsing: "1 h"))
print(timeInterval(parsing: "1 min"))
print(timeInterval(parsing: "1 day"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment