You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:05:21