mrkeyoor.com_
Sun 20 Sept 11:42 UTC
PyPIUtilsupdated 20 Sept 2026

pendulum review

Pendulum 3.2.0 is a timezone-aware date and time layer built on Python's standard `datetime` and `zoneinfo`. Its `DateTime` subclass adds named-zone conversion, calendar arithmetic, period boundaries, parsing, localized relative text, intervals, and durations with month or year units. Version 3.2.0 adds Python 3.14 support, removes the pytz dependency, lazy-loads locales, fixes timezone handling in `parse('now')`, and repairs Rust offset calculations plus interval and duration edge cases. Our installed wheel included py.typed and compiled extensions.

Verdict

Pendulum 3.2.0 installed in 0.3 seconds and used 5 MB across four packages in our sandbox, with typed APIs, native extensions, and no audit findings. It pays for itself in calendar-heavy scheduling; plain `datetime` and `zoneinfo` remain the better default for services that only move aware timestamps around.

We installed it

Lab card: what happened when we installed pendulumScreenshot of pendulum documentation
Install✓ · 0.3s4 packages on disk · 5 MB
Importimport pendulum in 0.19s · compiled extensions · py.typed · requires Python >=3.9
Known vulns0(pip-audit)

Answers from our run

Does pendulum install cleanly?

Yes. In a fresh container with an empty cache, pip install pendulum finished in 0.3s, leaving 4 packages and 5 MB on disk. pip-audit reported no known vulnerabilities.

What does pendulum need to run?

Python >=3.9, and a platform wheel with compiled extensions. In our run import pendulum succeeded in 0.19s, and the package ships py.typed for type checkers.

pendulum or whenever: which should you use?

whenever: Use it when separate types for instants, local datetimes, and zoned datetimes should catch category mistakes. Pendulum 3.2.0 installed in 0.3 seconds and used 5 MB across four packages in our sandbox, with typed APIs, native extensions, and no audit findings.

When should you not use pendulum?

The job only stores UTC instants and performs basic timezone conversion. Standard datetime with zoneinfo has no extra package cost.

API stability4/5The 3.x line retains `now`, `datetime`, `parse`, timezone conversion, `add`, `subtract`, boundaries, Duration, and Interval. Version 3.2.0 fixes behavior and packaging without replacing those entry points. Major history still matters: 3.0 renamed Period to Interval and replaced old test-clock helpers with an optional extra. The published 3.2.0 wheel permits Python 3.9, while the current development README has already moved to Python 3.10 and newer.
Docs4/5The official site covers construction, zones, DST normalization, parsing, arithmetic, formatting, localization, durations, intervals, and testing. The repository README goes further than most date libraries by naming exact-type failures in sqlite3, mysqlclient, PyMySQL, and Django/MySQL and showing adapter code. The main inconsistency is the Python floor: current source documentation says 3.10, but version 3.2.0 package metadata says 3.9, so deployment support requires checking both release and branch context.
Maintenance4/5Version 3.2.0 shipped on January 30, 2026 with Python 3.14 support, a PyO3 update, and fixes across parsing, offsets, intervals, durations, and locale loading. GitHub showed the unarchived repository pushed on August 20, 2026 with 266 open issues and pull requests. Work is active, although compiled-wheel coverage and a sizeable tracker justify pinned releases plus installation tests for every operating system and Python version you support.
Ecosystem4/5The queue's registry figure is 16,079,776 weekly downloads, and the repository has 6,673 stars. DateTime subclassing lets Pendulum cross many interfaces that already accept standard datetime values, while built-in locales and timezone support cover common server needs. Exact-type database drivers are important exceptions, and standard `zoneinfo` now handles the simplest named-zone work. The optional testing clock and native extension also add integration decisions absent from the standard library.

Use it if

  • Scheduling rules repeatedly add calendar months or days in a named timezone and must survive daylight-saving transitions.
  • The service wants aware datetimes by default plus concise conversion, boundary, interval, and humanized-difference methods.
  • Localized date output and relative phrases should be generated in Python rather than in every client.
  • Existing APIs accept datetime subclasses and your database drivers have been tested with Pendulum values.
Skip it if

Setup reality

We installed Pendulum 3.2.0 in a fresh Python 3.12 Bookworm sandbox. The install finished in 0.3 seconds and left four packages occupying 5 MB. Pendulum declares three direct dependencies, requires Python 3.9 or later, and includes py.typed plus compiled .so extensions. import pendulum succeeded in 0.19 seconds. pip-audit found zero known vulnerabilities, and the package reports the MIT License.

No credential or config file is needed. The package uses zoneinfo and installs timezone data support; 3.2.0 no longer depends on pytz. Its release metadata permits Python 3.9, while the current repository README says Python 3.10 and newer for ongoing development. Treat the wheel metadata as the floor for this exact release, then recheck before an upgrade. Time-travel testing helpers require the optional test extra.

DateTime subclasses standard datetime, but some database libraries compare exact types. The project documents adapter registration for sqlite3, mysqlclient, and PyMySQL, and notes a Django/MySQL issue caused by always-aware ISO output. Register adapters before creating connections and round-trip a real column in tests. Locale defaults are process-wide, so pass the locale to formatting calls in a concurrent server instead of changing global state per request.

