So, I've got iso8601 dates in my json which look like "2016-06-07T17:20:00.000+02:00"
Is there a way to parse these iso8601 dates using swift4? Am I missing something obvious?
I tried the following, but only the dateString "2016-06-07T17:20:00Z" from jsonShipA is parsable....
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") }
The output of the playground is:
jsonShipA date = 2016-06-07 17:20:00 +0000 Failed to decode iso8601 date format from jsonShipB
The error being thrown is "Expected date string to be ISO8601-formatted." But to my knowledge, the date "2016-06-07T17:20:00.000+02:00" is a valid ISO8601 date
You can use like this :
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 })
TL;DR version: it only parses the withInternetDateTime format of the ISO8601DateFormatter described here. This means that your string should not have milliseconds.
More info:
Looking at the Swift source on line 787, the comment says:
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
Looking at that RFC, it gives a couple of (admittedly tricky) examples in section 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
Only the second and the third example are actually decoded by Swift, the rest fails. It seems to me that either the comment is incorrect, or the implementation is not complete. As for the real implementation, that's outside the Swift source, it simply seems to use the ISO8601DateFormatter class in Foundation.
The Swift unittest is also very limited, see line 180. It simply encodes a single date, and then decodes it back. So in other words, the only thing that's tested, is the format that the ISO8601DateFormatter outputs by default, which is hardcoded to the option .withInternetDateTime
, described here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With