【A11】时间单位与精度设计笔记

版本 :v1.0

状态 :i64 毫秒 + f64 计算已实现;i128 纳秒为计划扩展

核心结论:i64 毫秒保底,i128 纳秒扩展,f64 秒/毫秒/工程单位做临时计算。不设 i64 微秒层,不单独为 f64 建结构体。


一、核心结论

  • :国际基准,理论正统,但工程上太粗。
  • 毫秒:工程默认,精度够用,范围富余,接口通用。
  • 微秒:无独立价值,只是接口遗留,跳过。
  • 纳秒:用 i128 承载,精度和范围同时拉满。
  • 计算:f64 仅做临时计算,不建结构体计算,结果可转回 i64。
  • 兼容:不支持 i128 的场景,用高低位 i64 对接。
text 复制代码
f64 秒/分/时/天/年  →  计算层(时长、速率、统计、物理公式)
i64 毫秒           →  常规时间点(默认、存储、接口、EPC)
i128 纳秒          →  高精度扩展(微秒/纳秒、超长跨度)
高低位 i64         →  传输兼容(不支持 i128 的 JSON/SQLite/FFI)

二、为什么是毫秒

2.1 精度够用

  • GUI/网页:秒太粗,人眼可感知延迟在几十毫秒,毫秒够用。
  • EPC/业务:99% 场景事件间隔在毫秒级,毫秒覆盖。
  • 微秒级需求:真实占比极小,不值得单设一层。

2.2 范围富余

i64 毫秒范围:

text 复制代码
2^63 - 1 ≈ 9.22 × 10^18 ms

换算成年:

text 复制代码
9.22 × 10^18 / (1000 × 60 × 60 × 24 × 365.25)
≈ 2.92 × 10^8 年

±约 2.92 亿年 ,总跨度约 5.85 亿年

结论:i64 毫秒范围远超实际需要,富余量充足。

2.3 整数可靠

  • 无浮点漂移。
  • 可比较、可排序、可序列化、可回放。
  • 跨平台一致。
  • 大量现成 API、数据库、协议以毫秒为单位。

三、为什么跳过 i64 微秒

  • 精度不是质变:微秒只是毫秒和纳秒之间的过渡。
  • 范围反而更紧:i64 微秒约 ±29.2 万年,比 i64 毫秒小四个数量级,溢出概率更高。
  • 接口无优势:对方给毫秒/微秒/纳秒,转 i128 纳秒都是无损乘,微秒作为中间层不简化任何转换。
  • 多一层多一份成本:转换、舍入、序列化、心智负担都会增加。
  • 现实不需要两个 time 类型:默认毫秒,高精度直接上 i128。

所以:不引入 i64 微秒,保持 i64 毫秒为唯一常规时间单位。


四、为什么用 i128 纳秒做扩展

4.1 范围

i128 纳秒范围:

text 复制代码
2^127 - 1 ≈ 1.7 × 10^38 ns

换算成年:

text 复制代码
1.7 × 10^38 / (10^9 × 60 × 60 × 24 × 365.25)
≈ 5.4 × 10^21 年

远超宇宙年龄,范围几乎无限。

4.2 精度

纳秒精度,覆盖微秒、亚微秒、纳秒级应用。

4.3 对比 i64+i32 纳秒

方案 精度 范围 复杂度
i64 毫秒 毫秒 ±2.92 亿年
i64 微秒 微秒 ±29.2 万年 冗余
i64+i32 纳秒 纳秒 ±2920 亿年 需拆装
i128 纳秒 纳秒 约 5.4×10^21 年 低,单类型

结论:i128 纳秒单类型搞定,覆盖更广,接口更单一。


五、分层模型

类型 是否结构体 状态 用途
常规时间点 i64 毫秒 已实现 默认时间戳、存储、接口、EPC
高精度扩展 i128 纳秒 计划 微秒/纳秒应用、超长跨度
传输兼容 高低位 i64 否/按需 计划 不支持 i128 的 JSON/SQLite/FFI
计算 f64 秒/毫秒 已实现 时长、速率、统计、物理公式
工程计算单位 f64 秒/分/时/天/年 部分实现 直接换算,不建结构体

规则:

  • 默认用 i64 毫秒。
  • 需要亚毫秒或超长跨度,走 i128 纳秒。
  • 不支持 i128,拆高低位 i64。
  • 物理/统计计算用 f64。
  • 不设 i64 微秒。

六、整数与浮点的分工

  • 时间点 :整数(i64 毫秒 / i128 纳秒)。
    • 可比较、可排序、可序列化、无漂移。
  • 时长、速率、统计、物理公式 :浮点(f64 秒/毫秒/工程单位)。
    • 连续计算自然,但精度有限,不适合做权威时间点。
  • f64 不单独设结构体:只在计算入口/出口转换,允许按需转回时间点,但须显式、受控。

原因:

  • f64 只有 53 位有效位,大时间戳下 ULP 变大,精度退化。
  • 浮点累加会漂移。
  • 浮点相等性、排序、跨平台一致性都不可靠。

所以:整数做时间点,浮点做计算,且浮点不建类型;计算结果可按需转回整数时间点,但转换须显式、受控。


七、工程计算单位

工程计算中,时间通常以 秒、分、时、天、年 等为单位表达。这些单位不单独设置结构体 ,一律在计算时直接转换为 f64

7.1 转换常量

text 复制代码
1 秒   = 1 s
1 分   = 60 s
1 小时 = 3600 s
1 天   = 86400 s
1 年 = 365.2425 天(工程概数;具体日期须走日历库。)

