【time-rs】Date 结构体详解

1. 基本定义和设计

1.1 结构体定义

rust 复制代码
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Date {
    /// 位打包字段:包含年、序数日、是否为闰年
    // |     x      | xxxxxxxxxxxxxxxxxxxxx |       x       | xxxxxxxxx |
    // |   1 bit    |        21 bits        |     1 bit     |  9 bits   |
    // | 未使用     |         年份          |  是否为闰年? | 序数日    |
    value: NonZero<i32>,
}

位布局说明:

  • 年份 :21位(启用 large-dates 时为15位)
  • 是否为闰年:1位(快速判断)
  • 序数日:9位(1-366)
  • 使用 NonZero<i32> 确保 value 永远不会为零

1.2 日期范围常量

rust 复制代码
/// 最小有效年份
pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
    -999_999  // 启用 large-dates 特性
} else {
    -9999     // 默认范围
};

/// 最大有效年份
pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
    999_999
} else {
    9999
};

/// 重要常量
pub(crate) const UNIX_EPOCH: Self;  // 1970-01-01
pub const MIN: Self;                // 最小有效日期
pub const MAX: Self;                // 最大有效日期

2. 构造方法

2.1 多种日期表示方式

方法 描述 示例
from_calendar_date() 年-月-日 2019-01-01
from_ordinal_date() 年-序数日 2019-001
from_iso_week_date() ISO 年-周-工作日 2019-01-Tuesday
from_julian_day() 儒略日 2458485

2.2 示例代码

rust 复制代码
// 从年-月-日创建
Date::from_calendar_date(2019, Month::January, 1)?;

// 从年-序数日创建
Date::from_ordinal_date(2019, 1)?;

// 从ISO周日期创建
Date::from_iso_week_date(2019, 1, Weekday::Tuesday)?;

// 从儒略日创建
Date::from_julian_day(2_451_545)?;  // 2000-01-01

3. 访问和计算方法

3.1 基本属性访问

rust 复制代码
impl Date {
    /// 获取年份
    pub const fn year(self) -> i32 {
        self.value.get() >> 10
    }
    
    /// 获取月份
    pub const fn month(self) -> Month {
        // 优化算法:避免查表
        let ordinal = self.ordinal() as u32;
        let jan_feb_len = 59 + self.is_in_leap_year() as u32;
        
        // 计算月份
        let month = ((ordinal * 268 + 8031) >> 13) + month_adj;
        // ... 转换为 Month 枚举
    }
    
    /// 获取月份中的日
    pub const fn day(self) -> u8 {
        // 类似月份的优化计算
        let days_in_preceding_months = (month * 3917 - 3866) >> 7;
        (ordinal - days_in_preceding_months) as u8
    }
    
    /// 获取年份中的序数日
    pub const fn ordinal(self) -> u16 {
        (self.value.get() & 0x1FF) as u16  // 提取低9位
    }
}

3.2 周和星期相关

rust 复制代码
impl Date {
    /// 获取ISO周编号
    pub const fn iso_week(self) -> u8 {
        self.iso_year_week().1
    }
    
    /// 获取星期几
    pub const fn weekday(self) -> Weekday {
        match self.to_julian_day() % 7 {
            -6 | 1 => Weekday::Tuesday,
            // ... 其他映射
            _ => Weekday::Monday,
        }
    }
    
    /// 获取下一个/上一个工作日出现
    pub const fn next_occurrence(self, weekday: Weekday) -> Self
    pub const fn prev_occurrence(self, weekday: Weekday) -> Self
}

3.3 日期导航

rust 复制代码
impl Date {
    /// 获取下一天
    pub const fn next_day(self) -> Option<Self> {
        if self.ordinal() == days_in_year(self.year()) {
            // 跨年情况
            if self == Self::MAX { None }
            else { /* 下一年第一天 */ }
        } else {
            // 简单加一
            Self { value: self.value.get() + 1 }
        }
    }
    
    /// 获取前一天
    pub const fn previous_day(self) -> Option<Self> {
        if self.ordinal() == 1 {
            // 跨年情况
            if self == Self::MIN { None }
            else { /* 上一年最后一天 */ }
        } else {
            // 简单减一
            Self { value: self.value.get() - 1 }
        }
    }
}

4. 日期运算

4.1 安全运算方法

rust 复制代码
impl Date {
    /// 检查加法(防止溢出)
    pub const fn checked_add(self, duration: Duration) -> Option<Self> {
        let whole_days = duration.whole_days();
        let julian_day = self.to_julian_day().checked_add(whole_days as i32)?;
        Self::from_julian_day(julian_day).ok()
    }
    
    /// 检查减法
    pub const fn checked_sub(self, duration: Duration) -> Option<Self>
    
    /// 饱和加法(溢出时返回边界值)
    pub const fn saturating_add(self, duration: Duration) -> Self {
        self.checked_add(duration).unwrap_or(if duration.is_negative() {
            Self::MIN
        } else {
            Self::MAX
        })
    }
}

4.2 运算符重载

rust 复制代码
// 支持标准的运算符
impl Add<Duration> for Date { /* ... */ }
impl Sub<Duration> for Date { /* ... */ }
impl Sub for Date {  // 日期相减得到 Duration
    type Output = Duration;
    fn sub(self, other: Self) -> Duration {
        Duration::days((self.to_julian_day() - other.to_julian_day()) as i64)
    }
}

