Typed and DST-safe datetimes for Python, available in Rust or pure Python.
Do you cross your fingers every time you work with Python's datetime—hoping that you didn't mix naive and aware?
or that you avoided its other pitfalls?
There’s no way to be sure...
✨ Until now! ✨
Whenever helps you write correct and type checked datetime code,
using well-established concepts from modern libraries in other languages.
It's also way faster than other third-party libraries—and usually the standard library as well.
If performance isn't your top priority, a pure Python version is available as well.
RFC3339-parse, normalize, compare to now, shift, and change timezone (1M times)
⚠️ Note: A 1.0 release is coming soon. Until then, the API may change
as we gather feedback and improve the library.
Leave a ⭐️ on GitHub if you'd like to see how this project develops!
Why not the standard library?
Over 20+ years, Python's datetime has grown
out of step with what you'd expect from a modern datetime library.
Two points stand out:
It doesn't always account for Daylight Saving Time (DST).
Here is a simple example:
bedtime = datetime(2023, 3, 25, 22, tzinfo=ZoneInfo("Europe/Paris"))
full_rest = bedtime + timedelta(hours=8)
# It returns 6am, but should be 7am—because we skipped an hour due to DST!
Note this isn't a bug, but a design decision that DST is only considered
when calculations involve two timezones.
If you think this is surprising, you
arenotalone.
Typing can't distinguish between naive and aware datetimes.
Your code probably only works with one or the other,
but there's no way to enforce this in the type system!
# Does this expect naive or aware? Can't tell!defschedule_meeting(at: datetime) -> None: ...
Why not other libraries?
There are two other popular third-party libraries, but they don't (fully)
address these issues. Here's how they compare to whenever and the standard library:
Whenever
datetime
Arrow
Pendulum
DST-safe
✅
❌
❌
⚠️
Typed aware/naive
✅
❌
❌
❌
Fast
✅
✅
❌
❌
Arrow
is probably the most historically popular 3rd party datetime library.
It attempts to provide a more "friendly" API than the standard library,
but doesn't address the core issues:
it keeps the same footguns, and its decision to reduce the number
of types to just one (arrow.Arrow) means that it's even harder
for typecheckers to catch mistakes.
Pendulum
arrived on the scene in 2016, promising better DST-handling,
as well as improved performance.
However, it only fixes some DST-related pitfalls,
and its performance has significantly degraded over time.
Additionally, it's in maintenance limbo with only one release in the last four years,
and many issues remaining unaddressed.
🚀 Support for the latest GIL-related improvements (experimental)
Quickstart
>>> 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-2822: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: get to feature-parity, process feedback, and tweak the API:
✅ Datetime classes
✅ Deltas
✅ Date and time of day (separate from datetime)
✅ Implement Rust extension for performance
🚧 Tweaks to the delta API
🔒 1.0: API stability and backwards compatibility
🚧 Customizable parsing and formatting
🚧 Intervals
🚧 Ranges and recurring times
🚧 Parsing leap seconds
Limitations
Supports the proleptic Gregorian calendar between 1 and 9999 AD
Timezone offsets are limited to whole seconds (consistent with IANA TZ DB)
No support for leap seconds (consistent with industry standards and other modern libraries)
Versioning and compatibility policy
Whenever follows semantic versioning.
Until the 1.0 version, the API may change with minor releases.
Breaking changes will be meticulously explained in the changelog.
Since the API is fully typed, your typechecker and/or IDE
will help you adjust to any API changes.
⚠️ Note: until 1.x, pickled objects may not be unpicklable across
versions. After 1.0, backwards compatibility of pickles will be maintained
as much as possible.
License
Whenever is licensed under the MIT License.
The binary wheels contain Rust dependencies which are licensed under
similarly permissive licenses (MIT, Apache-2.0, and others).
For more details, see the licenses included in the distribution.
Acknowledgements
This project is inspired by—and borrows most concepts from—the following projects. Check them out!
We found that whenever demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Bybit's $1.46B hack by North Korea's Lazarus Group pushes 2025 crypto losses to $1.6B in just two months, already surpassing all of 2024's $1.49B total.
OpenSSF has published OSPS Baseline, an initiative designed to establish a minimum set of security-related best practices for open source software projects.
Michigan TypeScript founder Dimitri Mitropoulos implements WebAssembly runtime in TypeScript types, enabling Doom to run after processing 177 terabytes of type definitions.