【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可用性。

相关推荐
DongLi011 天前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神2 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234562 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234562 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei2 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234562 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234562 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234562 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234563 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234563 天前
(done) 速通 rustlings(22) 泛型
rust