# Pendulum: a drop-in datetime replacement for Python 3.10 and newer

> Pendulum wraps the standard datetime class in a timezone-aware subclass with friendlier arithmetic, DST normalization and human-readable diffs. It is a good fit for application code that schedules or formats timestamps, and a poor fit for libraries that inspect object types.

**python-pendulum/pendulum** — Python datetimes made easy

- Repository: https://github.com/python-pendulum/pendulum
- Website: https://pendulum.eustace.io
- Stars: 6,676 · Forks: 465
- Language: Python
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/python-pendulum-pendulum

## The datetime problems Pendulum targets

The standard library datetime is fine until you need timezone conversion, DST arithmetic or a duration expressed in words. Then you write the same helper functions in every project. Pendulum's README frames the gap directly: native datetime instances "are enough for basic cases but when you face more complex use-cases they often show limitations and are not so intuitive to work with."

The audience is application developers, not library authors. Scheduling jobs, rendering timestamps in a user's zone, computing "2 minutes ago" for a feed, and adding days across a daylight saving boundary are the tasks Pendulum is built around. Each Pendulum instance is timezone-aware and defaults to UTC, so the naive-datetime ambiguity disappears from your code by construction. The package supports Python 3.10 and newer, and pyproject.toml declares python-dateutil>=2.6 and tzdata>=2020.1 as its only runtime dependencies.

## How DateTime subclasses datetime without breaking it

The core design decision is inheritance. Pendulum's DateTime inherits from the standard datetime class, which is what makes it a drop-in replacement: you can substitute DateTime instances for datetime instances in most code, and the C-level operations still work. The README is explicit that exceptions exist "for libraries that check the type of the objects by using the type function like sqlite3 or PyMySQL for instance."

A second layer sits underneath. The repository contains a rust/ directory, and pyproject.toml configures maturin with module-name = "pendulum._pendulum" and features = ["pyo3/extension-module"]. So part of the implementation is a compiled Rust extension. The Makefile confirms this is optional at test time: the test target runs pytest twice, first with PENDULUM_EXTENSIONS=0 and then without that variable. That means the pure-Python path and the extension path are both exercised.

Timezone data comes from tzdata rather than the operating system, so the same rules apply on every machine. Normalization is the visible payoff. The README shows pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris') returning 2013-03-31T03:30:00+02:00, because 2:30 does not exist on that date. Adding one microsecond to 01:59:59.999999 on the same day returns 03:00:00+02:00.

## Installing Pendulum and a first timezone conversion

Install from PyPI. The README's own example imports the package and asks for the current time in a named zone:

```bash
pip install pendulum
```

```python
>>> import pendulum

>>> now_in_paris = pendulum.now('Europe/Paris')
>>> now_in_paris
'2016-07-04T00:49:58.502116+02:00'

>>> now_in_paris.in_timezone('UTC')
'2016-07-03T22:49:58.502116+00:00'
```

The repr carries the UTC offset, which is the quickest way to confirm the object is not naive. From there, arithmetic and formatting are the README's other examples:

```python
>>> tomorrow = pendulum.now().add(days=1)
>>> last_week = pendulum.now().subtract(weeks=1)
>>> past = pendulum.now().subtract(minutes=2)
>>> past.diff_for_humans()
'2 minutes ago'
```

The delta object exposes both structured fields and localized text. The README shows delta.hours returning 23 and delta.in_words(locale='en') returning '6 days 23 hours 58 minutes'. If you are working on Pendulum itself rather than with it, the README's contributing section clones the repository and runs poetry install; the Makefile's dev target runs poetry install with the main, test, typing, build and lint groups and then poetry run maturin develop to build the Rust extension.

## Where the drop-in claim stops: type() checks and Django

The README's limitations section is the most useful part of the documentation, and it is honest about the failure mode. Drivers that call type() on a value to decide how to serialize it will not recognize a DateTime subclass. sqlite3 is one; mysqlclient (formerly MySQLdb) and PyMySQL are the others.

The README gives adapters for each. For sqlite3:

```python
from pendulum import DateTime
from sqlite3 import register_adapter

register_adapter(DateTime, lambda val: val.isoformat(' '))
```

For the MySQL drivers, the README registers DateTime in each library's converter map, mapping to MySQLdb.converters.DateTime2literal and pymysql.converters.escape_datetime respectively. Django is a separate case: it calls isoformat() to store datetimes, and because Pendulum is always timezone-aware the offset is always present, which the README says raises an error at least for MySQL. The suggested fix is a DateTimeField subclass whose value_to_string returns value.to_datetime_string().

