【Chrono库】TimeDelta 模块深度解析(time_delta.rs)

Chrono

模块概述

这是 Chrono 库的核心时间间隔处理模块,提供了高精度、有符号的时间持续时间处理功能。TimeDelta 类型替代了原有的 Duration 名称,提供更准确的语义表达。

核心架构设计

TimeDelta 结构体定义

pub struct TimeDelta {
    secs: i64,   // 秒数部分(有符号)
    nanos: i32,  // 纳秒部分,始终满足 0 <= nanos < NANOS_PER_SEC
}

设计特点

  • 双字段存储:秒数 + 纳秒,避免浮点数精度问题
  • 有符号支持:支持正负时间间隔
  • 范围限制:纳秒部分严格规范在 0-999,999,999 范围内

范围边界定义

// 最小时间间隔:-i64::MAX 毫秒
pub(crate) const MIN: TimeDelta = TimeDelta {
    secs: -i64::MAX / MILLIS_PER_SEC - 1,
    nanos: NANOS_PER_SEC + (-i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
};

// 最大时间间隔:i64::MAX 毫秒  
pub(crate) const MAX: TimeDelta = TimeDelta {
    secs: i64::MAX / MILLIS_PER_SEC,
    nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
};

重要设计决策:使用 -i64::MAX 而不是 i64::MIN 作为最小值,这样可以安全地进行绝对值操作而不会溢出。

构造函数系统

安全构造模式

// 基础构造函数 - 返回 Option 进行错误处理
pub const fn new(secs: i64, nanos: u32) -> Option<TimeDelta> {
    if secs < MIN.secs
        || secs > MAX.secs
        || nanos >= 1_000_000_000
        || (secs == MAX.secs && nanos > MAX.nanos as u32)
        || (secs == MIN.secs && nanos < MIN.nanos as u32)
    {
        return None;
    }
    Some(TimeDelta { secs, nanos: nanos as i32 })
}

时间单位构造器

双重API设计模式

  • try_* 方法:返回 Option<TimeDelta>,用于需要错误处理的场景
  • 直接方法:panic 在越界时,用于确定不会越界的场景
// 示例:周数构造
pub const fn weeks(weeks: i64) -> TimeDelta {
    expect(TimeDelta::try_weeks(weeks), "TimeDelta::weeks out of bounds")
}

pub const fn try_weeks(weeks: i64) -> Option<TimeDelta> {
    TimeDelta::try_seconds(try_opt!(weeks.checked_mul(SECS_PER_WEEK)))
}

支持的时间单位

  • 周(weeks)
  • 天(days)
  • 小时(hours)
  • 分钟(minutes)
  • 秒(seconds)
  • 毫秒(milliseconds)
  • 微秒(microseconds)
  • 纳秒(nanoseconds)

数值提取方法

整数部分提取

// 提取整周数
pub const fn num_weeks(&self) -> i64 {
    self.num_days() / 7
}

// 提取整天数
pub const fn num_days(&self) -> i64 {
    self.num_seconds() / SECS_PER_DAY
}

// 核心:提取整秒数(处理负数的纳秒部分)
pub const fn num_seconds(&self) -> i64 {
    if self.secs < 0 && self.nanos > 0 { 
        self.secs + 1 
    } else { 
        self.secs 
    }
}

小数部分提取

// 纳秒小数部分
pub const fn subsec_nanos(&self) -> i32 {
    if self.secs < 0 && self.nanos > 0 { 
        self.nanos - NANOS_PER_SEC 
    } else { 
        self.nanos 
    }
}

// 浮点数表示
pub fn as_seconds_f64(self) -> f64 {
    self.secs as f64 + self.nanos as f64 / NANOS_PER_SEC as f64
}

数学运算实现

安全检查的算术运算

// 加法运算
pub const fn checked_add(&self, rhs: &TimeDelta) -> Option<TimeDelta> {
    let mut secs = self.secs + rhs.secs;
    let mut nanos = self.nanos + rhs.nanos;
    if nanos >= NANOS_PER_SEC {
        nanos -= NANOS_PER_SEC;
        secs += 1;
    }
    TimeDelta::new(secs, nanos as u32)
}

// 乘法运算(使用i128防止溢出)
pub const fn checked_mul(&self, rhs: i32) -> Option<TimeDelta> {
    let total_nanos = self.nanos as i64 * rhs as i64;
    let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
    let secs: i128 = self.secs as i128 * rhs as i128 + extra_secs as i128;
    if secs <= i64::MIN as i128 || secs >= i64::MAX as i128 {
        return None;
    };
    Some(TimeDelta { secs: secs as i64, nanos: nanos as i32 })
}

运算符重载

impl Add for TimeDelta {
    type Output = TimeDelta;

    fn add(self, rhs: TimeDelta) -> TimeDelta {
        self.checked_add(&rhs).expect("`TimeDelta + TimeDelta` overflowed")
    }
}

impl Neg for TimeDelta {
    type Output = TimeDelta;

    fn neg(self) -> TimeDelta {
        let (secs_diff, nanos) = match self.nanos {
            0 => (0, 0),
            nanos => (1, NANOS_PER_SEC - nanos),
        };
        TimeDelta { secs: -self.secs - secs_diff, nanos }
    }
}

标准库互操作性

与 std::time::Duration 转换

// 从标准库 Duration 转换
pub const fn from_std(duration: Duration) -> Result<TimeDelta, OutOfRangeError> {
    if duration.as_secs() > MAX.secs as u64 {
        return Err(OutOfRangeError(()));
    }
    match TimeDelta::new(duration.as_secs() as i64, duration.subsec_nanos()) {
        Some(d) => Ok(d),
        None => Err(OutOfRangeError(())),
    }
}

// 转换为标准库 Duration(仅支持非负值)
pub const fn to_std(&self) -> Result<Duration, OutOfRangeError> {
    if self.secs < 0 {
        return Err(OutOfRangeError(()));
    }
    Ok(Duration::new(self.secs as u64, self.nanos as u32))
}

格式化输出

ISO 8601 持续时间格式

impl fmt::Display for TimeDelta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
        
        write!(f, "{sign}P")?;
        if abs.secs == 0 && abs.nanos == 0 {
            return f.write_str("0D");
        }

        f.write_fmt(format_args!("T{}", abs.secs))?;
        
        if abs.nanos > 0 {
            // 去除尾随零,只显示有效数字
            let mut figures = 9usize;
            let mut fraction_digits = abs.nanos;
            loop {
                let div = fraction_digits / 10;
                let last_digit = fraction_digits % 10;
                if last_digit != 0 { break; }
                fraction_digits = div;
                figures -= 1;
            }
            f.write_fmt(format_args!(".{fraction_digits:0figures$}"))?;
        }
        f.write_str("S")?;
        Ok(())
    }
}

