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

Swift环境下GraphQL服务端与客户端日期一致性处理问询

Swift GraphQL服务端与客户端日期编解码一致性解决方案

一、先解决当前的格式不匹配问题

你当前客户端的代码存在逻辑矛盾:服务端输出的是UNIX时间戳(整数/带小数的Double),但客户端却尝试将其当作ISO8601字符串解析,必然会报错。需要先统一两端的编解码规则,以下是两种可行方案:

方案1:统一使用UNIX时间戳(带毫秒精度)

服务端已默认输出时间戳,只需调整客户端的CustomScalarType实现:

// 删除ClientAPI的Date别名定义
extension Foundation.Date: CustomScalarType {
    public init(_jsonValue value: JSONValue) throws {
        // 兼容服务端返回的整数/带小数的时间戳
        guard let timestamp = value as? Double else {
            throw JSONDecodingError.couldNotConvert(value: value, to: Double.self)
        }
        self = Date(timeIntervalSince1970: timestamp)
    }

    public var _jsonValue: JSONValue {
        self.timeIntervalSince1970
    }
}

Fluent的@Timestamp返回带小数的时间戳是正常行为(Date本身包含毫秒级精度),客户端用Double解析即可兼容两种格式。

方案2:统一使用ISO8601字符串(更适合外部API)

如果需要可读性更强、符合通用API规范的格式,可修改服务端的自定义标量逻辑,同时调整客户端适配:

服务端修改(Graphiti/GraphQLSwift)

extension Date: GraphQLScalar {
    public static var name: String { "Date" }
    
    public static func serialize(value: Date) throws -> Map {
        let formatter = ISO8601DateFormatter()
        // 配置为带毫秒的UTC格式,两端保持一致
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        formatter.timeZone = TimeZone(identifier: "UTC")
        return Map.string(formatter.string(from: value))
    }
    
    public static func parse(value: Map) throws -> Date {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        formatter.timeZone = TimeZone(identifier: "UTC")
        
        guard let dateString = value.stringValue, let date = formatter.date(from: dateString) else {
            throw GraphQLError(message: "无效的日期格式,需符合ISO8601标准(如2024-01-17T08:27:07.214Z)")
        }
        return date
    }
}

// 在Schema中注册该标量
let schema = try Schema<Query, Mutation, Context> {
    Scalar(Date.self)
    // 其他类型定义...
}

客户端修改(Apollo iOS)

// 删除ClientAPI的Date别名定义
extension Foundation.Date: CustomScalarType {
    public init(_jsonValue value: JSONValue) throws {
        guard let dateString = value as? String else {
            throw JSONDecodingError.couldNotConvert(value: value, to: String.self)
        }
        
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        formatter.timeZone = TimeZone(identifier: "UTC")
        
        guard let date = formatter.date(from: dateString) else {
            throw JSONDecodingError.couldNotConvert(value: value, to: Date.self)
        }
        self = date
    }

    public var _jsonValue: JSONValue {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        formatter.timeZone = TimeZone(identifier: "UTC")
        return formatter.string(from: self)
    }
}

二、可靠管理日期格式的核心原则

  • 两端严格对齐规则:无论选择时间戳还是ISO8601,必须保证服务端输出格式与客户端解析逻辑完全匹配,包括精度(是否带毫秒)、时区(建议统一用UTC避免歧义)。
  • 复用格式化器配置:在服务端和客户端使用相同配置的日期格式化器,避免因参数差异导致解析失败。
  • 兼容Fluent字段:Fluent的@Timestamp生成的Date包含毫秒精度,只要两端逻辑支持该精度,就能正常处理。

三、除UNIX时间戳外的其他可选格式

当然可以使用其他格式,常见的有:

  • ISO8601/RFC3339字符串:最推荐的外部API格式,可读性强、包含时区信息,无歧义。
  • 自定义格式化字符串:如YYYY-MM-DD HH:mm:ss,但不建议——缺少时区信息容易引发时区转换错误,除非所有对接方明确约定统一时区。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:52:48