如何在Swift4中使用Codable将日期字符串转换为可选的小数秒

我正在用Swift的Codable替换旧的JSON解析代码,并且遇到了一些麻烦。我想这不是一个Codable问题,而是一个DateFormatter问题。

 struct JustADate: Codable {

var date: Date

}

let json = """

{ "date": "2017-06-19T18:43:19Z" }

"""

let decoder = JSONDecoder()

decoder.dateDecodingStrategy = .iso8601

let data = json.data(using: .utf8)!

let justADate = try! decoder.decode(JustADate.self, from: data) //all good

但是,如果我们更改日期,使其具有小数秒,例如:

let json = """

{ "date": "2017-06-19T18:43:19.532Z" }

"""

现在坏了。日期有时会以小数秒返回,有时却不会。我用来解决该问题的方法是在映射代码中,我有一个转换函数,该函数尝试使用带和不带小数秒的dateFormats。我不太确定如何使用Codable处理它。有什么建议?

回答:

您可以使用两个不同的日期格式器(带和不带小数秒)并创建自定义的DateDecodingStrategy。如果在解析API返回的日期时失败,则可以按@PauloMattos的建议在注释中引发DecodingError:

自定义ISO8601DateFormatter:

extension Formatter {

static let iso8601withFractionalSeconds: DateFormatter = {

let formatter = DateFormatter()

formatter.calendar = Calendar(identifier: .iso8601)

formatter.locale = Locale(identifier: "en_US_POSIX")

formatter.timeZone = TimeZone(secondsFromGMT: 0)

formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"

return formatter

}()

static let iso8601: DateFormatter = {

let formatter = DateFormatter()

formatter.calendar = Calendar(identifier: .iso8601)

formatter.locale = Locale(identifier: "en_US_POSIX")

formatter.timeZone = TimeZone(secondsFromGMT: 0)

formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"

return formatter

}()

}


习惯DateDecodingStrategy

extension JSONDecoder.DateDecodingStrategy {

static let customISO8601 = custom {

let container = try $0.singleValueContainer()

let string = try container.decode(String.self)

if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) {

return date

}

throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")

}

}


习惯DateEncodingStrategy

extension JSONEncoder.DateEncodingStrategy {

static let customISO8601 = custom {

var container = $1.singleValueContainer()

try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))

}

}


ISO8601DateFormatter现在支持formatOptions.withFractionalSeconds

extension Formatter {

static let iso8601withFractionalSeconds: ISO8601DateFormatter = {

let formatter = ISO8601DateFormatter()

formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

return formatter

}()

static let iso8601: ISO8601DateFormatter = {

let formatter = ISO8601DateFormatter()

formatter.formatOptions = [.withInternetDateTime]

return formatter

}()

}


习惯DateDecodingStrategyDateEncodingStrategy将与上面显示的相同。


// Playground testing

struct ISODates: Codable {

let dateWith9FS: Date

let dateWith3FS: Date

let dateWith2FS: Date

let dateWithoutFS: Date

}


let isoDatesJSON = """

{

"dateWith9FS": "2017-06-19T18:43:19.532123456Z",

"dateWith3FS": "2017-06-19T18:43:19.532Z",

"dateWith2FS": "2017-06-19T18:43:19.53Z",

"dateWithoutFS": "2017-06-19T18:43:19Z",

}

"""


let isoDatesData = Data(isoDatesJSON.utf8)

let decoder = JSONDecoder()

decoder.dateDecodingStrategy = .customISO8601

do {

let isoDates = try decoder.decode(ISODates.self, from: isoDatesData)

print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith9FS)) // 2017-06-19T18:43:19.532Z

print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith3FS)) // 2017-06-19T18:43:19.532Z

print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith2FS)) // 2017-06-19T18:43:19.530Z

print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWithoutFS)) // 2017-06-19T18:43:19.000Z

} catch {

print(error)

}

以上是 如何在Swift4中使用Codable将日期字符串转换为可选的小数秒 的全部内容, 来源链接: utcz.com/qa/402770.html

回到顶部