Skip to content

Instantly share code, notes, and snippets.

@christianselig
Last active July 3, 2019 11:28
Show Gist options
  • Save christianselig/d0d4e3398da07c423db973406e8b9378 to your computer and use it in GitHub Desktop.
Save christianselig/d0d4e3398da07c423db973406e8b9378 to your computer and use it in GitHub Desktop.
import Foundation
extension String {
/// Converts an ISO 8601 formatted `String` into `NSDateComponents`.
///
/// - Note: Does not accept fractional input (e.g.: P3.5Y), must be integers (e.g.: P3Y6M).
/// - SeeAlso: https://en.wikipedia.org/wiki/ISO_8601#Durations
/// - Returns: If valid ISO 8601, an `NSDateComponents` representation, otherwise `nil`.
func ISO8601DateComponents() -> NSDateComponents? {
// Regex adapted from Moment.js https://github.com/moment/moment/blame/develop/src/lib/duration/create.js#L16
let pattern = "^P(?:(\\d*)Y)?(?:(\\d*)M)?(?:(\\d*)W)?(?:(\\d*)D)?(?:T(?:(\\d*)H)?(?:(\\d*)M)?(?:(\\d*)S)?)?$"
let nsstringRepresentation = self as NSString
let regex = try! NSRegularExpression(pattern: pattern, options: [.anchorsMatchLines])
guard let match = regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: nsstringRepresentation.length)) else { return nil }
let dateComponents = NSDateComponents()
if match.rangeAt(1).location != NSNotFound, let years = Int(nsstringRepresentation.substring(with: match.rangeAt(1)) as String) {
dateComponents.year = years
}
if match.rangeAt(2).location != NSNotFound, let month = Int(nsstringRepresentation.substring(with: match.rangeAt(2)) as String) {
dateComponents.month = month
}
if match.rangeAt(3).location != NSNotFound, let week = Int(nsstringRepresentation.substring(with: match.rangeAt(3)) as String) {
dateComponents.weekOfYear = week
}
if match.rangeAt(4).location != NSNotFound, let day = Int(nsstringRepresentation.substring(with: match.rangeAt(4)) as String) {
dateComponents.day = day
}
if match.rangeAt(5).location != NSNotFound, let hour = Int(nsstringRepresentation.substring(with: match.rangeAt(5)) as String) {
dateComponents.hour = hour
}
if match.rangeAt(6).location != NSNotFound, let minute = Int(nsstringRepresentation.substring(with: match.rangeAt(6)) as String) {
dateComponents.minute = minute
}
if match.rangeAt(7).location != NSNotFound, let second = Int(nsstringRepresentation.substring(with: match.rangeAt(7)) as String) {
dateComponents.second = second
}
return dateComponents
}
}
@christianselig
Copy link
Author

Works great for the YouTube API, FYI. It's quite performant.

Note if you're on iOS 10 or higher, Apple added ISO8601DateFormatter, but I needed iOS 9 support.

@ychen820
Copy link

ychen820 commented Apr 5, 2018

Hi, does ISO8601DateFormatter support duration? I was not able to use ISO8601DateFormatter to convert duration

@florentalexdr
Copy link

I tried and it sadly doesn't seem like it supports duration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment