web-dev-qa-db-ja.com

Swift4を使用したiso8601日付jsonデコード

したがって、jsonには「2016-06-07T17:20:00.000 + 02:00」のようなiso8601日付があります。

Swift4を使用してこれらのiso8601日付を解析する方法はありますか?明らかなものがないのですか?

以下を試しましたが、jsonShipAからのdateString "2016-06-07T17:20:00Z"のみが解析可能です...

import Foundation

struct Spaceship : Codable {
    var name: String
    var createdAt: Date
}

let jsonShipA = """
{
    "name": "Skyhopper",
    "createdAt": "2016-06-07T17:20:00Z"
}
"""

let jsonShipB = """
{
    "name": "Skyhopper",
    "createdAt": "2016-06-07T17:20:00.000+02:00"
}
"""

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

let dataA = jsonShipA.data(using: .utf8)!
if let decodedShip = try? decoder.decode(Spaceship.self, from: dataA) {
    print("jsonShipA date = \(decodedShip.createdAt)")
} else {
    print("Failed to decode iso8601 date format from jsonShipA")
}

let dataB = jsonShipB.data(using: .utf8)!
if let decodedShip = try? decoder.decode(Spaceship.self, from: dataB) {
    print("jsonShipA date = \(decodedShip.createdAt)")
} else {
    print("Failed to decode iso8601 date format from jsonShipB")
}

遊び場の出力は次のとおりです。

jsonShipA date = 2016-06-07 17:20:00 +0000
Failed to decode iso8601 date format from jsonShipB

スローされるエラーは、「期待される日付文字列はISO8601形式です」です。しかし、私の知る限りでは、「2016-06-07T17:20:00.000 + 02:00」という日付はISO8601の有効な日付です

13
Tycho Pandelaar

次のように使用できます:

enum DateError: String, Error {
    case invalidDate
}

let decoder = JSONDecoder() 

let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
    let container = try decoder.singleValueContainer()
    let dateStr = try container.decode(String.self)

    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
    if let date = formatter.date(from: dateStr) {
        return date
    }
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
    if let date = formatter.date(from: dateStr) {
        return date
    }
    throw DateError.invalidDate
})
30
Vini App

TL; DRバージョン:ISO8601DateFormatter ここで説明 のwithInternetDateTime形式のみを解析します。これは、文字列がミリ秒であってはならないことを意味します。

より詳しい情報:

787行目の the Swift source を見ると、コメントには次のように書かれています。

/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).

[〜#〜] rfc [〜#〜] を見ると、セクション5.8に(確かにトリッキーな)例がいくつかあります。

1985-04-12T23:20:50.52Z
1996-12-19T16:39:57-08:00
1996-12-20T00:39:57Z
1990-12-31T23:59:60Z
1990-12-31T15:59:60-08:00
1937-01-01T12:00:27.87+00:20

2番目と3番目の例だけが実際にSwiftによってデコードされ、残りは失敗します。コメントが間違っているか、実装が完全ではないようです。実際の実装に関しては、それはSwiftソースの外にあり、単にFoundationのISO8601DateFormatterクラスを使用しているようです。

Swift unittest も非常に制限されています。180行目を参照してください。これは、単一の日付をエンコードしてからデコードし直すだけです。つまり、テストされる唯一のことは、ISO8601DateFormatterがデフォルトで出力するフォーマットであり、オプション.withInternetDateTimeここで説明 にハードコードされています。

24
Bart van Kuik