Pendulum is more than just a datetime library, it's also an efficient timezone library.

In Python, when you think about timezone handling you usually think pytz which is a useful library. However it is pretty slow, especially to localize and normalize naive datetime (it's less clear when localizing timezone-aware datetimes since both libraries rely on standard datetime operations.).

It also has some bugs that have not been fixed in quite some time (as you can see on its bug tracker https://bugs.launchpad.net/pytz).

So, Pendulum brings its own, more efficient, timezone library with a more intuitive API (you only need to use the convert() method).

Benchmarks

Each benchmark was done for Python 2.7, PyPy and Python 3.5. timeit was used with the default of 1 million executions per benchmark and the best of 3 repeats was picked.

Normalize and localize a naive datetime

The setup for the benchmark is the following:

import pytz
import pendulum
from datetime import datetime

dt = datetime(2013, 3, 31, 2, 30)
t1 = pytz.timezone('Europe/Paris')
t2 = pendulum.timezone('Europe/Paris')

The code executed for pytz:

t1.normalize(t1.localize(dt))

The code executed for pendulum:

t2.convert(dt)

The results:

Time in seconds (less is better)

Both libraries return a properly localized and normalized datetime object:

>>> t1.normalize(t1.localize(dt)).isoformat()
'2013-03-31T03:30:00+02:00'

>>> t2.convert(dt).isoformat()
'2013-03-31T03:30:00+02:00'

Normalize and localize a timezone-aware datetime

The setup for the benchmark is the following:

import pytz
import pendulum
from datetime import datetime

dt = datetime(2013, 3, 31, 2, 30)
t1 = pytz.timezone('Europe/Paris')
t2 = pendulum.timezone('Europe/Paris')
t3 = pytz.timezone('America/New_York')
t4 = pendulum.timezone('America/New_York')

loc1 = t1.normalize(t1.localize(dt))
loc2 = t2.convert(dt)

The code executed for pytz:

loc1.astimezone(t3)

The code executed for pendulum:

t4.convert(loc2)

The results:

Time in seconds (less is better)

Both libraries return the proper datetime object in the new timezone:

>>> loc1.astimezone(t3).isoformat()
'2013-03-30T21:30:00-04:00'

>>> t4.convert(loc2).isoformat()
'2013-03-30T21:30:00-04:00'

A quick comparison

Here are some examples where pytz will behave strangely:

Another example:

>>> pytz.timezone('Africa/Abidjan').localize(datetime(year=1, month=1, day=1))
# OverflowError: date value out of range
>>> pytz.timezone('Africa/Abidjan').localize(datetime(year=9999, month=12, day=31))
# OverflowError: date value out of range

With pendulum:

>>> timezone('Africa/Abidjan').convert(datetime(year=1, month=1, day=1))
# datetime.datetime(1, 1, 1, 0, 0, tzinfo=<TimezoneInfo [Africa/Abidjan, LMT, -1:43:52, STD]>)
>>> timezone('Africa/Abidjan').convert(datetime(year=9999, month=12, day=31))
# datetime.datetime(9999, 12, 31, 0, 0, tzinfo=<TimezoneInfo [Africa/Abidjan, GMT, +00:00:00, STD]>)