pendulum

A drop-in replacement for Python's datetime with timezone-aware objects by default and a friendlier API.

Library
PyPI
v3.2.0
6,673stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
69/100Good
Development Activity60
Maintenance52
Community64
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
83/100Excellent
Architecture82
Code Quality88
Innovation78
Learning Curve85

Pendulum subclasses the standard library’s datetime so it behaves as a drop-in replacement while removing the notion of naive datetimes: every instance is timezone-aware, defaulting to UTC. It adds fluent methods for adding, subtracting, and comparing dates, human-readable diffs like “2 minutes ago,” and correct handling of DST transitions and timezone normalization edge cases that trip up the raw standard library.

Parsing and low-level date arithmetic are implemented in a Rust extension (built with PyO3/maturin) for speed, with a pure-Python fallback, so the library stays fast without sacrificing the readable, chainable API it’s known for. It also improves on timedelta with a Duration class offering in_words, in_hours, and similar intuitive accessors, plus a testing module for freezing or traveling through time in tests.

What You Get

  • Timezone-aware by default - every DateTime created via pendulum.now() or pendulum.datetime() carries a timezone (UTC unless specified), eliminating an entire class of naive-datetime bugs.
  • Fluent date arithmetic - chainable .add(), .subtract(), .in_timezone(), and comparison helpers that read like English instead of timedelta juggling.
  • Human-readable diffs - diff_for_humans() renders relative time like “2 minutes ago” or “in 3 days”, with locale support for many languages.
  • Correct DST and normalization handling - operations across daylight-saving transitions and ambiguous/skipped local times resolve predictably instead of silently producing wrong wall-clock values.
  • Rust-accelerated parsing - ISO 8601 and common date-string parsing is implemented in a Rust extension via PyO3 for speed, transparent to callers.
  • Time-travel testing utilities - the pendulum.testing module lets tests freeze pendulum.now() or travel to arbitrary points in time without monkeypatching.

Common Use Cases

  • Scheduling and cron-like logic - computing “next occurrence” or recurring intervals across timezones without DST bugs.
  • API and log timestamps - parsing and formatting ISO 8601 timestamps from external services consistently, in UTC or a target timezone.
  • User-facing relative timestamps - showing “5 minutes ago” style text in dashboards or activity feeds via diff_for_humans().
  • Multi-timezone applications - converting and displaying the same moment in time correctly across users in different timezones.
  • Time-dependent test suites - freezing or traveling the clock in unit tests with pendulum.testing instead of mocking datetime.now.

Under The Hood

Architecture The public API is assembled in src/pendulum/__init__.py, which re-exports the core Date, DateTime, Time, Duration, and Interval classes along with module-level factory functions (now(), parse(), datetime(), timezone()). DateTime (in datetime.py, ~1400 lines) subclasses both datetime.datetime and the library’s own Date mixin, layering fluent methods (add, subtract, in_timezone, diff_for_humans) on top of the standard interface so existing code that type-checks against datetime.datetime keeps working. Timezone resolution lives in tz/timezone.py and tz/local_timezone.py, string parsing in parsing/iso8601.py, and output formatting in formatting/formatter.py and formatting/difference_formatter.py — a clean separation between representation, timezone logic, and parsing/formatting concerns. A testing/traveller.py module implements clock-freezing/travel by substituting the module-level now() used internally, letting test suites control time without patching the standard library.

Tech Stack The project is a hybrid Python/Rust package built with maturin and PyO3 (rust/ directory, exposed as the pendulum._pendulum extension module declared in pyproject.toml’s [tool.maturin] section), giving performance-sensitive parsing and helper routines a compiled fast path while the public API stays pure Python. Runtime dependencies are minimal — python-dateutil and tzdata — keeping the install footprint small. Tooling is modern Python: ruff for linting and import sorting, mypy --strict for type checking (with a shrinking legacy-exclusion allowlist), and Poetry-managed dependency groups for test, doc, lint, typing, dev, and benchmark concerns.

Code Quality The tests/ tree mirrors the source layout with dedicated suites per concern (date/, datetime/, duration/, interval/, time/, tz/, parsing/, formatting/, localization/, plus dedicated thread-safety and benchmark suites), run with pytest and pytest-benchmark/pytest-codspeed for performance regression tracking. Type hints are comprehensive with a py.typed marker and mypy --strict enforcement, and CI (.github/workflows/tests.yml) runs the suite across supported Python versions. Naming and structure are consistent throughout, and ruff enforces both style and a set of correctness-oriented lint rules (bugbear, comprehensions, simplify) rather than formatting alone.

What Makes It Unique Pendulum’s defining choice is being a strict subclass of datetime.datetime rather than a wrapper class — it can be passed anywhere a standard datetime is expected, which most “nicer datetime” libraries can’t claim. Combining that compatibility with mandatory timezone-awareness (no naive datetimes exist in the API) and DST-correct arithmetic addresses a class of bugs the standard library leaves as footguns. Offloading parsing to a Rust extension while keeping the ergonomic Python surface unchanged is a pragmatic response to Python datetime libraries’ historical performance complaints, without asking users to learn a different API.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search