Whenever - Python 中类型安全和 DST 安全的日期时间库
ariebovenberg / **whenever ** Public
- Notifications You must be signed in to change notification settings
- Fork 17
- Star 1.4k
⏰ 现代 Python 日期时间库 whenever.rtfd.io
License
MIT license 1.4k stars 17 forks Branches Tags Activity Star Notifications You must be signed in to change notification settings
Additional navigation options
ariebovenberg/whenever
main BranchesTags Go to file Code
Folders and files
Name| Name| Last commit message| Last commit date ---|---|---|---
Latest commit
History
260 Commits .cargo| .cargo .github| .github benchmarks| benchmarks docs| docs pysrc/whenever| pysrc/whenever requirements| requirements src| src tests| tests typesafety| typesafety .coveragerc| .coveragerc .flake8| .flake8 .gitignore| .gitignore .readthedocs.yml| .readthedocs.yml CHANGELOG.rst| CHANGELOG.rst CONTRIBUTING.md| CONTRIBUTING.md Cargo.lock| Cargo.lock Cargo.toml| Cargo.toml LICENSE| LICENSE MANIFEST.in| MANIFEST.in Makefile| Makefile README.md| README.md build.rs| build.rs generate_docstrings.py| generate_docstrings.py pyproject.toml| pyproject.toml pytest.ini| pytest.ini setup.py| setup.py View all files
Repository files navigation
⏰ Whenever
为 Python 提供类型安全和 DST 安全的日期时间,可以使用 Rust 实现或者纯 Python 实现。
你是否每次使用 Python 的 datetime
库时都祈祷自己没有混淆 naive 和 aware 的日期时间?或者希望自己避开了它的其他陷阱? 没有什么办法能确定...
✨ 现在不一样了! ✨
Whenever 帮助你编写正确且类型检查的日期时间代码,它使用了其他语言现代库中成熟的概念。它也比其他第三方库快得多 —— 通常也比标准库快。如果性能不是你的首要任务,还可以使用纯 Python 版本。
RFC3339 解析、标准化、与当前时间比较、偏移和更改时区 (100 万次)
📖 文档 | 🐍 PyPI | 🐙 GitHub | 🚀 Changelog | ❓ FAQ | 🗺️ Roadmap | 💬 Issues & feedback
⚠️ 注意:1.0 版本即将发布。在此之前,API 可能会随着我们收集反馈和改进库而发生变化。如果你想了解这个项目的发展,请在 GitHub 上留下一个 ⭐️!
为什么不使用标准库?
经过 20 多年,Python 的 datetime
库已经与你对现代日期时间库的期望不一致了。 有两点比较突出:
- 它并不总是考虑夏令时 (DST)。 这是一个简单的例子:
bedtime = datetime(2023, 3, 25, 22, tzinfo=ZoneInfo("Europe/Paris"))
full_rest = bedtime + timedelta(hours=8)
# 它返回 6am,但应该是 7am——因为由于 DST 我们跳过了一个小时!
请注意,这不是一个错误,而是一个设计上的决定,即只有当计算涉及_两个_时区时,才会考虑 DST。 如果你觉得这很奇怪,你不是 孤单的 。
- 类型系统无法区分 naive 和 aware 的日期时间。 你的代码可能只使用其中一种,但没有办法在类型系统中强制执行这一点!
# 这期望的是 naive 还是 aware? 无法分辨!
def schedule_meeting(at: datetime) -> None: ...
为什么不使用其他库?
还有两个流行的第三方库,但它们并没有(完全)解决这些问题。 以下是它们与 Whenever 和标准库的比较:
Whenever | datetime | Arrow | Pendulum ---|---|---|--- DST-safe | ✅ | ❌ | ❌ | ⚠️ Typed aware/naive | ✅ | ❌ | ❌ | ❌ Fast | ✅ | ✅ | ❌ | ❌
Arrow 可能是历史上最流行的第三方日期时间库。 它试图提供比标准库更“友好”的 API,但没有解决核心问题:它保留了相同的陷阱,并且它决定将类型数量减少到只有一个 (arrow.Arrow
) 意味着类型检查器更难发现错误。
Pendulum 于 2016 年出现,承诺更好的 DST 处理以及更高的性能。 然而,它只修复了_一些_与 DST 相关的陷阱,并且它的性能随着时间的推移显着下降。 此外,它处于维护停滞状态,过去四年仅发布了一个版本,并且许多问题仍未解决。
为什么要使用 Whenever?
- 🌐 DST 安全的算术运算
- 🛡️ 类型安全的 API 可防止常见错误
- ✅ 修复了 arrow/pendulum 没有解决的问题
- ⚖️ 基于经过验证的熟悉概念
- ⚡️ 无与伦比的性能
- 💎 经过全面测试和文档记录
- 📆 支持日期算术
- ⏱️ 纳秒精度
- 🦀 Rust! —— 但提供纯 Python 选项
- 🚀 支持最新的与 GIL 相关的改进(实验性)
快速开始
>>> from whenever import (
... # Explicit types for different use cases
... Instant,
... ZonedDateTime,
... LocalDateTime,
... )
# Identify moments in time, without timezone/calendar complexity
>>> now = Instant.now()
Instant(2024-07-04 10:36:56Z)
# Simple, explicit conversions
>>> now.to_tz("Europe/Paris")
ZonedDateTime(2024-07-04 12:36:56+02:00[Europe/Paris])
# A 'naive' local time can't accidentally mix with other types.
# You need to explicitly convert it and handle ambiguity.
>>> party_invite = LocalDateTime(2023, 10, 28, hour=22)
>>> party_invite.add(hours=6)
Traceback (most recent call last):
ImplicitlyIgnoringDST: Adjusting a local datetime implicitly ignores DST [...]
>>> party_starts = party_invite.assume_tz("Europe/Amsterdam")
ZonedDateTime(2023-10-28 22:00:00+02:00[Europe/Amsterdam])
# DST-safe arithmetic
>>> party_starts.add(hours=6)
ZonedDateTime(2023-10-29 03:00:00+01:00[Europe/Amsterdam])
# Comparison and equality
>>> now > party_starts
True
# Rounding and truncation
>>> now.round("minute", increment=15)
Instant(2024-07-04 10:30:00Z)
# Formatting & parsing common formats (ISO8601, RFC3339, RFC2822)
>>> now.format_rfc2822()
"Thu, 04 Jul 2024 10:36:56 GMT"
# If you must: you can convert to/from the standard lib
>>> now.py_datetime()
datetime.datetime(2024, 7, 4, 10, 36, 56, tzinfo=datetime.timezone.utc)
路线图
- 🧪 0.x:实现与现有库类似的功能、处理反馈并调整 API:
- ✅ 日期时间类
- ✅ Deltas(时间差)
- ✅ 与日期时间分开的日期和时间
- ✅ 实现 Rust 扩展以提高性能
- 🚧 调整 Delta API
- 🔒 1.0:API 稳定性和向后兼容性
- 🚧 可自定义的解析和格式化
- 🚧 时间间隔
- 🚧 范围和重复时间
- 🚧 解析闰秒
局限性
- 支持公元 1 年到 9999 年之间的 Proleptic Gregorian Calendar。
- 时区偏移限制为整数秒(与 IANA TZ DB 一致)。
- 不支持闰秒(与行业标准和其他现代库一致)。
版本控制和兼容性策略
Whenever 遵循语义版本控制。 在 1.0 版本之前,API 可能会随着次要版本的发布而发生变化。 破坏性更改将在更新日志中详细说明。 由于 API 是完全类型化的,因此你的类型检查器和/或 IDE 将帮助你适应任何 API 更改。
⚠️ 注意:在 1.x 版本之前,Pickle 序列化的对象可能无法跨版本反序列化。 1.0 版本之后,将尽可能保持 Pickle 的向后兼容性。
许可证
Whenever 基于 MIT 许可证。二进制 wheels 包含基于类似许可的 Rust 依赖项(MIT、Apache-2.0 和其他)。 更多详情请参见发行版中包含的许可证。
鸣谢
这个项目受到以下项目的启发,并借鉴了它们的大部分概念。 去看看吧!
基准比较图基于 Ruff 项目中的图。 对于时区数据,Whenever 使用 Python 自己的 zoneinfo
模块。
关于
⏰ 现代 Python 日期时间库 whenever.rtfd.io
Topics
python rust datetime cpython type-safe utc pypy utc-offsets pure-python timezones mypy rfc3339
Resources
License
Stars
Watchers
Forks
Releases 36
0.7.3 Latest Mar 19, 2025 [+ 35 releases](