5. 转换和格式化

5.1 转换为其他表示

rust 复制代码
impl Date {
    /// 转换为年-月-日
    pub const fn to_calendar_date(self) -> (i32, Month, u8)
    
    /// 转换为年-序数日
    pub const fn to_ordinal_date(self) -> (i32, u16)
    
    /// 转换为ISO周日期
    pub const fn to_iso_week_date(self) -> (i32, u8, Weekday)
    
    /// 转换为儒略日
    pub const fn to_julian_day(self) -> i32
}

5.2 智能显示(SmartDisplay)

rust 复制代码
impl SmartDisplay for Date {
    type Metadata = DateMetadata;
    
    fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
        let (year, month, day) = self.to_calendar_date();
        
        // 计算年份显示宽度(考虑符号)
        let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4);
        let display_sign = if !(0..10_000).contains(&year) {
            year_width += 1;
            true
        } else { false };
        
        // 格式化宽度:年-月-日
        let formatted_width = year_width + 1 + 2 + 1 + 2; // YYYY-MM-DD
    }
}

6. 高级功能

6.1 日期部分替换

rust 复制代码
impl Date {
    /// 替换年份(自动处理闰日)
    pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
        let ordinal = self.ordinal();
        
        match (self.is_in_leap_year(), is_leap_year(year)) {
            // 闰日处理逻辑
            (true, false) if ordinal == 60 => Err(...), // 2月29日不存在
            (false, true) => /* 序数日加1 */,
            (true, false) => /* 序数日减1 */,
            _ => /* 直接替换 */
        }
    }
    
    /// 替换月份
    pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange>
    
    /// 替换日
    pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange>
}

6.2 创建日期时间

rust 复制代码
impl Date {
    /// 创建午夜时间的日期时间
    pub const fn midnight(self) -> PrimitiveDateTime {
        PrimitiveDateTime::new(self, Time::MIDNIGHT)
    }
    
    /// 创建指定时间的日期时间
    pub const fn with_time(self, time: Time) -> PrimitiveDateTime {
        PrimitiveDateTime::new(self, time)
    }
    
    /// 创建指定时分秒的日期时间
    pub const fn with_hms(self, hour: u8, minute: u8, second: u8)
        -> Result<PrimitiveDateTime, error::ComponentRange>
}

7. 特性支持

7.1 格式化(需 formatting 特性)

rust 复制代码
#[cfg(feature = "formatting")]
impl Date {
    /// 格式化日期
    pub fn format(self, format: &impl Formattable) -> Result<String, error::Format>
    
    /// 格式化到输出流
    pub fn format_into(self, output: &mut impl io::Write, format: &impl Formattable)
        -> Result<usize, error::Format>
}

7.2 解析(需 parsing 特性)

rust 复制代码
#[cfg(feature = "parsing")]
impl Date {
    /// 从字符串解析日期
    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse>
}

8. 设计亮点总结

特性 说明
位打包存储 年、闰年标志、序数日打包到32位整数
零成本抽象 大量 const 函数和内联优化
内存安全 使用 NonZero<i32> 避免零值
性能优化 避免查表,使用算术计算月份/日
范围检查 所有方法都有明确的范围验证
多种表示 支持日历、序数、ISO周、儒略日
完整运算 加法、减法、工作日计算等

8.1 性能优化示例

月份计算算法(不使用查表):

rust 复制代码
// 传统方法:查表(需要闰年表+月份累积表)
// 优化方法:纯算术计算
let month = ((ordinal * 268 + 8031) >> 13) + month_adj;

儒略日转换:

  • 使用优化的算法,避免浮点运算
  • 支持大范围年份(±999,999年)

8.2 错误处理

所有可能失败的操作都返回 Result,错误类型清晰:

rust 复制代码
pub enum error::ComponentRange {
    name: &'static str,      // 组件名称:"year", "month", "day"
    value: i64,              // 提供的值
    minimum: i64,            // 最小有效值
    maximum: i64,            // 最大有效值
    conditional_message: Option<&'static str>,
}

这是一个高度优化的日期实现,平衡了性能、内存使用和API可用性。

相关推荐
qq_2562470516 小时前
Rust 模块化单体架构:告别全局 Migrations,实现真正的模块自治
开发语言·架构·rust
分布式存储与RustFS16 小时前
MinIO替代方案与团队适配性分析:RustFS如何匹配不同规模团队?
人工智能·rust·开源项目·对象存储·minio·企业存储·rustfs
分布式存储与RustFS16 小时前
MinIO替代方案生态集成指南:RustFS如何无缝融入现代技术栈
rust·github·开源项目·对象存储·minio·企业存储·rustfs
王燕龙(大卫)16 小时前
rust:线程
开发语言·rust
李广坤17 小时前
Rust基本使用
后端·rust
Source.Liu20 小时前
【time-rs】Duration 结构体详解
rust·time
Chen--Xing20 小时前
LeetCode 49.字母异位词分组
c++·python·算法·leetcode·rust
古城小栈21 小时前
Go+Rust混合编程:高性能系统开发的最优解之一
golang·rust
云雾J视界1 天前
告别手动寄存器编程:STM32-RS 生态如何重构嵌入式开发效率
rust·svd·嵌入式开发·寄存器·工具链·可编译·社区驱动