【Chrono库】Chrono DateTime 测试套件解析(src\datetime\tests.rs)
这个模块包含了 DateTime 类型的全面测试,涵盖了时间戳转换、时区处理、算术运算、格式化解析等核心功能。
测试结构设计
1. DST 测试时区模拟器
自定义时区实现:
#[derive(Clone)]
struct DstTester;impl DstTester {fn winter_offset() -> FixedOffset { FixedOffset::east_opt(8 * 60 * 60).unwrap() }fn summer_offset() -> FixedOffset { FixedOffset::east_opt(9 * 60 * 60).unwrap() }const TO_WINTER_MONTH_DAY: (u32, u32) = (4, 15); // 4月15日切换到冬令时const TO_SUMMER_MONTH_DAY: (u32, u32) = (9, 15); // 9月15日切换到夏令时
}
时区转换逻辑:
- 4月15日 02:00:00 切换到冬令时 (UTC+8)
- 9月15日 02:00:00 切换到夏令时 (UTC+9)
- 处理夏令时转换期间的模糊时间和不存在时间
核心测试分类
1. 时间戳转换测试
毫秒时间戳测试:
#[test]
fn test_datetime_from_timestamp_millis() {let valid_map = [(1662921288000, "2022-09-11 18:34:48.000000000"),(1662921288123, "2022-09-11 18:34:48.123000000"),(-2208936075000, "1900-01-01 14:38:45.000000000"),(0, "1970-01-01 00:00:00.000000000"),];for (timestamp_millis, formatted) in valid_map {let datetime = DateTime::from_timestamp_millis(timestamp_millis).unwrap();assert_eq!(timestamp_millis, datetime.timestamp_millis());}
}
多精度验证:
- 纳秒精度:
test_datetime_from_timestamp_nanos - 微秒精度:
test_datetime_from_timestamp_micros - 毫秒精度:
test_datetime_from_timestamp_millis - 秒级精度:
test_datetime_from_timestamp_secs
2. 边界值测试
纳秒精度边界:
#[test]
fn test_nanosecond_range() {const A_BILLION: i64 = 1_000_000_000;// 最大可表示时间let maximum = "2262-04-11T23:47:16.854775804UTC";let parsed: DateTime<Utc> = maximum.parse().unwrap();let nanos = parsed.timestamp_nanos_opt().unwrap();// 最小可表示时间 let minimum = "1677-09-21T00:12:44.000000000UTC";let parsed: DateTime<Utc> = minimum.parse().unwrap();let nanos = parsed.timestamp_nanos_opt().unwrap();// 超出范围的测试let beyond_max = parsed + TimeDelta::try_milliseconds(300).unwrap();assert!(beyond_max.timestamp_nanos_opt().is_none());
}
3. 时区算术运算测试
天数加减:
#[test]
fn test_datetime_add_days() {let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();// 普通时区测试assert_eq!(format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)),"2014-05-11 07:08:09 -05:00");// DST 时区测试assert_eq!(format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(10)),"2014-04-16 07:08:09 +08:00" // 跨越 DST 转换);
}
月份加减:
#[test]
fn test_datetime_add_months() {let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();assert_eq!(format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)),"2014-06-06 07:08:09 -05:00");// 月末日期处理let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap();assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
}
4. 格式化与解析测试
RFC 2822 格式:
#[test]
#[cfg(feature = "alloc")]
fn test_datetime_rfc2822() {let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap();// 基本格式assert_eq!(Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(),"Wed, 18 Feb 2015 23:16:09 +0000");// 闰秒处理assert_eq!(ymdhms_micro(&edt, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc2822(),"Wed, 18 Feb 2015 23:59:60 +0500");// 复杂格式解析assert_eq!(DateTime::parse_from_rfc2822("Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330 (Newfoundland Time)"),Ok(ymdhms(&FixedOffset::east_opt(-3*3600-30*60).unwrap(), 1969, 2, 13, 23, 32, 0)));
}
RFC 3339 格式:
#[test]
#[cfg(feature = "alloc")]
fn test_datetime_rfc3339() {let edt5 = FixedOffset::east_opt(5 * 60 * 60).unwrap();// 不同精度选项assert_eq!(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(),"2015-02-18T23:59:60.234567+05:00");// 格式选项测试assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false),"2018-01-11T10:05:13.084+08:00");assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), // UTC 使用 Z"2018-01-11T02:05:13.084Z");
}
5. 字符串解析测试
宽松的 RFC 3339 解析:
#[test]
fn test_parse_datetime_utc() {let valid = ["2001-02-03T04:05:06Z","2001-02-03T04:05:06+0000", // 允许 +0000 格式"2012-12-12 12:12:12Z", // 允许空格分隔符"2012-12-12t12:12:12Z", // 允许小写 t"2015-02-18T23:16:09.153Z", // 小数秒"+82701-05-6T15:9:60.898989898989Z", // 扩展年份范围];for &s in &valid {let d = s.parse::<DateTime<Utc>>().unwrap();let s_ = format!("{d:?}");let d_ = s_.parse::<DateTime<Utc>>().unwrap();assert_eq!(d, d_, "Round-trip failed for {}", s);}
}
自定义格式解析:
#[test]
fn test_datetime_parse_from_str() {let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35);// 各种时区格式变体assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %z"), Ok(dt));assert_eq!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %z"), Ok(dt));assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %z"), Ok(dt));assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %z"), Ok(dt));
}
6. 边界情况和极端值测试
最小/最大值处理:
#[test]
fn test_min_max_getters() {let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap();let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN);let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap();let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX);assert_eq!(beyond_min.year(), -262144);assert_eq!(beyond_max.year(), 262143);assert_eq!(beyond_max.nanosecond(), 999_999_999);
}
赋值运算测试:
#[test]
fn test_datetime_add_assign() {let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();let datetime = naivedatetime.and_utc();let mut datetime_add = datetime;datetime_add += TimeDelta::try_seconds(60).unwrap();assert_eq!(datetime_add, datetime + TimeDelta::try_seconds(60).unwrap());
}
7. 系统时间互操作性测试
SystemTime 转换:
#[test]
#[cfg(all(feature = "std", not(all(target_arch = "wasm32", target_os = "wasi"))))]
fn test_from_system_time() {use std::time::{Duration, SystemTime, UNIX_EPOCH};let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap();// SystemTime -> DateTime<Utc>assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch);// DateTime<Utc> -> SystemTime assert_eq!(SystemTime::from(epoch), UNIX_EPOCH);
}
测试工具函数
便捷构造函数:
// 创建带时区的日期时间
fn ymdhms(fixedoffset: &FixedOffset, year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<FixedOffset> {fixedoffset.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
}// 创建带毫秒的日期时间
fn ymdhms_milli(fixedoffset: &FixedOffset, year: i32, month: u32, day: u32,hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<FixedOffset> {fixedoffset.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap().with_nanosecond(milli * 1_000_000).unwrap()
}
测试设计特点
- 全面性:覆盖所有主要功能和边界情况
- 可读性:使用清晰的测试数据和断言
- 隔离性:每个测试专注于特定功能
- 边界测试:特别关注最小/最大值和边缘情况
- 往返测试:验证序列化/反序列化的正确性
- 错误情况:测试无效输入的处理
- 特性门控:使用
cfg属性控制特性相关测试
这个测试套件确保了 DateTime 类型的正确性、健壮性和一致性,是 Chrono 库高质量的重要保障。
