鸿蒙PC适配llvm-gcc-compat编译安装第三方库chrono,打造Rust 第三方日期时间处理库
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_rust_cargo
本文讲解鸿蒙 PC 端 Rust 开发环境搭建,鸿蒙基于 musl 库、强制二进制签名,无法直接使用通用 Linux 编译产物。需借助鸿蒙专属包管理器 Harmonybrew,提供两套编译方案:方案一安装 llvm-gcc-compat,零配置开箱即用;方案二仅安装 ohos-sdk,需手动配置 Cargo 链接器,二者都依托 ohos-sdk 完成自动签名编译。
可以来参考一下这个文章搭建环境OpenHarmony 鸿蒙 PC + CodeArts IDE 实现 Rust开发完整开发环境搭建指南
一、chrono 是什么
chrono是 Rust 生态最主流、功能完整的日期时间处理库,替代标准库单薄的std::time/SystemTime。
标准库时间缺陷:时区支持差、格式化繁琐、缺少日期加减、星期/月份便捷方法、无法简单解析日期字符串;
chrono 完整补齐所有时间业务需求,是 Rust 后端、CLI、数据处理项目标配。
核心作用
- 支持本地时间、UTC时间、带时区时间,时区计算完善
- 日期快速加减:年/月/日/时/分/秒,自动处理月末、闰年边界
- 强大格式化 + 字符串解析,自定义格式模板
- 获取星期、季度、月初月末、年份首尾、时间戳互转
- 时间差值计算(计算相隔几天、几小时)
- 支持序列化(搭配 serde 存JSON/数据库)
- 兼容时间戳:秒、毫秒、微秒、纳秒互相转换
二、安装依赖
1. 新建项目(已有项目跳过)
cargonew chrono_democdchrono_demo2. 添加 chrono 依赖
# 基础版本(本地/UTC时间,格式化)cargoaddchrono# 如果需要时区支持(拓展)cargoaddchrono--featuresclock,timezone# 如果需要serde序列化(接口/数据库必用)cargoaddchrono--featuresserde打开Cargo.toml自动生成依赖:
[dependencies] chrono = { version = "0.4", features = ["clock", "timezone", "serde"] }三、完整无报错 main.rs 示例
覆盖绝大多数业务场景:当前时间、格式化、解析字符串、日期偏移、时间戳、差值、月初月末、时区、serde序列化
usechrono::{DateTime,Datelike,Local,Months,NaiveDate,NaiveDateTime,NaiveTime,TimeDelta,Utc,};fnmain(){// ===================== 1. 获取当前时间 =====================println!("===== 1. 当前本地时间 / UTC 时间 =====");letnow_local:DateTime<Local>=Local::now();letnow_utc:DateTime<Utc>=Utc::now();println!("本地完整时间: {}",now_local);println!("UTC 标准时间: {}",now_utc);// 只获取日期/只获取时间(无时区裸时间)lettoday:NaiveDate=Local::now().date_naive();letnow_time:NaiveTime=Local::now().time();println!("今日日期: {}",today);println!("当前时刻: {}",now_time);// ===================== 2. 时间格式化 =====================println!("\n===== 2. 自定义格式化输出 =====");letfmt_str="%Y-%m-%d %H:%M:%S";println!("标准格式: {}",now_local.format(fmt_str));println!("仅年月日: {}",now_local.format("%Y-%m-%d"));println!("中文格式: {}",now_local.format("%Y年%m月%d日 %H:%M:%S"));println!("星期: {}",now_local.format("%A"));// ===================== 3. 字符串解析成时间 =====================println!("\n===== 3. 字符串解析为日期时间 =====");letdate_text="2026-08-15 14:30:00";matchNaiveDateTime::parse_from_str(date_text,fmt_str){Ok(dt)=>println!("解析成功: {}",dt),Err(e)=>eprintln!("解析失败: {}",e),}letday_text="2026-10-01";letholiday=NaiveDate::parse_from_str(day_text,"%Y-%m-%d").unwrap();println!("解析纯日期: {}",holiday);// ===================== 4. 日期加减 =====================println!("\n===== 4. 日期偏移 加减年/月/日/时/分 =====");lettomorrow=today+TimeDelta::days(1);letlast_month=today-TimeDelta::days(30);letafter_2_hour=Local::now()+TimeDelta::hours(2);println!("明天: {}",tomorrow);println!("30天前: {}",last_month);println!("2小时后: {}",after_2_hour.format(fmt_str));// 按月偏移letnext_month=today.checked_add_months(Months::new(1)).unwrap();println!("下个月今日: {}",next_month);// ===================== 5. 时间戳互转 =====================println!("\n===== 5. 时间戳转换 =====");letnow=Utc::now();letts_sec=now.timestamp();letts_ms=now.timestamp_millis();println!("UTC 秒级时间戳: {}",ts_sec);println!("UTC 毫秒时间戳: {}",ts_ms);letfrom_sec=DateTime::from_timestamp(ts_sec,0).unwrap();println!("时间戳还原UTC时间: {}",from_sec);// ===================== 6. 计算两个时间差值 =====================println!("\n===== 6. 计算时间间隔 =====");letstart=NaiveDate::from_ymd_opt(2026,1,1).unwrap();letend=NaiveDate::from_ymd_opt(2026,12,31).unwrap();letdiff=end-start;println!("2026全年相差天数: {} 天",diff.num_days());println!("相差小时数: {} h",diff.num_hours());// ===================== 7. 获取月初、月末 =====================println!("\n===== 7. 当月第一天 / 当月最后一天 =====");letcurrent_month_first=today.with_day(1).unwrap();letnext_month_first=current_month_first.checked_add_months(Months::new(1)).unwrap();letcurrent_month_last=next_month_first-TimeDelta::days(1);println!("本月一号: {}",current_month_first);println!("本月最后一天: {}",current_month_last);}四、运行
cargorunchrono是 Rust 生态最主流、功能最全的日期时间处理库,替代标准库简陋的时间工具,支持时区、格式化、解析、时间偏移、时间戳、日期差值、月初月末等业务常用能力。
前置配置
先在Cargo.toml引入依赖(推荐开启本地时区、月份运算功能)
[dependencies] chrono = { version = "0.4", features = ["local", "months"] }导入包中核心类型:
usechrono::{DateTime,Datelike,Local,Months,NaiveDate,NaiveDateTime,NaiveTime,TimeDelta,Utc,};先区分核心概念(看懂下面代码的关键)
- 带时区时间:
DateTime<Local>本地时区、DateTime<Utc>UTC零时区,包含时区信息,可直接格式化、转时间戳 - 裸时间(无时区):
NaiveDate:只有年月日,无时分秒、无时区NaiveTime:只有时分秒,无年月日、无时区NaiveDateTime:年月日+时分秒,没有时区,仅存储纯时间数字
TimeDelta:时间间隔,用来做时间加减(天/时/分/秒/毫秒)Months:专门按月偏移(解决2月、大小月天数不一致问题,TimeDelta只能按固定天数偏移)Dateliketrait:提供年月日、星期、当月第几天等日期取值方法
1. 获取当前时间
letnow_local:DateTime<Local>=Local::now();letnow_utc:DateTime<Utc>=Utc::now();println!("本地完整时间: {}",now_local);println!("UTC 标准时间: {}",now_utc);// 只提取裸日期 / 裸时刻lettoday:NaiveDate=Local::now().date_naive();letnow_time:NaiveTime=Local::now().time();println!("今日日期: {}",today);println!("当前时刻: {}",now_time);详解
Local::now():获取操作系统本地时区当前完整时间(东八区北京时间)Utc::now():获取零时区标准时间,服务器存储时间戳优先用UTC,避免时区混乱.date_naive():从带时区时间中剥离时区,只保留年月日NaiveDate.time():剥离日期,只保留时分秒NaiveTime
输出示例
本地完整时间: 2026-06-18 15:20:30.123456 +08:00 UTC 标准时间: 2026-06-18 07:20:30.123456 UTC 今日日期: 2026-06-18 当前时刻: 15:20:30.1234562. 时间格式化输出
letfmt_str="%Y-%m-%d %H:%M:%S";println!("标准格式: {}",now_local.format(fmt_str));println!("仅年月日: {}",now_local.format("%Y-%m-%d"));println!("中文格式: {}",now_local.format("%Y年%m月%d日 %H:%M:%S"));println!("星期: {}",now_local.format("%A"));格式化占位符常用对照表
| 占位符 | 含义 | 示例 |
|---|---|---|
%Y | 4位年份 | 2026 |
%m | 两位月份 | 06 |
%d | 两位日期 | 18 |
%H | 24小时制小时 | 15 |
%M | 分钟 | 20 |
%S | 秒 | 30 |
%A | 完整英文星期 | Thursday |
%w | 数字星期(0=周日) | 4 |
说明
.format(模板字符串)支持带时区DateTime和裸时间NaiveDateTime;可以自由拼接中文,非常适合前端展示、日志打印。
3. 字符串解析成时间
letdate_text="2026-08-15 14:30:00";matchNaiveDateTime::parse_from_str(date_text,fmt_str){Ok(dt)=>println!("解析成功: {}",dt),Err(e)=>eprintln!("解析失败: {}",e),}letday_text="2026-10-01";letholiday=NaiveDate::parse_from_str(day_text,"%Y-%m-%d").unwrap();println!("解析纯日期: {}",holiday);核心逻辑
parse_from_str(待解析字符串, 格式化模板):模板必须和字符串格式完全匹配,否则返回解析错误。
NaiveDateTime::parse_from_str:解析「年月日 时分秒」完整字符串NaiveDate::parse_from_str:只解析纯日期,不带时分秒
错误处理
返回Result<T, ParseError>:
match分支:生产环境推荐,优雅处理格式错误.unwrap():测试、确定字符串格式合法时快速取值,格式不对程序直接panic
4. 日期时间加减偏移
4.1 TimeDelta 固定时长偏移(天/时/分/秒)
lettomorrow=today+TimeDelta::days(1);letlast_month=today-TimeDelta::days(30);letafter_2_hour=Local::now()+TimeDelta::hours(2);println!("明天: {}",tomorrow);println!("30天前: {}",last_month);println!("2小时后: {}",after_2_hour.format(fmt_str));TimeDelta代表固定时长,支持:
TimeDelta::days(n)天TimeDelta::hours(n)小时TimeDelta::minutes(n)分钟TimeDelta::seconds(n)秒
4.2 Months 按月自然偏移(推荐处理月份)
letnext_month=today.checked_add_months(Months::new(1)).unwrap();println!("下个月今日: {}",next_month);关键区别(重点)
TimeDelta::days(30):强制固定30天,遇到大小月、2月会出错
例:1月31日 +30天 = 3月2日,不符合“下个月同一天”业务需求checked_add_months(Months::new(1)):自然按月跳转
1月31日 → 2月28/29日,3月31日→4月30日,完美适配日历逻辑
checked_add_months返回Option:月末跨月无对应日期返回None,.unwrap()适合确定合法场景
5. 时间戳互转(前后端交互核心)
letnow=Utc::now();letts_sec=now.timestamp();letts_ms=now.timestamp_millis();println!("UTC 秒级时间戳: {}",ts_sec);println!("UTC 毫秒时间戳: {}",ts_ms);letfrom_sec=DateTime::from_timestamp(ts_sec,0).unwrap();println!("时间戳还原UTC时间: {}",from_sec);方法说明
.timestamp():输出秒级时间戳(i64),后端接口通用.timestamp_millis():输出毫秒时间戳,前端JS常用DateTime::from_timestamp(秒, 纳秒):秒级时间戳转回带时区UTC时间
第二个参数是纳秒补充,不需要填0即可
最佳实践
数据库存储、接口传输一律使用UTC时间戳,避免本地时区导致的时差bug。
6. 计算两个时间差值
letstart=NaiveDate::from_ymd_opt(2026,1,1).unwrap();letend=NaiveDate::from_ymd_opt(2026,12,31).unwrap();letdiff=end-start;println!("2026全年相差天数: {} 天",diff.num_days());println!("相差小时数: {} h",diff.num_hours());流程拆解
NaiveDate::from_ymd_opt(年,月,日):安全构造日期,返回Option<NaiveDate>,非法日期(2月30日)返回None- 两个同类型时间相减,得到
TimeDelta差值对象 - 差值取值方法:
.num_days():总相差天数.num_hours():总相差小时.num_minutes()/.num_seconds():总分钟、总秒数
适用场景
倒计时、活动剩余时长、计算年龄、统计间隔天数。
7. 获取当月第一天、当月最后一天
letcurrent_month_first=today.with_day(1).unwrap();letnext_month_first=current_month_first.checked_add_months(Months::new(1)).unwrap();letcurrent_month_last=next_month_first-TimeDelta::days(1);println!("本月一号: {}",current_month_first);println!("本月最后一天: {}",current_month_last);逻辑分步拆解
.with_day(1):把当前日期强制改为当月1号,得到本月首日- 本月1号 +1个月 → 下个月1号
- 下个月1号 减1天 → 本月最后一天
优势
自动兼容28/29天2月、31天大月、30天小月,不用手动判断月份天数,报表统计、月度筛选高频使用。
关键类型总结 & 使用建议
什么时候用 Naive(裸时间)
- 只做本地日期计算、报表统计、不需要跨时区传输
- 本地文件存储、仅程序内部计算
什么时候用 DateTime/DateTime
- 和前端、数据库交互、生成时间戳
- 需要格式化展示本地时间、处理时区转换
加减选择
- 固定时长(往后推2小时、7天)→
TimeDelta - 自然月/年(下个月、去年今日)→
Months
安全编码小提示
- 构造日期、月份偏移优先用
xxx_opt+ match 处理None,少用unwrap上线 - 对外接口时间统一用UTC时间戳,避免时区混乱
- 解析用户输入日期必须捕获解析错误,防止非法格式panic
五、核心模块与类型说明
1. 基础时间类型
| 类型 | 含义 | 使用场景 |
|---|---|---|
NaiveDate | 纯日期,无时间、无时区 | 生日、订单创建日 |
NaiveTime | 纯时刻,无日期、无时区 | 固定每日执行时刻 |
NaiveDateTime | 日期+时刻,无时区 | 数据库不带时区存储 |
DateTime<Local> | 带本地时区完整时间 | 展示给前端本地时间 |
DateTime<Utc> | UTC零时区时间 | 服务统一存储、时间戳 |
2. 关键结构体
TimeDelta:时间差值,days/hours/minutes/secondsMonths:按月偏移,自动适配大小月、闰年
3. 格式化模板常用占位符
%Y4位年份2026%m两位月份01~12%d两位日期01~31%H24小时制00~23%M分钟00~59%S秒%A完整星期名称 Monday%w数字星期 0=周日
六、常用Feature功能说明
- clock(默认开启)
Local::now()/Utc::now()获取系统当前时间,关闭则无法获取实时时间。 - timezone
拓展时区库,支持Asia/Shanghai等完整IANA时区,用于跨国系统。 - serde
实现Serialize/Deserialize,时间结构体直接转JSON,Web后端必备。 - rkyv / sqlx
额外feature适配数据库ORM(sqlx、diesel)直接存取时间字段。
七、典型业务开发场景
- Web后端接口
UTC时间存库,本地时区格式化返回前端,JSON序列化时间 - 定时任务计算
计算下月执行日、每月1号、距离过期剩余天数 - 日志系统
日志打印格式化时间、计算请求耗时(两个时间差值) - 数据库业务
生日、订单创建时间、活动起止时间校验 - 爬虫/数据同步
时间戳转换、按日期分段拉取历史数据
八、优势对比标准库std::time
- 标准库
SystemTime只支持时间戳,无日期、月份、星期操作; - chrono自动处理闰年、2月、月末边界,不用手动判断;
- 统一格式化/解析模板,不用手写复杂转换;
- 原生支持时区,解决前后端时区错乱问题;
- 生态兼容serde、sqlx、axum、rocket等主流框架。
