-
-
Save paulz/d98492f63337aee1d4e817d080fe8d31 to your computer and use it in GitHub Desktop.
Parse user friendly time duration string in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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