SwiftでISO8601形式の期間の文字列表現を扱います。
ISO8601形式の期間
ISO8601では期間を示す形式の文字列が存在します。
https://ja.wikipedia.org/wiki/ISO_8601#期間
次のような文字列は、2021年10月1日12時から2021年12月31日15時の期間を示す文字列になります。
2021-10-01T12:00:00+09:00/2021-12-31T15:00:00+09:00
これを、Swiftで扱えるようにします。
DateIntervalに変換する
FoundationにDateIntervalというクラスが含まれます。
これはstartからendまでの特定の期間を示すクラスです。
このクラスにはcontains
などの便利なfunctionがあり、特定の日付が期間に含まれているかを簡単に調べることができます。
ですので、期間文字列をDateIntervalに変換することを最終的な形とします。
DateIntervalFormatter
Foundationには同じくDateIntervalFormatterというクラスも含まれます。
日付の処理するFormatterとしてDateFormatterがあるように、このDateIntervalFormatterを使えば簡単に期間文字列の処理ができそうな感じもしますが、残念ながらDateIntervalFormatterには、DateFormatterのようなdate(from:String)
が用意されていません。
ですので、DateIntervalFormatterに対して文字列からDateIntervalに変換する機能を追加します。
DateIntervalFormatterを継承して機能を追加する
DateIntervalFormatterにextensionを追加する方法もありますが、せっかくなのでDateIntervalFormatterを継承した ISO8601DateIntervalFormatter
というクラスを作成しようと思います。
getObjectValue(_:for:errorDescription:)
にも対応してSwiftUIのText等にも使えるようにしてみます。
class ISO8601DateIntervalFormatter: DateIntervalFormatter {
var formatOptions: ISO8601DateFormatter.Options?
func dateInterval(from string: String) -> DateInterval? {
let split = string.split(separator: "/")
guard split.count == 2 else { return nil }
let dateFormatter = ISO8601DateFormatter()
if let formatOptions = formatOptions {
dateFormatter.formatOptions = formatOptions
}
guard let start = dateFormatter.date(from: .init(split[0])),
let end = dateFormatter.date(from: .init(split[1])) else {
return nil
}
return DateInterval(start: start, end: end)
}
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
if let measurement = dateInterval(from: string) {
obj?.pointee = measurement as AnyObject
return true
} else {
return false
}
}
}
ISO8601DateIntervalFormatterを使う
内部的には文字列解析にISO8601DateFormatter
を使っているので、ISO8601DateFormatterの使い方と同様にformatOptions
を設定して使う形になります。
let dateIntervalString = "2004-04-01/2005-07-01"
let formatOptions: ISO8601DateFormatter.Options = [
.withYear,
.withDashSeparatorInDate,
]
let formatter = ISO8601DateIntervalFormatter()
formatter.formatOptions = formatOptions
let dateInterval = formatter.dateInterval(from: dateIntervalString)