1. ホーム
  2. 日付

[解決済み]SwiftでUTCタイムゾーン(ISO 8601、RFC 3339)の端数秒でフォーマットされた日付のタイムスタンプをパース/作成するにはどうすればよいですか?

2022-04-06 06:26:01

質問

日付と時刻のスタンプを生成する方法。 ISO 8601 RFC 3339 ?

ゴールは次のような文字列です。

"2015-01-01T00:00:00.000Z"

フォーマット

  • 年、月、日、quot;XXXX-XX-XX"のように。
  • セパレータとしての文字quot;T"。
  • 時、分、秒、ミリ秒、quot;XX:XX:XX.XXX"のように。
  • UTC、GMT、Zulu時間など、ゼロオフセットを表すゾーン指定子として、文字quot;Z"を使用します。

ベストケース

  • シンプルで短く、わかりやすいSwiftのソースコード。
  • フレームワーク、サブプロジェクト、cocoapod、Cコードなどを追加で使用する必要はありません。

StackOverflow、Google、Appleなどを検索しましたが、これに対するSwiftの回答は見つかりませんでした。

最も有望と思われるクラスは NSDate , NSDateFormatter , NSTimeZone .

関連するQ&A iOSでISO 8601の日付を取得するにはどうすればよいですか?

これまでに思いついたベストはこちらです。

var now = NSDate()
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
println(formatter.stringFromDate(now))

解決方法は?

Swift 4 - iOS 11.2.1 またはそれ以降

extension ISO8601DateFormatter {
    convenience init(_ formatOptions: Options) {
        self.init()
        self.formatOptions = formatOptions
    }
}


extension Formatter {
    static let iso8601withFractionalSeconds = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
}


extension Date {
    var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
}


extension String {
    var iso8601withFractionalSeconds: Date? { return Formatter.iso8601withFractionalSeconds.date(from: self) }
}


使用方法

Date().description(with: .current)  //  Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
let dateString = Date().iso8601withFractionalSeconds   //  "2019-02-06T00:35:01.746Z"

if let date = dateString.iso8601withFractionalSeconds {
    date.description(with: .current) // "Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
    print(date.iso8601withFractionalSeconds)           //  "2019-02-06T00:35:01.746Z\n"
}


iOS 9 - Swift 3 以降

extension Formatter {
    static let iso8601withFractionalSeconds: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
        return formatter
    }()
}


コード化可能なプロトコル

Codableで作業する際に、このフォーマットのエンコードとデコードが必要な場合 プロトコルでは、独自の日付エンコード/デコードのストラテジーを作成することができます。

extension JSONDecoder.DateDecodingStrategy {
    static let iso8601withFractionalSeconds = custom {
        let container = try $0.singleValueContainer()
        let string = try container.decode(String.self)
        guard let date = Formatter.iso8601withFractionalSeconds.date(from: string) else {
            throw DecodingError.dataCorruptedError(in: container,
                  debugDescription: "Invalid date: " + string)
        }
        return date
    }
}

とエンコード戦略

extension JSONEncoder.DateEncodingStrategy {
    static let iso8601withFractionalSeconds = custom {
        var container = $1.singleValueContainer()
        try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))
    }
}


プレイグラウンドテスト

let dates = [Date()]   // ["Feb 8, 2019 at 9:48 PM"]

エンコーディング

let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
print(String(data: data, encoding: .utf8)!)

デコード

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data)  // ["Feb 8, 2019 at 9:48 PM"]