Gui*_*rez 30 nsdateformatter swift dateformatter swift4 codable
我正在用Swift的Codable替换旧的JSON解析代码,并且遇到了一些麻烦.我想这不是一个可编码的问题,因为它是一个DateFormatter问题.
从结构开始
struct JustADate: Codable {
var date: Date
}
Run Code Online (Sandbox Code Playgroud)
和一个json字符串
let json = """
{ "date": "2017-06-19T18:43:19Z" }
"""
Run Code Online (Sandbox Code Playgroud)
现在让我们解码
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let data = json.data(using: .utf8)!
let justADate = try! decoder.decode(JustADate.self, from: data) //all good
Run Code Online (Sandbox Code Playgroud)
但是,如果我们更改日期以使其具有小数秒,例如:
let json = """
{ "date": "2017-06-19T18:43:19.532Z" }
"""
Run Code Online (Sandbox Code Playgroud)
现在它打破了.日期有时会以小秒数回归,有时则不会.我以前解决它的方式是在我的映射代码中我有一个转换函数,它尝试使用和不使用小数秒的dateFormats.我不太确定如何使用Codable来处理它.有什么建议?
Leo*_*bus 43
您可以使用两个不同的日期格式化程序(有和没有分数秒)并创建自定义DateDecodingStrategy.如果在解析API返回的日期时出现故障,您可以按照@PauloMattos在评论中的建议抛出DecodingError:
iOS 9,macOS 10.9,tvOS 9,watchOS 2,Xcode 9或更高版本
自定义ISO8601 DateFormatter:
extension 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:ss.SSSXXXXX"
return formatter
}()
static let iso8601noFS: 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
}()
}
Run Code Online (Sandbox Code Playgroud)
习俗DateDecodingStrategy和Error:
extension JSONDecoder.DateDecodingStrategy {
static let customISO8601 = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
if let date = Formatter.iso8601.date(from: string) ?? Formatter.iso8601noFS.date(from: string) {
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
}
}
Run Code Online (Sandbox Code Playgroud)
习俗DateEncodingStrategy:
extension JSONEncoder.DateEncodingStrategy {
static let customISO8601 = custom {
var container = $1.singleValueContainer()
try container.encode(Formatter.iso8601.string(from: $0))
}
}
Run Code Online (Sandbox Code Playgroud)
编辑/更新:
Xcode 9•Swift 4•iOS 11或更高版本
ISO8601DateFormatter现在支持formatOptions .withFractionalSecondsiOS11或更高版本:
extension Formatter {
static let iso8601: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
static let iso8601noFS = ISO8601DateFormatter()
}
Run Code Online (Sandbox Code Playgroud)
海关DateDecodingStrategy和DateEncodingStrategy上面所示的相同.
// 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.iso8601.string(from: isoDates.dateWith9FS)) // 2017-06-19T18:43:19.532Z
print(Formatter.iso8601.string(from: isoDates.dateWith3FS)) // 2017-06-19T18:43:19.532Z
print(Formatter.iso8601.string(from: isoDates.dateWith2FS)) // 2017-06-19T18:43:19.530Z
print(Formatter.iso8601.string(from: isoDates.dateWithoutFS)) // 2017-06-19T18:43:19.000Z
} catch {
print(error)
}
Run Code Online (Sandbox Code Playgroud)
小智 5
要解析 ISO8601 字符串到日期,您必须使用 DateFormatter。在较新的系统(例如 iOS11+)中,您可以使用 ISO8601DateFormatter。
只要您不知道日期是否包含毫秒,您就应该为每种情况创建 2 个格式化程序。然后,在将 String 解析为 Date 期间,因此使用两者。
/// 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
}()
Run Code Online (Sandbox Code Playgroud)
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
}()
Run Code Online (Sandbox Code Playgroud)
您可以注意到,需要创建 2 个格式化程序。如果你想支持旧系统,它会提供 4 个格式化程序。为了更简单,请查看GitHub 上的 Tomorrow,您可以在其中查看此问题的完整解决方案。
要将字符串转换为日期,请使用:
let date = Date.fromISO("2020-11-01T21:10:56.22+02:00")
| 归档时间: |
|
| 查看次数: |
6430 次 |
| 最近记录: |