DST arithmetic needs an explicit rule. Adding one day preserves a calendar intent, while 24 hours preserves elapsed time and may land on a different local clock hour. Pendulum normalizes a nonexistent spring-forward time unless raise_on_unknown_times=True; an autumn duplicate uses fold to select the occurrence. Version 3.2.0 also fixes parse('now', tz=...), invalid intervals, Duration deepcopy with weeks, and an incorrect offset calculation in the Rust extension.

Patterns

Construct and convert aware datetimes create-aware-datetime

import pendulum

meeting = pendulum.datetime(
    2026, 9, 15, 9, 30, tz='Europe/Paris'
)
utc_meeting = meeting.in_timezone('UTC')
local_now = pendulum.now('Asia/Kolkata')

Pendulum constructors default to UTC. Name the timezone at application boundaries so local-machine settings cannot change results.

Parse ISO and known custom formats parse-datetime

import pendulum

instant = pendulum.parse('2026-08-26T14:05:00+02:00')
date = pendulum.parse('2026-08-26', tz='America/New_York')
known = pendulum.from_format(
    '26 Aug 2026 14:05',
    'DD MMM YYYY HH:mm',
    tz='UTC',
)

Use `from_format` when the input contract is fixed. Non-strict free-form parsing needs an explicit day-first policy for ambiguous numeric dates.

Choose days or elapsed hours calendar-arithmetic

import pendulum

start = pendulum.datetime(2026, 3, 28, 12, 0, tz='Europe/Paris')
next_local_noon = start.add(days=1)
exactly_one_day_later = start.add(hours=24)
next_month = start.add(months=1)

At a DST boundary, one calendar day and 24 elapsed hours can produce different local clock times. Match the unit to the business rule.

Reject gaps and select folds dst-gap-fold

import pendulum

strict = pendulum.datetime(
    2013, 3, 31, 2, 30,
    tz='Europe/Paris',
    raise_on_unknown_times=True,
)

first = pendulum.datetime(
    2013, 10, 27, 2, 30, tz='Europe/Paris', fold=0
)
second = first.replace(fold=1)

The spring value raises because that local time never occurred. `fold` distinguishes the two autumn occurrences of the same wall time.

Render a localized difference relative-text

import pendulum

past = pendulum.now('UTC').subtract(minutes=2)
english = past.diff_for_humans(locale='en')
french = past.diff_for_humans(locale='fr')
absolute = past.diff_for_humans(absolute=True, locale='en')

Pass locale per call in request-handling code. `pendulum.set_locale()` changes process-wide state.

Build half-open period bounds period-boundaries

import pendulum

now = pendulum.now('UTC')
month_start = now.start_of('month')
next_month_start = month_start.add(months=1)

rows = fetch_between(month_start, next_month_start)

A half-open range avoids relying on `end_of('month')` and its inclusive final microsecond.

Measure and iterate an interval interval-measurement

import pendulum

start = pendulum.datetime(2026, 1, 1, tz='UTC')
end = pendulum.datetime(2026, 1, 4, 6, tz='UTC')
span = pendulum.interval(start, end)

print(span.in_hours())
for day in span.range('days'):
    process(day)

Total methods such as `in_hours()` differ from remainder properties such as `.hours`; choose the one your report means.

Keep months in a duration calendar-duration

import pendulum

term = pendulum.duration(months=1, days=3, hours=4)
renewal = pendulum.datetime(2026, 1, 31, tz='UTC') + term
summary = term.in_words(locale='en')

Adding Pendulum Duration preserves calendar units. Converting to standard timedelta collapses variable-length months and years.

Register Pendulum values with sqlite3 sqlite-adapter

import sqlite3
from pendulum import DateTime

sqlite3.register_adapter(
    DateTime,
    lambda value: value.isoformat(' '),
)
connection = sqlite3.connect('events.db')

Register the adapter before opening database connections because sqlite3 dispatches adapters by exact type.

Wrap a standard datetime convert-native-datetime

from datetime import datetime, timezone
import pendulum

native = datetime.now(timezone.utc)
wrapped = pendulum.instance(native)
back_to_native = datetime.fromisoformat(wrapped.isoformat())

Check timezone awareness before wrapping external values. A naive datetime has no reliable instant until the application assigns a zone.

Format for machines and people format-output

import pendulum

value = pendulum.datetime(2026, 8, 26, 14, 5, tz='Asia/Kolkata')
machine = value.to_iso8601_string()
human = value.format('dddd, D MMMM YYYY HH:mm', locale='en')

Keep the offset in machine output. Localized display strings should not be parsed back as an interchange format.

Freeze Pendulum's clock in a test freeze-time-test

import pendulum

def test_due_date():
    frozen = pendulum.datetime(2026, 8, 26, 9, tz='UTC')
    with pendulum.travel_to(frozen, freeze=True):
        assert pendulum.now('UTC') == frozen

Install `pendulum[test]` first. The 3.x time-travel helpers depend on the optional test extra.

Alternatives

PackageRegistryPick it when
wheneverPyPIUse it when separate types for instants, local datetimes, and zoned datetimes should catch category mistakes.
arrowPyPIUse it for a compact date convenience API after checking whether its timezone and arithmetic rules fit your code.
python-dateutilPyPIChoose it for recurrence rules, flexible parsing, and standard datetime values without adopting Pendulum's object model.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.