extension Formatter {
static let iso8601withFractionalSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return formatter
}()
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter
}()
static let ddMMyyyy: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "dd-MM-yyyy"
return formatter
}()
}
extension JSONDecoder.DateDecodingStrategy {
static let multiple = custom {
let container = try $0.singleValueContainer()
do {
return try Date(timeIntervalSince1970: container.decode(Double.self))
} catch DecodingError.typeMismatch {
let string = try container.decode(String.self)
if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ??
Formatter.iso8601.date(from: string) ??
Formatter.ddMMyyyy.date(from: string) {
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
}
}
}
Playground testing:
struct Root: Codable {
let articles: Articles
}
struct Articles: Codable {
let authors: Authors
let relevantUntil: Date
let publicationDate: Date
let lastComment: Date
}
struct Authors: Codable {
let birthday: Date
}
let json = """
{"articles": {
"authors": {"birthday": "01-01-1970"},
"relevant_until": "2020-11-19 01:23:45",
"publication_date": 1605705003.0019,
"last_comment": "2020-11-19 01:23:45.678"}
}
"""
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .multiple
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let root = try decoder.decode(Root.self, from: .init(json.utf8))
print(root.articles) // Articles(authors: __lldb_expr_107.Authors(birthday: 1970-01-01 03:00:00 +0000), relevantUntil: 2020-11-19 04:23:45 +0000, publicationDate: 2020-11-18 13:10:03 +0000, lastComment: 2020-11-19 04:23:45 +0000)
} catch {
print(error)
}