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