首页 > 解决方案 > 谁以正确的方式实施 ISO8601?斯威夫特还是春天?

问题描述

Spring 框架中使用的 Jackson 序列化器的默认行为如下:(来自 spring-data-rest 源代码):

        /**
         * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ},
         * e.g. "2000-10-31T01:30:00.000-05:00".
         * <p>This is the default if no annotation value is specified.
         */

所以这种格式的一个例子可能是2019-03-20T11:18:46.000+0000. Swift 也有 ISO8601 解码器,但事实上,对于 Swift,这个字符串只是因为毫秒而具有无效格式。如果去掉毫秒部分,swift可以成功反序列化字符串。

struct Test: Codable {
    let createdAt: Date
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

let data2 = "{\"createdAt\": \"2018-12-05T14:05:35.000+0000\"}".data(using: .utf8)!
let decoded2 = try! decoder.decode(Test.self, from: data2)

Swift 指的是 RFC 3339 https://www.ietf.org/rfc/rfc3339.txt并没有提到毫秒。我的问题是 - 那么什么格式是正确的呢?如果某些东西是 ISO 格式,应该严格定义,我通常不希望编写自定义反序列化器 (swift) 或定义用于序列化的自定义模式 (spring)。

标签: swiftspringiso

解决方案


您应该设置 iso8601 选项。您可以使用 ISO8601DateFormatter 来完成。

struct Test: Codable {
    let createdAt: Date
}

enum CustomDateDecodingStrategy {

    private static let formatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [
            .withFullDate,
            .withFullTime,
            .withTimeZone,
            .withFractionalSeconds
        ]
        return formatter
    }()

    static func decode(_ decoder: Decoder) throws -> Date {
        let container = try decoder.singleValueContainer()
        let dateStr = try container.decode(String.self)
        if let date = formatter.date(from: dateStr) {
            return date
        } else {
            throw NSError(domain: "date", code: -1, userInfo: nil)
        }
    }

}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(CustomDateDecodingStrategy.decode)

let data2 = "{\"createdAt\": \"2018-12-05T14:05:35.000+0000\"}".data(using: .utf8)!
let decoded2 = try! decoder.decode(Test.self, from: data2)

推荐阅读