pendulum / arrow
pendulum and arrow improve on datetime ergonomics - parsing fuzzy strings, fluent deltas, timezone conversion, and human-readable diffs. Python 3.9+ zoneinfo covers many needs, but these libraries still shine in CLIs, reports, and legacy codebases.
Recipe
import pendulum
now = pendulum.now("UTC")
tomorrow = now.add(days=1)
parsed = pendulum.parse("2026-07-09 14:30", tz="America/Chicago")
print(parsed.in_timezone("Europe/London").to_iso8601_string())When to reach for this:
- Parsing inconsistent date strings from CSV exports or third-party APIs
- Displaying relative time ("2 hours ago") in admin tools
- Scheduling reports in local time with DST awareness
- Quick scripts where
zoneinfoboilerplate feels heavy
Working Example
from __future__ import annotations
import pendulum
def next_billing_run(anchor: str, tz: str = "UTC") -> pendulum.DateTime:
"""Return next monthly run at 02:00 local on the anchor day."""
base = pendulum.parse(anchor, tz=tz)
run = base.replace(hour=2, minute=0, second=0, microsecond=0)
now = pendulum.now(tz)
while run <= now:
run = run.add(months=1)
return run.in_timezone("UTC")
def format_relative(dt: pendulum.DateTime) -> str:
return dt.diff_for_humans()
if __name__ == "__main__":
utc_run = next_billing_run("2026-01-15", tz="America/New_York")
print(utc_run.to_iso8601_string())
print(format_relative(pendulum.now("UTC").subtract(hours=3)))What this demonstrates:
pendulum.parseaccepts ISO-like strings with explicit tzadd(months=1)handles month-end edge cases better than manual timedelta- Billing anchor computed in local tz, stored/exported as UTC
diff_for_humansfor operator-facing messages
Deep Dive
How It Works
- pendulum - Subclasses
datetime; instances are mostly compatible with stdlib APIs. - arrow - Similar fluent API;
arrow.get()parses many formats;shift()adds calendar units. - Timezones - Both use the IANA tz database (pendulum bundles tzdata on Windows).
- stdlib overlap - Python 3.14
datetime+zoneinfo+dateutil.parsercovers parsing for strict ISO pipelines.
pendulum vs arrow vs stdlib
| Library | Strength | Trade-off |
|---|---|---|
| pendulum | Mutable-friendly helpers, duration humanization | Extra dependency |
| arrow | Compact API, good for quick scripts | Smaller community than pendulum |
| zoneinfo + datetime | No dependency, explicit | More boilerplate for fuzzy parse |
Python Notes
from datetime import datetime
from zoneinfo import ZoneInfo
# Stdlib equivalent for storage boundary
utc = datetime.now(ZoneInfo("UTC"))
local = utc.astimezone(ZoneInfo("America/Chicago"))Gotchas
- Naive datetimes in databases - DST bugs and ambiguous local times. Fix:
TIMESTAMPTZ+ UTC storage. - Parsing without timezone -
"2026-07-09"means different instants globally. Fix: require offset or default org tz explicitly. - Mixing pendulum and stdlib unaware - Some APIs expect
datetimeonly. Fix:.naive()or convert via.in_timezone("UTC")to stdlib. - Locale-dependent parse -
"09/07/2026"is ambiguous (US vs EU). Fix: enforce ISO 8601 at API boundaries. - Using
.timestamp()on naive values - Wrong epoch on servers not in UTC. Fix: attach tz before timestamp math. - Heavy libs for one format call - Import cost in Lambda cold starts. Fix: stdlib
fromisoformatfor strict ISO only.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
zoneinfo + datetime | New services with strict ISO inputs | Fuzzy human-entered dates |
python-dateutil | Parser flexibility without full pendulum | You want humanized diffs built-in |
pandas.to_datetime | Columnar ETL in DataFrames | Non-tabular app code |
| Store epoch ms in JSON | API contracts with integer timestamps | You need calendar month math |
FAQs
pendulum or arrow in 2026?
Both work; pendulum has broader maintenance and docs. For greenfield apps with strict ISO APIs, zoneinfo may be enough.
Are pendulum objects JSON-serializable?
Convert to ISO string before JSON - same rule as stdlib datetime.
How do I test time-dependent code?
Inject a clock callable or use pendulum.set_test_now() / freezegun in tests.
Does pendulum handle leap seconds?
Same as stdlib - POSIX timestamps ignore leap seconds; use UTC for logs regardless.
How do month additions work on Jan 31?
Libraries typically clamp or roll to month-end - verify behavior matches billing rules.
Can I use pendulum with SQLAlchemy?
Yes - store UTC datetime columns; convert at read for display layers.
What about Django timezone support?
Django's timezone utilities integrate with zoneinfo; pendulum is optional in management commands.
How do I parse RFC 2822 email dates?
email.utils.parsedate_to_datetime in stdlib - no need for pendulum unless you want fluent chaining.
Arrow still maintained?
Check PyPI release cadence before new deps - pendulum is the safer default for new projects.
How do I format for logs?
Always log ISO 8601 UTC with offset: 2026-07-09T14:30:00+00:00.
Related
- datetime, zoneinfo & Time - stdlib foundation
- Data Validation - date fields in pipelines
- pydantic-settings - config with date types
- openpyxl / python-docx / reportlab - dates in spreadsheets
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.