sebastienrousseau/dtt

GitHub: sebastienrousseau/dtt

一款注重往返安全性和时区消歧的 Rust 日期时间操作库,提供严格的解析、验证与格式化能力。

Stars: 7 | Forks: 0

DateTime (DTT) logo

DateTime (DTT)

一个符合人体工程学的 Rust 库,用于解析、验证、操作和格式化日期、时间与时区——具有保证的往返安全性和明确的时区代码。

Build Crates.io Docs.rs Coverage lib.rs

## 目录 - [安装](#install) - [快速开始](#quick-start) - [为什么选择 DTT?](#why-dtt) - [功能](#features) - [支持的时区缩写](#supported-timezone-abbreviations) - [API 概览](#api-highlights) - [开发](#development) - [故障排除](#troubleshooting) - [文档](#documentation) - [贡献](#contributing) - [许可证](#license) ## 安装 ``` cargo add dtt ``` 或者添加到 `Cargo.toml`: ``` [dependencies] dtt = "0.0.11" ``` ### 前置条件 DTT 要求 **Rust 1.88.0 或更高版本**(由 `time = 0.3.47` 锁定,其中包含了针对 `time < 0.3.47` 中 [RUSTSEC 栈耗尽 DoS](https://rustsec.org/) 的上游修复)。 | 平台 | 设置 | |----------|-------| | **macOS** | `brew install rustup-init && rustup-init -y` | | **Linux** | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh -s -- -y` | | **WSL** | 与 Linux 相同,在你的 WSL 发行版中运行 | | **Windows** | 从 [rustup.rs](https://rustup.rs/) 下载 `rustup-init.exe` | 安装后,使用 `rustc --version` 验证(必须 ≥ 1.88.0)。使用 `rustup update stable` 升级现有工具链。 ## 快速开始 ``` use dtt::prelude::*; fn main() -> Result<(), AppError> { // Current UTC time let now = DateTime::new(); println!("Current time: {}", now); // Parse — strict, offset-required for time-bearing inputs let parsed = DateTime::parse("2024-01-15T10:30:00Z")?; println!("Parsed: {}", parsed); // Round-trip is guaranteed: parse(format(x)) == x let s = parsed.format_rfc3339()?; assert_eq!(parsed, DateTime::parse(&s)?); // Arithmetic let next_week = parsed.add_days(7)?; let next_year = parsed.add_years(1)?; println!("Next week: {next_week}, next year: {next_year}"); // Timezone conversion (note the explicit USA suffix) let est = parsed.convert_to_tz("EST_USA")?; println!("In US Eastern: {est}"); // Validation assert!(DateTime::is_valid_iso_8601("2024-01-15T10:30:00Z")); assert!(!DateTime::is_valid_year("10000")); // outside time crate range Ok(()) } ``` 运行完整演示: ``` cargo run --example dtt ``` ## 为什么选择 DTT? 大多数日期时间库会在令人意想不到的地方默默地产生错误结果。DTT 的设计理念是**大声报错**,而不是进行猜测: - **往返安全性:** `DateTime::parse(&dt.format_rfc3339()?)? == dt` 始终成立。没有默默的纯日期截断。 - **明确的时区:** `IST` 可能表示印度 (+05:30)、爱尔兰 (+01:00) 或以色列 (+02:00)。DTT 要求使用明确的后缀(`IST_INDIA`、`IST_IRELAND`、`IST_ISRAEL`),这样您就不会意外地使用错误的时区。 - **拒绝混合符号的偏移量:** `new_with_custom_offset(5, -30)` 会返回错误,而不是默默地产生 `+05:30`。 - **确定性的 `Default`:** `DateTime::default()` 返回 Unix epoch,而不是本地时钟时间,因此测试是可复现的。 - **UTC 标准化的相等性:** 表示同一时刻的两个 `DateTime` 值比较结果为相等,无论它们是以哪种偏移量存储的。 - **严格验证:** `is_valid_year` 限定在实际的 `time::Date` 范围(`-9999..=9999`)内,因此验证器和构建器始终保持一致。 ## 功能 | | | | :--- | :--- | | **解析** | 带偏移量的 RFC 3339,仅日期的 ISO 8601,自定义格式字符串 | | **格式化** | RFC 3339,自定义格式描述符 | | **验证** | 组件、范围、闰年、ISO 8601、时间字符串 | | **算术运算** | `add_days`、`add_months`、`add_years`(带有溢出检查) | | **比较** | `Eq`、`Ord`、`Hash` — 全部经过 UTC 标准化 | | **日历辅助方法** | `start_of_week`、`end_of_month`、`iso_week`、`iso_year` | | **时区支持** | 22 个已消歧的缩写 + 自定义偏移量 | | **序列化** | 通过规范的 RFC 3339 字符串进行 `serde` 往返 | | **跨平台** | macOS、Linux、WSL、Windows | ## 支持的时区缩写 常见缩写经过了刻意的**消歧义**。像 `EST`、`CST`、`IST` 和 `WADT` 这样的裸代码是**不被接受**的,因为它们在现实世界中指向多个时区。 | 代码 | 偏移量 | 区域 | |------|-------:|--------| | `UTC`、`GMT` | +00:00 | 协调世界时 | | `EST_USA` | −05:00 | 美国东部标准时间 | | `EDT` | −04:00 | 美国东部夏令时 | | `CST_USA` | −06:00 | 美国中部标准时间 | | `CDT` | −05:00 | 美国中部夏令时 | | `MST` / `MDT` | −07/−06 | 美国山区 | | `PST` / `PDT` | −08/−07 | 美国太平洋 | | `CET` / `CEST` | +01/+02 | 中欧 | | `EET` / `EEST` | +02/+03 | 东欧 | | `IST_IRELAND` | +01:00 | 爱尔兰标准时间 | | `IST_ISRAEL` | +02:00 | 以色列标准时间 | | `IST_INDIA` | +05:30 | 印度标准时间 | | `JST` | +09:00 | 日本 | | `HKT` | +08:00 | 香港 | | `CST_CHINA` | +08:00 | 中国标准时间 | | `EST_AUS` / `AEST` | +10:00 | 澳大利亚东部 | | `AEDT` | +11:00 | 澳大利亚东部夏令时 | | `ACWST` | +08:45 | 澳大利亚中西部 | 对于任何其他时区,请使用 [`DateTime::new_with_custom_offset(hours, minutes)`](https://docs.rs/dtt/latest/dtt/datetime/struct.DateTime.html#method.new_with_custom_offset)。 ## API 概览 ### 构造 ``` use dtt::prelude::*; use time::UtcOffset; let now = DateTime::new(); // current UTC let utc = DateTime::new_with_tz("UTC")?; // explicit let mumbai = DateTime::new_with_tz("IST_INDIA")?; // disambiguated let custom = DateTime::new_with_custom_offset(5, 30)?; // +05:30 let exact = DateTime::from_components(2024, 1, 15, 10, 30, 0, UtcOffset::UTC)?; let epoch = DateTime::default(); // 1970-01-01T00:00:00Z // Builder pattern let dt = DateTimeBuilder::new() .year(2024).month(1).day(15) .hour(10).minute(30).second(0) .offset(UtcOffset::UTC) .build()?; # Ok::<(), AppError>(()) ``` ### 解析与格式化 ``` # use dtt::prelude::*; let dt1 = DateTime::parse("2024-01-15T10:30:00Z")?; let dt2 = DateTime::parse("2024-01-15T10:30:00+05:30")?; let dt3 = DateTime::parse("2024-01-15")?; // date-only OK let custom = DateTime::parse_custom_format( "15/01/2024 10:30", "[day]/[month]/[year] [hour]:[minute]", )?; let s: String = dt1.format_rfc3339()?; let pretty = dt1.format("[year]-[month]-[day]")?; # Ok::<(), AppError>(()) ``` ### 算术与日历运算 ``` # use dtt::prelude::*; let dt = DateTime::parse("2024-01-31T00:00:00Z")?; let next_day = dt.next_day()?; let prev_day = dt.previous_day()?; let next_week = dt.add_days(7)?; let next_feb = dt.add_months(1)?; // → 2024-02-29 (leap year aware) let next_year = dt.add_years(1)?; let monday = dt.start_of_week()?; let sunday = dt.end_of_week()?; let last_day = dt.end_of_month()?; # Ok::<(), AppError>(()) ``` ### 宏 ``` use dtt::prelude::*; use dtt::{dtt_now, dtt_parse, dtt_add_days, dtt_diff, dtt_diff_seconds}; let now = dtt_now!(); let dt = dtt_parse!("2024-01-15T10:30:00Z")?; let later = dtt_add_days!(dt, 7)?; let secs: Option = dtt_diff_seconds!("1609459200", "1609459230"); assert_eq!(secs, Some(30)); # Ok::<(), AppError>(()) ``` ## 开发 在任何平台上一分钟内完成克隆、构建和验证: ``` git clone https://github.com/sebastienrousseau/dtt.git cd dtt make verify # fmt-check + lint + test in one command make help # full task list ``` `Makefile` 只是底层 Cargo 命令的一层简单封装, 因此直接调用等效命令也可以正常工作: ``` cargo build # build the library and binary cargo test # run all 240+ tests cargo clippy --all-targets -- -D warnings # lint with strict warnings cargo fmt --check # verify formatting cargo doc --no-deps --open # open API docs in your browser cargo run --example dtt # run the end-to-end demo cargo bench # run criterion benchmarks ``` 所有命令在 macOS、Linux 和 WSL 上的工作方式完全相同。CI 在每次提交 PR 时,都会通过 [`.github/workflows/cross-platform.yml`](.github/workflows/cross-platform.yml) 在 Linux、macOS **以及** Windows 上运行相同的测试矩阵。 ## 故障排除 | 症状 | 可能原因 | 解决方法 | |---------|--------------|-----| | `time-core` 报错 `feature 'edition2024' is required` | Rust 版本 < 1.88.0 | `rustup update stable` | | 对于 `"EST"`、`"CST"`、`"IST"` 报错 `Err(InvalidTimezone)` | 设计上已拒绝不明确的裸代码 | 使用带后缀的形式(例如 `EST_USA`、`IST_INDIA`) | | 解析 `"2024-01-01T12:00:00"` 时报错 `Err(InvalidFormat)` | RFC 3339 要求带有偏移量 | 在末尾加上 `Z` 或 `+HH:MM` | | `new_with_custom_offset(5, -30)` 报错 `Err(InvalidTimezone)` | 混合符号的偏移量会被拒绝 | 传入符号相同的组件,例如 `(4, 30)` | | `from_components(10000, ...)` 报错 `Err(InvalidDate)` | `time::Date` 仅支持 `-9999..=9999` | 使用该范围内的年份 | | 测试因环境变量竞争而失败 | `cargo test` 会并行运行测试 | 已通过 `serial_test` 缓解;如果您有自定义的环境变量测试,请使用 `cargo test -- --test-threads=1` | ## 文档 - **API 参考:** - **端到端示例:** [`examples/dtt.rs`](examples/dtt.rs) - **基准测试:** [`benches/criterion.rs`](benches/criterion.rs) — 运行 `cargo bench` - **更新日志:** [`CHANGELOG.md`](CHANGELOG.md) - **安全策略:** [`.github/SECURITY.md`](.github/SECURITY.md) - **行为准则:** [`.github/CODE-OF-CONDUCT.md`](.github/CODE-OF-CONDUCT.md) ## 许可证 根据您的选择,采用 [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) 或 [MIT](https://opensource.org/licenses/MIT) 双重许可。

返回顶部

标签:Rust, 可视化界面, 开发库, 数据解析, 日期时间处理, 时区转换, 时间格式化, 网络流量审计, 通知系统