7.2 年的特殊性

年不是固定时长,常见定义:

  • 格里高利 400 年平均年 :365.2425 天 = 31,556,952 秒。
    • 400 年 = 146,097 天,146,097 / 400 = 365.2425。
    • 与公历 400 年周期绝对一致;与回归年误差约 26 秒/年,约 3300 年差 1 天。
    • 工程概数默认用值
  • 儒略年 :365.25 天 = 31,557,600 秒。
    • 二进制简单,天文/工程历史常用。
    • 与公历差 0.0075 天/年,400 年累积差 3 天。
  • 回归年:约 365.2422 天,天文实际,不用于固定换算。
  • 日历年:365 或 366 天,随闰年变化,必须走日历库。

使用规则

  • 工程概数、按年规划:用 365.2425 天,误差有明确上界。
  • 具体年份、具体日期:必须走日历库,不得用平均年直接乘。
  • 同一系统内不得混用不同年定义。

7.3 实践建议

  • 定义常量:
rust 复制代码
const SECONDS_PER_MIN:  f64 = 60.0;
const SECONDS_PER_HOUR: f64 = 3600.0;
const SECONDS_PER_DAY:  f64 = 86400.0;
/// 格里高利 400 年平均年(工程概数)
/// 365.2425 天 × 86400 秒 = 31,556,952 秒
const SECONDS_PER_YEAR: f64 = 31_556_952.0;
  • 注意 f64 精度:极大跨度或需要精确日历计算时,保持整数或使用专门日历库。

八、i64 毫秒 ↔ i128 纳秒转换规则

必须提前定死:

8.1 方向

text 复制代码
i64 ms → i128 ns:ms as i128 * 1_000_000,无损
i128 ns → i64 ms:显式舍入(截断/四舍五入),处理溢出

8.2 溢出

  • i128 ns 转 i64 ms 超出范围,报错或饱和,不静默截断。

8.3 序列化

  • i64 ms 用整数。
  • i128 无原生 JSON/SQLite 支持,转字符串或拆两个 i64。

8.4 使用边界

  • 默认 i64 ms。
  • 只有明确需要亚毫秒精度或超长跨度,才走 i128。

九、已实现:duration_float.rs

9.1 定位

  • Duration 的浮点扩展,独立文件,不改动 Duration 主结构。
  • 内部始终 i64 毫秒 ,浮点只在入口和出口使用。
  • 不新建结构体,只通过 impl Duration 扩展。

9.2 接口

浮点构造(入口)

rust 复制代码
Duration::from_secs_f64(secs: f64) -> Self
Duration::from_mins_f64(mins: f64) -> Self
Duration::from_hours_f64(hours: f64) -> Self
Duration::from_days_f64(days: f64) -> Self

浮点访问器(出口)

rust 复制代码
Duration::as_millis_f64(&self) -> f64
Duration::as_secs_f64(&self) -> f64
Duration::as_mins_f64(&self) -> f64
Duration::as_hours_f64(&self) -> f64
Duration::as_days_f64(&self) -> f64

9.3 精度边界

范围 精度
≤ 2^53(约 9.01×10^15 ms) i64 ↔ f64 往返完全精确
> 2^53 最多丢失低 10 位,误差 < 1024 ms(约 1.024 秒)
对应精确范围 ±约 285 万年

结论:在 2^53 毫秒(约 285 万年)范围内往返零损失,远超 EPC 及一般工程需求。

9.4 可选后续

  • 年相关接口:from_years_f64 / as_years_f64,常量建议 SECONDS_PER_YEAR = 31_557_600.0(儒略年)。
  • 溢出策略:from_secs_f64 目前是 as i64 直接截断,极大值在 Rust 中是饱和转换,不会 panic,但语义上可考虑显式处理。
  • 往返测试已有,覆盖了 2^53 边界和极大值。

十、未来计划

  • i128 纳秒:作为高精度扩展层,内部计算用 i128,对外默认仍输出 i64 毫秒。
  • 高低位 i64 兼容:在不支持 i128 的 JSON/SQLite/FFI 中,将 i128 拆为两个 i64 传输,规则定死(高 64 位、低 64 位、字节序、符号处理)。
  • 年接口:按需补充平均年常量,统一用儒略年常量。
  • 不设 i64 微秒:保持两层时间点结构(i64 毫秒 + i128 纳秒)。

十一、一句话总结

秒是理论基准,毫秒是工程默认且富余量充足,i128 纳秒是扩展上限。整数做时间点;f64 只做临时计算,秒、分、时、天、年直接换算,不单独设结构体;微秒层跳过;不支持 i128 用高低位 i64 对接。duration_float.rs 已按此原则实现浮点入口/出口。


笔记完。

相关推荐
梦醒沉醉17 小时前
std1.97.1——cmp模块细览
rust
PC2005-cloud20 小时前
Rust学习笔记:错误处理——panic!、Result、Option与_运算符
rust
Amos_Web1 天前
Rspack 源码解析(十):Hash 与 Asset 生成
前端·rust·源码阅读
柯南46681 天前
【AI开发之Rust】第 12 课:并发模型与同步原语
rust·编程语言
音符犹如代码2 天前
RustFS 1.0.0 深度解析:GA 之后,它值得替代 MinIO 吗?
rust
qq_452396232 天前
第十三篇:《unsafe Rust 与 FFI:与 C 和系统调用交互》
rust
传奇开心果编程2 天前
【Rust入门练中学】 第1课:从零开始
开发语言·学习·rust