rust时间类型转换

发布时间:2026/11/2 6:38:42
rust时间类型转换

Rust 里时间类型转换主要围绕chrono展开,也会涉及std::time、time,以及 DB/HTTP 边界上的互转。下面按常见场景整理,示例以chrono 0.4.x为准。

1. 标准库 ↔ chrono

use std::time::{SystemTime, UNIX_EPOCH}; use chrono::{DateTime, Utc}; // SystemTime -> DateTime<Utc> let st = SystemTime::now(); let dt: DateTime<Utc> = DateTime::<Utc>::from(st); // DateTime<Utc> -> SystemTime let st2: SystemTime = dt.into(); // 手动拿秒/纳秒 let dur = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time before epoch"); let secs = dur.as_secs(); let nanos = dur.subsec_nanos();

2. 时间戳 ↔ DateTime

use chrono::{TimeZone, Utc}; // DateTime -> 秒/毫秒时间戳 let now = Utc::now(); let ts_sec = now.timestamp(); let ts_ms = now.timestamp_millis(); // 秒时间戳 -> DateTime<Utc> let dt = Utc.timestamp_opt(ts_sec, 0) .single() .expect("invalid timestamp"); // 毫秒时间戳 -> DateTime<Utc> let dt_ms = Utc.timestamp_millis_opt(ts_ms) .single() .expect("invalid timestamp");

注意:毫秒时间戳不要直接传给timestamp_opt(secs, nanos)的秒参数,否则会得到错误年份。

3. 字符串 ↔ NaiveDateTime / DateTime

use chrono::{DateTime, NaiveDateTime, Utc}; // NaiveDateTime 解析 let naive = NaiveDateTime::parse_from_str( "2026-10-31 12:30:00", "%Y-%m-%d %H:%M:%S", )?; // NaiveDateTime -> DateTime<Utc> let dt = Utc.from_utc_datetime(&naive); // DateTime -> 字符串 let s = dt.format("%Y-%m-%d %H:%M:%S").to_string(); // RFC3339 字符串解析 let dt2: DateTime<Utc> = "2026-10-31T04:30:00Z".parse()?; let rfc = dt2.to_rfc3339();

如果字符串带时区,例如"2026-10-31 12:30:00 +08:00",可以用:

let dt = DateTime::parse_from_str( "2026-10-31 12:30:00 +08:00", "%Y-%m-%d %H:%M:%S %:z", )?; let utc = dt.with_timezone(&Utc);

4. NaiveDateTime ↔ DateTime

use chrono::{DateTime, NaiveDateTime, Utc, Local, FixedOffset, TimeZone}; let naive = NaiveDateTime::parse_from_str( "2026-10-31 12:30:00", "%Y-%m-%d %H:%M:%S", )?; // 明确它是 UTC let utc: DateTime<Utc> = Utc.from_utc_datetime(&naive); // 明确它是本地时间 let local: DateTime<Local> = Local.from_local_datetime(&naive) .single() .expect("ambiguous or invalid local time"); // 转成固定时区,例如 UTC+8 let offset = FixedOffset::east_opt(8 * 3600) .expect("invalid offset"); let dt_offset: DateTime<FixedOffset> = utc.with_timezone(&offset);

NaiveDateTime没有时区信息,所以不能直接“变成”DateTime<Tz>,必须说明它原本属于哪个时区。

5. 时区互转

use chrono::{DateTime, Utc}; use chrono_tz::Asia::Shanghai; let utc: DateTime<Utc> = Utc::now(); let shanghai = utc.with_timezone(&Shanghai);

后端服务里比较稳妥的做法是:存储和内部传输用 UTC,展示时再转目标时区。

6. chrono ↔ time crate

如果项目里同时用了chrono和time,可以手动转:

use time::OffsetDateTime; use chrono::{DateTime, Utc}; // time -> chrono let odt: OffsetDateTime = OffsetDateTime::now_utc(); let ts = odt.unix_timestamp(); let dt: DateTime<Utc> = Utc.timestamp_opt(ts, odt.nanosecond()) .single() .expect("invalid timestamp"); // chrono -> time let now = Utc::now(); let odt2 = OffsetDateTime::from_unix_timestamp_nanos( now.timestamp_nanos_opt().expect("out of range") as i128, ).expect("invalid time");

也可以用timetraveler这类小库做互转,但它不是标准方案,接入前要看维护状态。

7. DB / SeaORM / serde 场景

  • SeaORM / sqlx:实体里常见DateTime<Utc>、DateTime<FixedOffset>或chrono::NaiveDateTime,具体取决于字段类型和 feature。
  • serde 序列化:给chrono开启serdefeature 后,DateTime<Utc>默认通常按 RFC3339 序列化;如果需要自定义格式,可以用serde_with或手写serialize_with/deserialize_with。