Read that as a boundary, not a bug. If your application is a web service writing timestamps through an ORM, you inherit adapter work. If you are writing a library that other code will pass to arbitrary drivers, the subclass is a liability and plain datetime is the safer return type.

## Pendulum versus python-dateutil and the standard library

python-dateutil is already a dependency of Pendulum, and it is the obvious alternative for parsing and relative deltas. The difference is scope and object model. dateutil gives you parser.parse, relativedelta and tz objects that you apply to ordinary datetime instances; it does not give you a datetime subclass with its own methods. Pendulum's approach is to make the object itself carry the convenience: .in_timezone(), .add(), .subtract(), .diff_for_humans() and .in_words() are methods on the value you already have.

That choice is why the type-checking limitation exists at all. dateutil cannot break a driver's type() check because it never introduces a new type. If you want one library that handles both parsing and arithmetic without changing the class of your values, dateutil is the lower-risk pick. If you want the call sites to read as English and you control the database boundary, Pendulum is the shorter path. The two are not mutually exclusive: Pendulum depends on dateutil, so the parsing machinery is present either way.

## Maintenance, licence and what an upgrade costs

The repository is not archived, and the last push was on 2026-09-19. The most recent release is 3.2.0, published on 2026-01-30, following 3.1.0 on 2025-04-19 and 3.0.0 on 2023-12-16. That cadence matters when you plan upgrades: the 3.0.0 to 3.1.0 gap was roughly sixteen months, so a project pinned to a 3.x release should not assume frequent patch drops.

The licence is MIT, declared in pyproject.toml as license = { text = "MIT License" } and classified as OSI Approved :: MIT License. For most users that means permissive reuse with attribution; it is not a copyleft licence, and this is a description of the metadata rather than legal advice.

Upgrade cost has two components. The Python floor is 3.10 or newer per pyproject.toml, and the classifiers list 3.10 through 3.14. The other component is the Rust extension: because the build uses maturin and the Makefile exposes maturin develop, contributors building from source need a Rust toolchain, though the PENDULUM_EXTENSIONS=0 test path indicates the package can run without the compiled module. The CHANGELOG.md at the repository root is where release-to-release changes are recorded; the README does not document a rollback procedure.

## Conclusion

Adopt Pendulum for application code that needs timezone-aware arithmetic, human-readable diffs and DST normalization without rewriting every call site. Do not adopt it if your stack passes datetimes into sqlite3, mysqlclient, PyMySQL or Django's DateTimeField without an adapter, because those drivers use type() and will reject a DateTime subclass. Before committing, verify two things: that your Python is 3.10 or newer as pyproject.toml requires, and that every database boundary in your code either registers the adapter shown in the README or converts with to_datetime_string().

## FAQ

### How do I install Pendulum in a Python project?

Install it from PyPI with pip install pendulum. The package requires Python 3.10 or newer and pulls in python-dateutil and tzdata as runtime dependencies.

### How do I use Pendulum to get the current time in another timezone?

Call pendulum.now('Europe/Paris') to get a timezone-aware DateTime, then call .in_timezone('UTC') on it to convert. The README shows both calls and the resulting offset-carrying repr.

### Does Pendulum replace the standard datetime class?

It is a drop-in replacement in the sense that DateTime inherits from datetime, so instances can stand in for datetime objects in most code. The README notes exceptions for libraries that check the type of objects with type(), such as sqlite3 and PyMySQL.

### What happens when a Pendulum datetime falls in a daylight saving gap?

Pendulum normalizes it. The README shows pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris') returning 2013-03-31T03:30:00+02:00 because 2:30 does not exist on that date.

### Why does Pendulum fail with sqlite3 or Django?

Those layers inspect the object type or call isoformat() to serialize. The README provides a register_adapter call for sqlite3 and a DateTimeField subclass overriding value_to_string for Django, and notes the same class of problem for mysqlclient and PyMySQL.

## Sources

- [License: MIT](https://github.com/python-pendulum/pendulum/blob/master/LICENSE)
- [Project website](https://pendulum.eustace.io)
- [python-pendulum/pendulum on GitHub](https://github.com/python-pendulum/pendulum)
- [README](https://github.com/python-pendulum/pendulum/blob/master/README.md)
- [Releases](https://github.com/python-pendulum/pendulum/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/python-pendulum-pendulum
