Skip to content

Instantly share code, notes, and snippets.

@wata
Last active March 28, 2023 14:46
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wata/9cd2d4779a55f5663fed0e07b05af1e6 to your computer and use it in GitHub Desktop.
Save wata/9cd2d4779a55f5663fed0e07b05af1e6 to your computer and use it in GitHub Desktop.
A simple ISO string parsing and converting library for Swift
public struct ISOString {
/// Parse ISO 6709 string.
/// e.g. "+34.0595-118.4460+091.541/"
/// SeeAlso: [ISO 6709](https://en.wikipedia.org/wiki/ISO_6709)
public static func parse(iso6709 text: String?) -> CLLocation? {
guard
let results = text?.capture(pattern: "([+-][0-9.]+)([+-][0-9.]+)"),
let latitude = results[safe: 1] as NSString?,
let longitude = results[safe: 2] as NSString?
else { return nil }
return CLLocation(latitude: latitude.doubleValue, longitude: longitude.doubleValue)
}
/// Convert `CLLocation` to ISO 6709 string.
/// e.g. "+34.0595-118.4460+091.541/"
/// SeeAlso: [ISO 6709](https://en.wikipedia.org/wiki/ISO_6709)
public static func iso6709(from location: CLLocation) -> String {
String(
format: "%+09.5f%+010.5f%+.0fCRSWGS_84/",
location.coordinate.latitude,
location.coordinate.longitude,
location.altitude
)
}
/// Parse ISO 8601 string.
/// e.g. "2019-07-19T13:40:59+0900"
/// SeeAlso: [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
public static func parse(iso8601 text: String?) -> Date? {
guard
let text = text,
let date = ISO8601DateFormatter().date(from: text)
else { return nil }
return Date(timeInterval: 0, since: date)
}
/// Convert `Date` to ISO 8601 string.
/// e.g. "2019-07-19T13:40:59+0900"
/// SeeAlso: [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
public static func iso8601(from date: Date) -> String {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone.current
formatter.formatOptions.remove(.withColonSeparatorInTimeZone) // iOS camera output format
return formatter.string(from: date)
}
}
private extension String {
func capture(pattern: String) -> [String] {
guard
let regex = try? NSRegularExpression(pattern: pattern),
let result = regex.firstMatch(in: self, range: NSRange(location: 0, length: count))
else { return [] }
return (0..<result.numberOfRanges).map { String(self[Range(result.range(at: $0), in: self)!]) }
}
}
private extension Collection {
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
public extension CLLocation {
convenience init?(iso6709 text: String?) {
guard let location = ISOString.parse(iso6709: text) else { return nil }
self.init(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
}
var iso6709: String {
ISOString.iso6709(from: self)
}
}
public extension Date {
init?(iso8601 text: String?) {
guard let date = ISOString.parse(iso8601: text) else { return nil }
self = date
}
var iso8601: String {
ISOString.iso8601(from: self)
}
}
@wata
Copy link
Author

wata commented Apr 8, 2020

Usage

// Parse ISO 6709 string
let locationItem = AVMetadataItem.metadataItems(from: asset.metadata, filteredByIdentifier: .quickTimeMetadataLocationISO6709).first
let location = ISOString.parse(iso6709: locationItem?.stringValue)
print(String(describing: location)) // CLLocation

// Convert Date to ISO 8601 string
let now = Date()
let iso8601 = ISOString.iso8601(from: now)
let creationDateItem = AVMutableMetadataItem(identifier: .quickTimeMetadataCreationDate, value: iso8601)
print(String(describing: creationDateItem))

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