Open swift-ci opened 7 years ago
cc @itaiferber
This is a known issue — ISO8601DateFormatter
does not currently support sub-second precision, but this will be added in a future release of Swift.
Comment by Maurice Arikoglu (JIRA)
@itaiferber ISO8601DateFormatter
supports sub-second precision in the meantime, this inconsistency still exists though. Has the time come to tackle this maybe?
arim (JIRA User) Unfortunately, we haven't had the bandwidth to dedicate to expanding the API to support this — because ISO8601DateFormatter
is strict about the values it supports, we cannot change the behavior of .iso8601
to add subsecond parsing because that will break existing use cases of strings which don't have fractional seconds.
Instead, we'd like to add an .iso8601WithOptions(ISO8601DateFormatter.Options)
option, but this requires going through API review first. It's still an open task that we'd like to resolve.
Comment by Basem Emara (JIRA)
I really like the proposed solution of doing this:
JSONDecoder().dateDecodingStrategy = .iso8601WithOptions(ISO8601DateFormatter.Options)
To me this is an oversight with the JSONDecoder because I'd imagine the majority of ISO8601 use cases is with JSON. Unless we use a custom strategy and get messy with `.custom`, we can't use the `ISO8601DateFormatter` and `JSONDecoder` together which seem like a natural combination.
I will happily submit the PR for this if someone from Swift core can make a decision on how it should be fixed. Need it for my current work.
The issue is not a decision about how it should work, but about the bandwidth to get things through API review internally. In the meantime, you can work around this by creating a date formatter with the expected format (including sub-second precision) and apply it using the JSONDecoder.DateDecodingStrategy.formatted
strategy.
Comment by Maurice Arikoglu (JIRA)
@Itai Ferber I have opened another bug with the ISO date formatter. Maybe this could be fixed with the same proposed solution: #SR-9266
Comment by Maurice Arikoglu (JIRA)
@itaiferber
Comment by Tanner Stirrat (JIRA)
We just ran into this problem today. Any updates here? We're currently hacking around it on our backend, but that's a less than ideal solution, to put it lightly.
Comment by Oswaldo Rubio (JIRA)
Same here, I don´t like to use the custom decoding strategy and having to iterate each iso8601 variant.
extension JSONDecoder.DateDecodingStrategy {
static let iso8601WithSeconds = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
guard let date = formatter.date(from: string) else {
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Invalid date in iso8601withSeconds: " + string)
}
return date
}
}
gives a .iso8601WithSeconds
variant that solves this issue. (Swift 5.5)
Use like
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601WithSeconds
Additional Detail from JIRA
| | | |------------------|-----------------| |Votes | 4 | |Component/s | Foundation | |Labels | Bug | |Assignee | @itaiferber | |Priority | Medium | md5: 6cc2a18bb37f3c5b8856aeac7a543bbaIssue Description:
The ISO8601 DateFormatter which is apparently also used for the Codeable dateDecodingStrategy/dateEncodingStrategy iso8601 does not parse/validate all ISO8601 dates as expected.
It especially does not accept iso8601 dates with sub seconds.
Expected:
2017-09-03T10:08:00Z => valid iso8601 date
2017-09-03T10:08:00.000Z => valid iso8601 date
Actual:
2017-09-03T10:08:00Z => valid iso8601 date
2017-09-03T10:08:00.000Z => invalid iso8601 date
Minimal Sample Code:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
try decoder.decode([Date].self, from: "["2017-09-03T10:08:00Z"]".data(using: .utf8)!) // works
try decoder.decode([Date].self, from: "["2017-09-03T10:08:00.000Z"]".data(using: .utf8)!) // throws
Version Info:
Used Xcode 9 Beta 6 in iOS 11 Simulator on macOS Sierra 10.12.6.
Note:
I discovered this while consuming an REST API that is implemented in JavaScript (node.js). When creating a Date object in JavaScript and transforming it to an iso string using "toISOString" (e.g. "(new Date()).toISOString()") it creates a date string including sub-seconds (e.g. "2017-09-03T10:08:00.000Z").
This means that one cannot consume a "standard" JavaScript API with Swift's builtin Codable date parser and need to create a custom DateFormatter (e.g. with the pattern: "yyyy-MM-dd'T'HH:mm:ss'.'SSSZZZZZ").
This behavior is really inconvenient and a great bummer for Swift's Codeable support.
I'm not an expert on date formats but I expected this to work and it took me some time to dig through the differences why this did not work. Would be great if we could save this time waste from future developers.