输出示例

  • P0D - 零持续时间
  • PT42S - 42秒
  • PT0.042S - 42毫秒
  • -PT86401S - 负86401秒

特性集成

序列化支持(Serde)

#[cfg(feature = "serde")]
impl Serialize for TimeDelta {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        <(i64, i32) as Serialize>::serialize(&(self.secs, self.nanos), serializer)
    }
}

零拷贝序列化(Rkyv)

#[cfg(any(feature = "rkyv", feature = "rkyv-16", ...))]
#[derive(Archive, Deserialize, Serialize)]
pub struct TimeDelta {
    secs: i64,
    nanos: i32,
}

测试数据生成(Arbitrary)

#[cfg(all(feature = "arbitrary", feature = "std"))]
impl arbitrary::Arbitrary<'_> for TimeDelta {
    fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<TimeDelta> {
        // 生成在有效范围内的随机 TimeDelta
    }
}

错误处理

范围错误类型

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutOfRangeError(());

impl fmt::Display for OutOfRangeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Source duration value is out of range for the target type")
    }
}

工具函数

除法工具函数

#[inline]
const fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
    (this.div_euclid(other), this.rem_euclid(other))
}

设计哲学总结

  1. 安全性优先:所有操作都进行边界检查,防止溢出
  2. 性能优化:使用常量函数,编译时计算
  3. 类型安全:清晰的错误处理路径
  4. 互操作性:与标准库和其他序列化框架良好集成
  5. 用户体验:提供 panic 和非 panic 两种API选择

这个模块为 Rust 提供了工业级的时间间隔处理能力,特别适合需要高精度时间计算和跨平台一致性的应用场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuyuan77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值