我正在用 Swift 的 Codable 替换旧的 JSON 解析代码,遇到了一点麻烦。我想这与其说是 Codable 问题,不如说是 DateFormatter 问题。从结构开始
我正在用 Swift 的 Codable 替换旧的 JSON 解析代码,但遇到了一些问题。我想这与其说是 Codable 的问题,不如说是 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" }
"""
现在它坏了。日期有时会返回小数秒,有时则不会。我以前解决这个问题的方法是在我的映射代码中,我有一个转换函数,它尝试了带小数秒和不带小数秒的 dateFormats。不过,我不太确定如何使用 Codable 来解决这个问题。有什么建议吗?
要将 ISO8601 字符串解析为日期,您必须使用 DateFormatter。在较新的系统(例如 iOS11+)中,您可以使用 ISO8601DateFormatter。
只要您不知道日期是否包含毫秒,就应该为每种情况创建 2 个格式化程序。然后,在将字符串解析为日期时依次使用两者。
/// Formatter for ISO8601 with milliseconds
lazy var iso8601FormatterWithMilliseconds: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
return dateFormatter
}()
/// Formatter for ISO8601 without milliseconds
lazy var iso8601Formatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
return dateFormatter
}()
lazy var iso8601FormatterWithMilliseconds: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
// GMT or UTC -> UTC is standard, GMT is TimeZone
formatter.timeZone = TimeZone(abbreviation: "GMT")
formatter.formatOptions = [.withInternetDateTime,
.withDashSeparatorInDate,
.withColonSeparatorInTime,
.withTimeZone,
.withFractionalSeconds]
return formatter
}()
/// Formatter for ISO8601 without milliseconds
lazy var iso8601Formatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
// GMT or UTC -> UTC is standard, GMT is TimeZone
formatter.timeZone = TimeZone(abbreviation: "GMT")
formatter.formatOptions = [.withInternetDateTime,
.withDashSeparatorInDate,
.withColonSeparatorInTime,
.withTimeZone]
return formatter
}()
如您所见,需要创建 2 个格式化程序。如果您想支持较旧的系统,则需要创建 4 个格式化程序。为了更简单,请查看 GitHub 上的 Tomorrow ,您可以在那里看到此问题的完整解决方案。
要将字符串转换为日期,请使用:
let date = Date.fromISO("2020-11-01T21:10:56.22+02:00")