一、设计原则1.1 核心分类时间单位按数学/历法本质分为两类类别单位特性历法单位年、月长度不固定依赖锚点闰年、大小月但年 ↔ 月固定倍率 12物理单位周、日、时、分、秒、毫秒长度固定日≈86400秒忽略闰秒以毫秒为基底互转精确1.2 关键结论时间点Timestamp和时间段Duration语义不同必须分开维护年↔月精确转换×12 / ÷12月↔日依赖锚点查日历表无锚点时无意义日↔周/时/分/秒/毫秒固定倍率精确转换月或年 日或更小单位混合作为时间段禁止因为日依赖于月的实际天数无锚定时毫无意义月或年单独作为时间段允许用于计划/心理预期场景如3个月后交付锚定后自动精确化所有时间值统一用i64毫秒存储确保统一、精确、无歧义。锚点2000-03-01之前的日期用负数表示。二、数据结构定义2.1 物理单位常量固定倍率constMS_PER_SEC:i641_000;constSEC_PER_MIN:i6460;constMIN_PER_HOUR:i6460;constHOUR_PER_DAY:i6424;constDAY_PER_WEEK:i647;constMS_PER_DAY:i6486_400_000;constMS_PER_HOUR:i643_600_000;constMS_PER_MIN:i6460_000;constMS_PER_SEC:i641_000;2.2 结构体定义1时间点Timestamp/// 时间点从固定锚点2000年3月1日0时起算的毫秒偏移/// - 可正可负锚点之前为负锚点之后为正/// - 提供 年/月/日/时/分/秒/毫秒 的视图转换/// - 提供 天数↔月数 转换依赖锚点精确#[derive(Copy, Clone, Debug, PartialEq, Eq)]pubstructTimestamp{ms:i64,// 从锚点起算的毫秒数}2精确时间段Duration/// 精确时间段以毫秒为单位的连续时间长度/// - 可正可负/// - 仅提供物理单位天/时/分/秒/毫秒的视图/// - ❌ 不提供月视图无物理意义#[derive(Copy, Clone, Debug, PartialEq, Eq)]pubstructDuration{ms:i64,// 持续毫秒数}3粗略时间段RoughDuration/// 粗略时间段以年、月为单位的计划/心理预期时间长度/// - 仅用于计划场景如3个月后交付/// - 可正可负/// - ❌ 不混入日或更小单位因为无锚定时无意义/// - 锚定到具体时间点后通过日历表转换为精确的 Timestamp#[derive(Copy, Clone, Debug, PartialEq, Eq)]pubstructRoughDuration{months:i64,// 月数年可通过 months/12 获得}三、结构体职责对照表结构体存储单位语义可否为负提供月视图提供日视图运算规则Timestamp毫秒i64从锚点起算的绝对时刻✅锚点前为负✅依赖锚点精确✅依赖锚点精确只能与 Duration 或 RoughDuration 运算Duration毫秒i64精确时间段长度✅❌✅固定倍率可与 Duration 任意运算RoughDuration月i64粗略时间段长度计划/心理预期✅✅本身❌可与 RoughDuration 进行、-运算仅计划层面可与 Timestamp 进行、-运算锚定精确化四、运算规则4.1 允许的运算左操作数运算符右操作数结果说明Timestamp/-DurationTimestamp精确时间偏移可逆Timestamp-TimestampDuration计算两个时刻的精确差值Timestamp/-RoughDurationTimestamp锚定粗略月数转为精确时间点不可逆见 4.4Duration/-DurationDuration精确时间段叠加Duration*//i64Duration标量倍乘RoughDuration/-RoughDurationRoughDuration粗略月数叠加仅用于计划不涉及精确计算RoughDuration*//i64RoughDuration标量倍乘仅用于计划4.2 禁止的运算左操作数运算符右操作数原因DurationRoughDuration物理单位与历法单位混合无意义RoughDurationDuration同上Duration-RoughDuration同上TimestampTimestamp两个绝对时刻相加无意义Durationi64类型不匹配需显式构造4.3 关键运算示例// 精确偏移可逆无警告 letts2ts1Duration::from_days(30);letts3ts2-Duration::from_days(30);assert_eq!(ts1,ts3);// ✅ 精确可逆// 月偏移不可逆 无警告- 有警告 letts2ts1RoughDuration::from_months(1);// ✅ 无警告// ts1 2026-03-31 → ts2 2026-05-01letts3ts2-RoughDuration::from_months(1);// ⚠️ 编译警告// ts3 2026-04-01不等于 ts1 (2026-03-31)// 警告月偏移不可逆- 不是 的逆运算。// ✅ 推荐写法用 配合负数letts3ts1RoughDuration::from_months(-1);// 无警告// ts1 2026-03-31 → ts3 2026-03-01// ✅ 两个时间点相减得精确差值无警告letdurts2-ts1;// Duration毫秒精确差值// ❌ 禁止时间段 粗略月数编译错误// let bad Duration::from_days(30) RoughDuration::from_months(1);4.4 ⚠️ 月偏移的不可逆性与#[deprecated]标记问题根源历法月长度不固定28/29/30/31天月偏移的运算依赖于当前锚点。和-的锚点不同因此不可逆。运算锚点结果2026-03-31 1个月2026-03-312026-05-012026-05-01 - 1个月2026-05-01已改变2026-04-01两次运算的锚点不同因此不可逆。设计决策为提醒开发者注意此不可逆性对月偏移的-运算符添加#[deprecated]标记implstd::ops::SubRoughDurationforTimestamp{typeOutputSelf;#[deprecated( note 月偏移不可逆ts - n.months() 不是 ts n.months() 的逆运算。\ 由于历法月长度不固定反向偏移不一定回到原点。\ 建议使用 ts (-n) 替代。)]fnsub(self,rhs:RoughDuration)-Self{// 查日历表向前偏移 rhs.months 个月selfRoughDuration{months:-rhs.months}}}效果用法编译结果说明ts RoughDuration✅ 无警告日常使用流畅体验ts - RoughDuration⚠️#[deprecated]警告提醒不可逆引导使用负数ts RoughDuration::from_months(-n)✅ 无警告推荐替代方案ts Duration✅ 无警告精确可逆放心使用ts - Duration✅ 无警告精确可逆放心使用开发者体验// 第一次使用 - 时看到警告lett3t2-RoughDuration::from_months(1);// warning: use of deprecated operator// note: 月偏移不可逆- 不是 的逆运算。// 建议使用 ts RoughDuration::from_months(-n) 替代。// 理解后可选择// 方案A改用推荐写法无警告lett3t2RoughDuration::from_months(-1);// 方案B明确知晓风险后继续使用添加 allow#[allow(deprecated)]lett3t2-RoughDuration::from_months(1);设计意图总结用于向后偏移正向符合直觉无警告-虽在语法上支持但通过#[deprecated]在编译期发出提醒推荐统一使用 正负数来表达双向偏移避免-带来的可逆性错觉Timestamp ± Duration物理单位不受影响保持精确可逆五、月年的两种语境语境对应类型精度用途计划/心理预期无锚点RoughDuration约30天/月允许误差项目排期、合同条款、薪资周期日历视图有锚点Timestamp的视图方法精确到天/毫秒日期显示、历史计算、时间轴对齐5.1 从计划到精确的转换流程RoughDuration3个月 ↓ 锚定到具体时间点如 2026-07-08 00:00:00 ↓ 查日历表7月8日 3个月 10月8日实际日历月 ↓ Timestamp精确到毫秒六、实现要点6.1 Timestamp 核心方法implTimestamp{/// 从锚点2000年3月1日0时起算pubfnfrom_ms(ms:i64)-Self{Self{ms}}/// 转换为日历月数从3月起算1-indexed/// 使用精确分数 48699/1600 回推修正pubfnto_calendar_months(self)-i64{// 实现略}/// 转换为公历日期年/月/日/时/分/秒/毫秒pubfnto_ymd_hms(self)-(i64,i64,i64,i64,i64,i64,i64){// 实现略}}6.2 Duration 核心方法implDuration{pubfnfrom_ms(ms:i64)-Self{Self{ms}}pubfnfrom_days(days:i64)-Self{Self{ms:days*MS_PER_DAY}}pubfnto_days(self)-i64{self.ms/MS_PER_DAY}pubfnto_ms(self)-i64{self.ms}// ❌ 无 to_months()}6.3 RoughDuration 核心方法implRoughDuration{pubfnfrom_months(months:i64)-Self{Self{months}}pubfnfrom_years(years:i64)-Self{Self{months:years*12}}pubfnto_months(self)-i64{self.months}pubfnto_years(self)-i64{self.months.div_euclid(12)}/// 取负用于 配合负数表达反向偏移pubfnneg(self)-Self{Self{months:-self.months}}/// 锚定到具体时间点返回精确时间点pubfnanchor(self,start:Timestamp)-Timestamp{start*self// 使用已实现的 AddRoughDuration}// ❌ 无 to_days() 方法}七、错误模式禁止事项错误模式示例正确替代Duration提供月视图dur.to_months()❌ 不提供如需月数请锚定到TimestampRoughDuration混入天数RoughDuration { months: 3, days: 5 }拆分为RoughDuration(3) 锚定后加Duration::from_days(5)Duration与RoughDuration相加dur rough先锚定rough得Timestamp再与dur运算将RoughDuration用于精确日历计算直接用 90天 表示 3个月用anchor()转为Timestamp后再计算认为Timestamp - RoughDuration是的逆运算let t3 t2 - 1.months();期望回到原点使用 RoughDuration::from_months(-1)或#[allow(deprecated)]明确知晓风险八、总结8.1 三类结构体对比结构体核心用途存储单位锚点依赖视图能力典型场景Timestamp表示绝对时刻毫秒i64固定锚点提供所有单位视图年/月/日/时/分/秒/毫秒时间点记录、日期计算、历史时间Duration表示精确时间长度毫秒i64无仅物理单位日/时/分/秒/毫秒时间差计算、超时设置、性能测量RoughDuration表示计划性时间长度月i64无仅历法单位年/月项目排期、合同期限、薪资周期8.2 核心约束约束编号约束内容违反后果1Duration与RoughDuration不能直接混合运算类型错误编译失败2RoughDuration只能与Timestamp运算通过锚定精确度丢失计算结果无意义3Timestamp之间只能相减得Duration不能相加语义错误两个绝对时刻相加无意义4月或年 日混合的时间段禁止出现无锚定时日无法对应具体月天数5Timestamp - RoughDuration带有#[deprecated]警告编译警告提醒不可逆引导使用配合负数核心设计时间点Timestamp、精确时间段Duration、粗略时间段RoughDuration三结构体分离设计统一毫秒存储明确禁止月日混合时间段。月偏移的运算符畅通无阻-运算符通过#[deprecated]在编译期提醒不可逆性引导开发者使用配合负数实现双向偏移。