Swift使用JSONDecoder解码日期之ISO 8601 标准格式
Swift使用JSONDecoder解码日期之ISO 8601 标准格式

Swift使用JSONDecoder解码日期之ISO 8601 标准格式

在JSONDecoder中,可以使用dateDecodingStrategy支持以下几种解码方法:

.iso8601: 使用ISO8601 解码策略
.deferredToDate:使用 Date 自身的编码/解码方法。
.secondsSince1970:将日期表示为自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数。
.millisecondsSince1970:将日期表示为自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数。
.formatted(formatter):使用自定义的 DateFormatter 来解析日期。

假设需要解码ISO 8601标准的日期:当返回的JSON日期格式符合ISO 8601 标准,可以直接使用 JSONDecoder 的内置策略。

使用内置策略解码时间

解决方案为,在解码时配置dateDecodingStrategy:

do {
    let (data, _) = try await URLSession.shared.data(from: url)
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601 // 使用 ISO8601 解码策略
    return try decoder.decode([Friendface].self, from: data)
} catch {
    print("Error decoding data: \(error)")
    return nil
}

同时,将符合ISO 8601 标准字段修改为Date类型:

class Friendface: Codable, Hashable {
    ...
    var registered: Date // 修改为 Date 类型
}

在显示视图新增formatDate函数:

func formatDate(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    formatter.timeZone = TimeZone.current // 设置为本地时区
    return formatter.string(from: date)
}

这个函数规定了显示的日期格式。

在视图中使用formatDate函数显示时间:

Section("registered") {
    Text("\(formatDate(friend?.registered ?? Date()))")
}

最后的显示效果为:

自定义日期解析器

除了使用内置策略解码时间,还可以自定义 DateDecodingStrategy 并结合 DateFormatter 处理时间。

首先确保符合ISO 8601 标准字段为Date类型:

var registered: Date // 修改为 Date 类型

使用JSONDecoder进行解码时,配置解码策略:

do {
    let (data, _) = try await URLSession.shared.data(from: url)
    let decoder = JSONDecoder()
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" // 根据数据格式
    decoder.dateDecodingStrategy = .formatted(dateFormatter)
    return try decoder.decode([Friendface].self, from: data)
} catch {
    print("Error decoding data: \(error)")
    return nil
}

数据格式规定了显示的日期格式,注意dateFormat的格式为:

yyyy-MM-dd HH:mm:ssZ

尾部后面需要添加Z,这是因为这种格式是一种标准的日期时间格式,通常用于解析或生成符合 ISO 8601 标准的日期字符串。

固定字符 ‘T’,用于分隔日期和时间。

在 ISO 8601 格式中,日期和时间之间通常由 ‘T’ 分隔,例如:2024-11-10T13:45:30Z。

Z 是 “Zulu time” 的简写,表示 UTC时间。

在显示视图部分,和内置策略解码时间一样:

新增formatDate函数:

func formatDate(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    formatter.timeZone = TimeZone.current // 设置为本地时区
    return formatter.string(from: date)
}

这个函数规定了显示的日期格式。

在视图中使用formatDate函数显示时间:

Section("registered") {
    Text("\(formatDate(friend?.registered ?? Date()))")
}

带有毫秒的ISO8601格式

在使用dateDecodingStrategy的过程中,可能存在无法解码ISO8601格式的情况:

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

如果使用DecodingError就会看到如下报错:

---4---
Data corrupted: Expected date string to be ISO8601-formatted.
codingPath: [_CodingKey(stringValue: "Index 0", intValue: 0)]

报错说明:在 JSON 解码过程中,第一个元素(Index 0)中的某个字段被指定为 Date 类型,但它的字符串格式并不是 ISO8601 格式,导致解码失败。

如果JSON的日期字段为ISO8601格式,但是带了毫秒.000Z导致的报错。因为虽然大多数系统能识别带有毫米的格式,Swift默认的 .iso8601 并不能正确解析这种格式,所以会得到 dataCorrupted 错误。

针对带有毫米的ISO8601格式,需要使用DateFormatter:


let decoder = JSONDecoder()
        
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
formatter.locale = Locale(identifier: "en_US_POSIX")
decoder.dateDecodingStrategy = .formatted(formatter)

通过DateFormatter设置对应的日期格式,Swift就可以解析带有毫秒的ISO时间字符串。

总结

1、内置策略解码时间使用:

decoder.dateDecodingStrategy = .iso8601 // 使用 ISO8601 解码策略

配置JSONDecoder的日期解码策略为.ISO8601。

2、自定义的日期解码器使用:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" // 根据数据格式
decoder.dateDecodingStrategy = .formatted(dateFormatter)

在这里使用的是DateFormatter日期格式化,使用dateFormat设置了日期格式,然后将设置的DateFormatter作为JSONDecoder的日期解析策略。

两种解码方式都可以用于处理ISO 8601标准格式解析日期。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注