I have the following code to parse an ISO8601 date.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
Problem is sometimes the date is in a format like 2018-01-21T20:11:20.057Z
, and other times it's in a format like 2018-01-21T20:11:20Z
.
So basically part of the time it has the .SSS
millisecond part, and other times it does not.
How can I setup the date formatter to make that part optional?
Edit
I forgot to mention a few details tho in my question I just realized. So I'm using the JSON Codable feature in Swift 4. So it just throws an error if it fails.
So I basically have the following code.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(isoMilisecondDateFormatter())
return try decoder.decode([Person].self, from: _people)
An example JSON object for _people
is the following.
[
{
"name": "Bob",
"born": "2018-01-21T20:11:20.057Z"
},
{
"name": "Matt",
"born": "2018-01-21T20:11:20Z"
}
]
The API I'm working with is pretty inconsistent so I have to deal with multiple types of data.
Two suggestions:
Convert the string with the date format including the milliseconds. If it returns nil
convert it with the other format.
Strip the milliseconds from the string with Regular Expression:
var dateString = "2018-01-21T20:11:20.057Z"
dateString = dateString.replacingOccurrences(of: "\\.\\d+", with: "", options: .regularExpression)
// -> 2018-01-21T20:11:20Z
Or in Swift 5.7+
dateString.replace(/\.\d+/, with: "")
Edit:
To use it with Codable
you have to write a custom initializer, specifying dateDecodingStrategy
does not work
struct Foo: Decodable {
let birthDate : Date
let name : String
private enum CodingKeys : String, CodingKey { case born, name }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var rawDate = try container.decode(String.self, forKey: .born)
rawDate.replace(/\.\d+/, with: "")
birthDate = ISO8601DateFormatter().date(from: rawDate)!
name = try container.decode(String.self, forKey: .name)
}
}
let jsonString = """
[{"name": "Bob", "born": "2018-01-21T20:11:20.057Z"}, {"name": "Matt", "born": "2018-01-21T20:11:20Z"}]
"""
do {
let data = Data(jsonString.utf8)
let result = try JSONDecoder().decode([Foo].self, from: data)
print(result)
} catch {
print("error: ", error)
}