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

Swift中如何统一将Int类型Unix时间编码为可读字符串?

问题描述

现有存储自定义对象的结构体:

struct Event {
   let type: String
   let value: String
   let time: Int
}

其中time为毫秒级Unix时间戳,默认转JSON后输出为数字格式:

{
   "type" : "rain",
   "value" : "0.5",
   "time" : 1681663944000
}

需求是将所有结构体中名为(或包含)time的Int类型时间戳,统一转为16/04/2023这类人类可读的日期字符串。所有对象都遵循BaseObject协议,通过协议扩展获得基于Codable的toJsonString()方法:

protocol BaseObject: Codable {
    
}

extension BaseObject {
    func toJsonString() -> String {
        let jsonEncoder = JSONEncoder()
        do {
            let jsonData = try jsonEncoder.encode(self)
            let json = String(data: jsonData, encoding: String.Encoding.utf8)
            return json ?? ""
        } catch {
            return ""
        }
    }
}

目前临时方案是先编码为JSON再转字典修改time字段,但存在性能和嵌套处理的问题,寻求更优实现思路。

更优实现思路

方案一:自定义Timestamp类型(类型安全,推荐)

定义专门的Timestamp类型封装毫秒时间戳,将日期转换逻辑完全封装在类型内部,实现自定义Codable逻辑。

// 自定义Timestamp类型,封装毫秒时间戳
struct Timestamp: Codable {
    let milliseconds: Int
    
    // 转换为指定格式的日期字符串
    private func formattedDate() -> String {
        let date = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
        let formatter = DateFormatter()
        formatter.dateFormat = "dd/MM/yyyy"
        // 按需设置时区,例如:formatter.timeZone = TimeZone(identifier: "UTC")
        return formatter.string(from: date)
    }
    
    // 自定义编码逻辑:输出日期字符串
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(formattedDate())
    }
    
    // 自定义解码逻辑:解析回毫秒时间戳(如果需要解码功能)
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        milliseconds = try container.decode(Int.self)
    }
    
    // 便捷初始化器,直接传入毫秒数
    init(_ milliseconds: Int) {
        self.milliseconds = milliseconds
    }
}

修改原有结构体的time属性类型:

struct Event: BaseObject {
    let type: String
    let value: String
    let time: Timestamp // 替换原来的Int类型
}

使用效果:

let event = Event(type: "rain", value: "0.5", time: Timestamp(1681663944000))
print(event.toJsonString())
// 输出: {"type":"rain","value":"0.5","time":"16/04/2023"}

优点:

  • 类型安全,避免普通Int被误识别为时间戳
  • 编码逻辑封装在类型内部,无需修改原有toJsonString()方法
  • 自动支持嵌套对象中的time属性处理

缺点:需要修改所有结构体中time属性的类型


方案二:自定义JSONEncoder编码策略(无需修改结构体,灵活)

通过扩展KeyedEncodingContainer,拦截所有属性名包含"time"的Int类型值,自动转换为日期字符串,同时通过userInfo传递日期格式配置。

// 定义用户信息键,用于传递日期格式
extension CodingUserInfoKey {
    static let timestampDateFormat = CodingUserInfoKey(rawValue: "timestampDateFormat")!
}

// 扩展KeyedEncodingContainer,处理含"time"的Int属性
extension KeyedEncodingContainer {
    mutating func encode(_ value: Int, forKey key: Key) throws {
        // 判断属性名是否包含"time",且存在日期格式配置
        guard key.stringValue.contains("time"), 
              let dateFormat = codingUserInfo[.timestampDateFormat] as? String else {
            // 不符合条件则按默认方式编码
            try super.encode(value, forKey: key)
            return
        }
        
        // 将毫秒时间戳转为指定格式的日期字符串
        let date = Date(timeIntervalSince1970: TimeInterval(value) / 1000)
        let formatter = DateFormatter()
        formatter.dateFormat = dateFormat
        // 按需设置时区,例如:formatter.timeZone = TimeZone(identifier: "Asia/Shanghai")
        
        // 编码为字符串
        try encode(formatter.string(from: date), forKey: key)
    }
}

// 修改BaseObject扩展的toJsonString()方法,配置编码器
extension BaseObject {
    func toJsonString() -> String {
        let jsonEncoder = JSONEncoder()
        // 设置日期格式
        jsonEncoder.userInfo[.timestampDateFormat] = "dd/MM/yyyy"
        do {
            let jsonData = try jsonEncoder.encode(self)
            return String(data: jsonData, encoding: .utf8) ?? ""
        } catch {
            return ""
        }
    }
}

使用效果:原有结构体无需修改,直接调用toJsonString()即可自动转换time属性:

let event = Event(type: "rain", value: "0.5", time: 1681663944000)
print(event.toJsonString())
// 输出: {"type":"rain","value":"0.5","time":"16/04/2023"}

优点:

  • 无需修改任何结构体的属性类型
  • 支持所有层级(包括嵌套对象)中属性名含"time"的Int值自动处理
  • 日期格式可通过userInfo灵活配置

缺点:

  • 依赖属性名命名规则(需包含"time"),不匹配则无法处理
  • 非时间戳的Int属性若命名含"time"会被错误转换(需规范命名)
临时方案的不足

你的临时方案先编码为JSON再转字典修改,存在以下问题:

  • 性能损耗:JSON转字典再转JSON的过程会额外消耗CPU和内存,大量数据场景下更明显
  • 嵌套处理失效:无法处理嵌套对象中的time属性(比如结构体内部包含另一个带time属性的结构体)
  • 类型不安全:转字典时需要强制类型转换,容易出现意外错误

上述两个方案均直接在Codable编码流程中处理,性能更优,支持嵌套结构,逻辑更严谨。

内容的提问来源于stack exchange,提问作者Fry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:42:47