工程化建议

  • 内部统一用DateTime<Utc>,边界层再做解析、格式化、时区转换。
  • 解析外部时间一律走Result,不要直接unwrap。
  • 时间戳转换优先用timestamp_opt、timestamp_millis_opt、single(),避免溢出和非法时间静默出错。
  • 如果项目已经用 SeaORM,实体时间类型要和数据库列类型、chronofeature 保持一致。

需要我帮你整理一份 SeaORM 实体中时间字段配置与序列化示例代码吗?

按chrono 0.4.x整理,优先列推荐写法;带opt/Result/Option的更适合生产代码,后端解析外部时间时尽量不要unwrap。

1. 时间戳 ↔ DateTime

方向推荐函数说明
秒时间戳 → DateTimeDateTime::from_timestamp(secs, nanos)返回Option<DateTime<Utc>>
秒时间戳 → DateTimeUtc.timestamp_opt(secs, nanos)返回MappedLocalTime<DateTime<Utc>>
毫秒时间戳 → DateTimeDateTime::from_timestamp_millis(millis)返回Option<DateTime<Utc>>
毫秒时间戳 → DateTimeUtc.timestamp_millis_opt(millis)返回MappedLocalTime<DateTime<Utc>>
DateTime → 秒时间戳dt.timestamp()返回i64
DateTime → 毫秒时间戳dt.timestamp_millis()返回i64
DateTime → 微秒时间戳dt.timestamp_micros()返回i64
DateTime → 纳秒时间戳dt.timestamp_nanos_opt()返回Option<i64>

2. 字符串 ↔ 时间对象

方向推荐函数说明
自定义格式 → NaiveDateTimeNaiveDateTime::parse_from_str(s, fmt)无时区
自定义格式 → DateTimeDateTime::parse_from_str(s, fmt)字符串需带时区
RFC3339 → DateTimeDateTime::parse_from_rfc3339(s)如2026-10-31T12:00:00+08:00
RFC2822 → DateTimeDateTime::parse_from_rfc2822(s)邮件/HTTP 场景常见
字符串 → DateTimes.parse::<DateTime<Utc>>()适合...Z结尾
时间 → 字符串dt.format(fmt).to_string()strftime 风格
时间 → RFC3339dt.to_rfc3339()API 常用
时间 → RFC2822dt.to_rfc2822()邮件/协议常用

3. Naive ↔ DateTime / 时区转换

方向推荐函数说明
NaiveDateTime → DateTimeUtc.from_utc_datetime(&naive)明确它是 UTC
NaiveDateTime → DateTimetz.from_local_datetime(&naive)返回MappedLocalTime
DateTime → NaiveDateTimedt.naive_utc()丢弃时区,取 UTC 字面值
DateTime → NaiveDateTimedt.naive_local()取本地字面值
时区切换dt.with_timezone(&tz)不改变绝对时间点
转 UTCdt.to_utc()得到DateTime<Utc>
转固定偏移dt.fixed_offset()得到DateTime<FixedOffset>

4. 标准库 ↔ chrono

方向推荐函数说明
SystemTime → DateTimeDateTime::<Utc>::from(st)直接转换
DateTime → SystemTimedt.into()直接转换
SystemTime → 秒数st.duration_since(UNIX_EPOCH)返回Result<Duration>
秒数 → SystemTimeUNIX_EPOCH + Duration::from_secs(secs)手动构造

5. 日期/时间片段的组合

方向推荐函数说明
NaiveDate → NaiveDateTimedate.and_time(time)组合日期和时间
NaiveDateTime → NaiveDatedt.date()取日期部分
NaiveDateTime → NaiveTimedt.time()取时间部分
NaiveDateTime → DateTimenaive.and_utc()明确按 UTC 解释
NaiveDateTime → DateTimenaive.and_local_timezone(Local)可能歧义或缺失

6. 建议少用或避免的写法

  • NaiveDateTime::from_timestamp*:已弃用,优先用DateTime::from_timestamp*。
  • DateTime::from_utc/DateTime::from_local:旧 API,优先用from_naive_utc_and_offset、from_utc_datetime、from_local_datetime。
  • timestamp_nanos():可能溢出,优先用timestamp_nanos_opt()。
  • 解析外部时间直接unwrap():应走?、match或ok()?。

工程化小约定

  • 存储、日志、跨服务传输统一用DateTime<Utc>。
  • 用户输入、前端展示、本地报表再做时区转换。
  • 所有解析入口统一包一层Result<DateTime<Utc>, AppTimeError>,避免每个 handler 重复写格式判断。

需要我提供一段统一的 DateTime 解析封装代码示例,方便你直接集成到项目里吗?