Rust 中的日期时间序列化:将 UTC 转换为北京时间
在 Rust 后端开发中,处理日期时间并确保其在 API 响应中以正确的时区显示是一个常见需求。本文将基于提供的代码示例,探讨如何在 SeaORM 模型中使用自定义序列化函数,将 UTC 时间转换为北京时间并格式化输出。
代码结构分析
首先,让我们看一下主要的 Model 结构体:
rust#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize, ToSchema)] #[sea_orm(table_name = "categories")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub name: String, #[sea_orm(unique)] pub slug: String, #[sea_orm(column_type = "Text", nullable)] pub description: Option<String>, #[schema(value_type = String, format = DateTime)] #[serde(serialize_with = "fmt_beijing")] pub created_at: DateTimeUtc, #[schema(value_type = String, format = DateTime)] #[serde(serialize_with = "fmt_beijing")] pub updated_at: DateTimeUtc, }
这个结构体表示一个分类(category)实体,包含 ID、名称、唯一标识符(slug)、描述以及创建和更新时间字段。
核心功能:时区转换与格式化
时区转换函数
rustuse chrono::{DateTime, FixedOffset, Utc}; /// UTC -> 北京时间 (+8) fn to_beijing(dt: DateTime<Utc>) -> DateTime<FixedOffset> { let beijing = FixedOffset::east_opt(8 * 3600).unwrap(); dt.with_timezone(&beijing) }
这个函数将 UTC 时间转换为北京时间(东八区)。它使用 FixedOffset 创建一个 +8 小时的时区偏移,然后使用 with_timezone 方法进行转换。
自定义序列化函数
rust/// 序列化函数:2025-09-24 13:44:29 pub fn fmt_beijing<S>(dt: &DateTime<Utc>, s: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { let bj = to_beijing(*dt); s.serialize_str(&bj.format("%Y-%m-%d %H:%M:%S").to_string()) }
这是核心的序列化函数,它:
- 接收 UTC 时间戳和序列化器
- 将 UTC 时间转换为北京时间
- 格式化为 "年-月-日 时:分:秒" 的字符串格式
- 通过序列化器返回字符串
关键技术点
1. Serde 自定义序列化
通过 #[serde(serialize_with = "fmt_beijing")] 属性,我们告诉 Serde 库在序列化 created_at 和 updated_at 字段时使用自定义的 fmt_beijing 函数,而不是默认的序列化方式。
2. OpenAPI 集成
#[schema(value_type = String, format = DateTime)] 属性确保了在生成 OpenAPI 文档时,这些字段会被正确描述为日期时间格式的字符串。
3. 时区处理
代码正确处理了时区转换,这对于国际化应用至关重要。数据库中通常存储 UTC 时间,但面向用户时需要显示本地时间。
实际应用场景
这种时间处理方式特别适用于:
- 中文用户界面的 API:确保时间显示符合中国用户的习惯
- 国际化应用:可以根据用户所在地动态调整时区显示
- 日志和审计:保持时间记录的一致性,同时提供友好的显示格式
总结
通过结合 SeaORM、Serde 和 Chrono 库,我们实现了一个优雅的日期时间处理方案,既保证了数据存储的一致性(使用 UTC),又提供了符合用户期望的显示格式(北京时间)。这种模式可以轻松扩展到其他需要自定义序列化的场景,是 Rust Web 开发中的一个实用技巧。
这种处理方式体现了 Rust 语言的强大之处:通过类型系统和 trait 实现,可以在编译期保证代码的正确性,同时保持高度的灵活性和可定制性。
