Swift中如何实现自定义日期类型解析yyyy-MM-dd并兼容JSON解码?
解决方案:同时解析两种日期格式的JSON数据
一、修复DateOnly类的初始化问题
你继承NSDate时调用的初始化器不正确,NSDate的指定初始化器是init(timeIntervalSinceReferenceDate:),而非init(timeInterval:since:)。同时建议替换强制解包为错误抛出,提升代码安全性:
class DateOnly: NSDate, @unchecked Sendable { convenience init(from dateString: String) throws { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" formatter.locale = Locale(identifier: "en_US_POSIX") guard let date = formatter.date(from: dateString) else { throw NSError(domain: "DateParsingError", code: 0, userInfo: [NSLocalizedDescriptionKey: "无效的日期格式"]) } self.init(timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
二、创建MyCustomDate实现双格式JSON解码
更符合Swift设计的方式是用结构体封装Date并实现Decodable,专门处理yyyy-MM-dd格式解析,同时不影响原有Date的RFC3339解码逻辑:
1. 定义MyCustomDate结构体
struct MyCustomDate: Decodable, @unchecked Sendable { let date: Date init(from dateString: String) throws { guard let date = Formatter.yyyyMMdd.date(from: dateString) else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: [], debugDescription: "无效的yyyy-MM-dd格式日期:\(dateString)" ) ) } self.date = date } // 实现Decodable协议的解码初始化器 init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) try self.init(from: dateString) } }
2. 优化日期格式化器(避免重复创建)
将格式化器改为静态属性,减少性能开销:
extension Formatter { static let yyyyMMdd: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() static let rfc3339: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter }() } // 同步更新RFC3339解码策略 extension JSONDecoder.DateDecodingStrategy { static let rfc3339 = custom { decoder in let dateStr = try decoder.singleValueContainer().decode(String.self) if let date = Formatter.rfc3339.date(from: dateStr) { return date } throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "无效的RFC3339格式日期:\(dateStr)" ) ) } }
3. 调整Foo结构体
直接使用定义好的类型即可,Date会沿用全局的RFC3339解码策略,MyCustomDate会自动处理yyyy-MM-dd格式:
struct Foo: Decodable { let date1: Date // 解析RFC3339格式 let date2: MyCustomDate // 解析yyyy-MM-dd格式 static let decoder: JSONDecoder = { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .rfc3339 return decoder }() }
三、测试示例
针对如下JSON数据:
{ "date1": "2024-05-20T12:34:56.789Z", "date2": "2024-05-20" }
可以通过以下代码完成解析:
let jsonData = """ { "date1": "2024-05-20T12:34:56.789Z", "date2": "2024-05-20" } """.data(using: .utf8)! do { let foo = try Foo.decoder.decode(Foo.self, from: jsonData) print("date1: \(foo.date1)") print("date2: \(foo.date2.date)") } catch { print("解码失败:\(error)") }
内容的提问来源于stack exchange,提问作者James Sumners
相关产品推荐
相关产品推荐

