gpt4 book ai didi

swift - 如何使用 Swift 中的 Codable 转换带有可选小数秒的日期字符串?

转载 作者:行者123 更新时间:2023-11-30 11:57:28 25 4
gpt4 key购买 nike

我正在用 Swift 的 Codable 替换旧的 JSON 解析代码,但遇到了一些障碍。我想这与其说是一个可编码的问题,不如说是一个 DateFormatter 的问题。

从结构开始

 struct JustADate: Codable {
var date: Date
}

和一个 json 字符串

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" }
"""

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

最佳答案

您可以使用两种不同的日期格式化程序(带和不带小数秒)并创建自定义 DateDecodingStrategy。如果解析 API 返回的日期失败,您可以按照@PauloMattos 在评论中的建议抛出 DecodingError:

iOS 9、macOS 10.9、tvOS 9、watchOS 2、Xcode 9 或更高版本

自定义 ISO8601 DateFormatter:

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))
}
}
<小时/>

编辑/更新:

Xcode 10 • Swift 4.2 或更高版本 • iOS 11.2.1 或更高版本

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)
}

关于swift - 如何使用 Swift 中的 Codable 转换带有可选小数秒的日期字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47644872/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com