NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #403 most downloaded on PyPI
Python datetimes made easy
Last release 8 months ago
30 Jan 2026
Release timing varies
gaps range from 2 weeks to 2.3 years
Nearly every release is documented
notes for 49 of 54 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
57 releases · first in 2016
See CHANGELOG.md for details
See CHANGELOG.md for details
re. methods #741pendulum.parse not being marked as exported #693pendulum.parse('now', tz='...') ignoring the timezone #701pathlib to read Unix TZ data #742Interval deepcopying #850end_of('century') docs #910pytest to be lazy loaded #926Duration deepcopy not including weeks #933Durations not being an error in Python ISO8601 parser implementation #903Duration.in_words() #826pytz #911One column per quarter.
See CHANGELOG.md for details
See CHANGELOG.md for details
pendulum.tz.timezones() to use system tzdata #801DeprecationWarning from utcfromtimestamp #887instance() now supports all native types (date, time and datetime)
instance() now supports all native types (date, time and datetime)Before this release, only a datetime could be given to the instance() helper to get an equivalent DateTime instance.
Now you can give to instance a date or time as well:
import pendulum
from datetime import date, time
pendulum.instance(date(2023, 12, 15))
pendulum.instance(time(12, 34, 56))This release now provides improved testing helpers that rely internally on time-machine.
These helpers are optional and for them to work, you need to opt-in the test extra when installing Pendulum.
poetry add "pendulum[test]"
# Or directly via pip
pip install "pendulum[test]"You can travel in time relatively to the current time
>>> import pendulum
>>> now = pendulum.now()
>>> pendulum.travel(minutes=5)
>>> pendulum.now().diff_for_humans(now)
"5 minutes after"Note that once you've travelled in time the clock keeps ticking. If you prefer to stop the time completely
you can use the freeze parameter:
>>> import pendulum
>>> now = pendulum.now()
>>> pendulum.travel(minutes=5, freeze=True)
>>> pendulum.now().diff_for_humans(now)
"5 minutes after" # This will stay like this indefinitelySometimes, you may want to place yourself at a specific point in time.
This is possible by using the travel_to() helper. This helper accepts a DateTime instance
that represents the point in time where you want to travel to.
>>> import pendulum
>>> pendulum.travel_to(pendulum.yesterday())Similarly to travel, it's important to remember that, by default, the time keeps ticking so, if you prefer
stopping the time, use the freeze parameter:
>>> import pendulum
>>> pendulum.travel_to(pendulum.yesterday(), freeze=True)Using any of the travel helpers will keep you in the past, or future, until you decide
to travel back to the present time. To do so, you may use the travel_back() helper.
>>> import pendulum
>>> now = pendulum.now()
>>> pendulum.travel(minutes=5, freeze=True)
>>> pendulum.now().diff_for_humans(now)
"5 minutes after"
>>> pendulum.travel_back()
>>> pendulum.now().diff_for_humans(now)
"a few seconds after"However, it might be cumbersome to remember to travel back so, instead, you can use any of the helpers as a context
manager:
>>> import pendulum
>>> now = pendulum.now()
>>> with pendulum.travel(minutes=5, freeze=True):
>>> pendulum.now().diff_for_humans(now)
"5 minutes after"
>>> pendulum.now().diff_for_humans(now)
"a few seconds after"Some parts of Pendulum were historically written in C for improved performances. These are now
written in Rust for cleaner, more maintainable code. Performances are also better with this rewrite.
This is the first step towards a more extensive rewrite in Rust of the library. Note that Pendulum
will still provide a pure Python version of the library.
Timezone class now relies on the native zoneinfo.ZoneInfo class. #569Period class to Interval. #676period helper to interval. #676test() and set_test_now(). #626week_of_month property for edge cases in January and December. #774fold attribute when deep-copying a DateTime instance. #776start_of()/end_of() methods. #713test extra. #778week_of_month property for edge cases in January and December. #774fold attribute when deep-copying a DateTime instance. #776Made instance() support all native types (date, time, datetime). #732
Added new testing helpers to time travel. #626
Timezone class now relies on the native zoneinfo.ZoneInfo class. #569Period class to Interval. #676period helper to interval. #676test() and set_test_now(). #626Added support for build scripts without the setup.py file generation in the editable builder ( #2718 ).
Fixed errors where invalid timezones were matched in from_format() ( #374 ).
from_format() (#374).fold attribute was overridden when using replace() (#414).now() was not returning the correct result on DST transitions (#483).parse() function (#452).pl locale (#459).Fixed deprecation warnings due to internal regexps ( #427 ).
is_anniversary() method as an alias of is_birthday() (#298).is_utc() will now return True for any datetime with an offset of 0, similar to the behavior in the 1.* versions (#295)Duration.in_words() will now return 0 milliseconds for empty durations.nth_of("month") (#357).from_format() (#372).DateTime with a fixed timezone (#431).Period instances were not properly compared to other classes, especially timedelta instances (#427).test() helper would not unset the test instance when an exception was raised (#445).week_of_month attribute was not returning the correct value (#446).Z ISO-8601 UTC designator was not parsed as UTC (#448).nl locale.it locale.id locale.nb locale.nn locale.Fixed ISO week dates not being parsed properly in from_format().
from_format().Fixed from_format() not recognizing input strings when the specified pattern had escaped elements.
from_format() not recognizing input strings when the specified pattern had escaped elements.x token for string formatting.from_format()from_format() trying to parse escaped tokens.z token timezone parsing in from_format() to allow underscores.age calculation for future dates.Fixed handling of pytz timezones.
pytz timezones.diff methods.closest()/farthest() methods.Fixed the weeks property for negative Period instances.
weeks property for negative Period instances.start_of() methods not setting microseconds to 0.strftime format.Fixed behavior of the YY token in from_format().
YY token in from_format().Added years and months support to durations.
test_local_timezone() and set_local_timezone() helpers to ease testing.local() helper.naive() helper and a naive() method.Pendulum class has been renamed to DateTime.Interval class has been renamed to Duration.create() helper.utcnow() helper.strict keyword argument for parse has been renamed to exact.at() now supports setting partial time.local, utc and is_dst are now methods rather than properties (is_local(), is_utc(), is_dst()).repr of most common objects.strict keyword argument for parse false by default, which means it will not fallback on the dateutil parser.precise_diff() helper.alternative formatter is now the default one.set_to_string_format()/reset_to_string_format() methods have been removed.from_format() now uses the alternative formatter tokens.xrange() method of the Period class and made range() a generator.diff_for_humans() now returns a few seconds where appropriate.Period.intersect().test_local_timezone() and set_local_timezone() helpers to ease testing.local() helper.naive() helper and a naive() method.Pendulum class has been renamed to DateTime.Interval class has been renamed to Duration.create() helper.utcnow() helper.strict keyword argument for parse has been renamed to exact.at() now supports setting partial time.local, utc and is_dst are now methods rather than properties (is_local(), is_utc(), is_dst()).repr of most common objects.strict keyword argument for parse false by default, which means it will not fallback on the dateutil parser.precise_diff() helper.alternative formatter is now the default one.set_to_string_format()/reset_to_string_format() methods have been removed.from_format() now uses the alternative formatter tokens.xrange() method of the Period class and made range() a generator.diff_for_humans() now returns a few seconds where appropriate.Period.intersect().Nothing published for this version
Fixed set() not acception the tz keyword argument.
set() not acception the tz keyword argument.datetime() not setting the timezone to UTC by default.year_(), month_(), day_(), hour_(), minute_(), second_(), microsecond_() are now deprecated.
datetime() helper.set() method to set properties.is_utc() and is_local() methods.year_(), month_(), day_(), hour_(), minute_(), second_(), microsecond_() are now deprecated.timezone_() and tz_() are now deprecated.timestamp_() is now deprecated.with_date_time(), with_time_from_string() and with_timestamp() are now deprecated.between() is now deprecated.min_(), max_(), minimum(), maximum() are now deprecated.is_today(), is_yesterday(), is_tomorrow() and is_same_day() are now depecreated.is_sunday() -> is_saturday() are now deprecated.utc and local properties are now deprecated. Use is_utc() and is_local() instead.Fixed extension building script.
Fixed an error when adding intervals to a Pendulum instance across DST transition.
Nothing published for this version
Fixed an error when comparing a Period to a timedelta in PyPy.
format(), diff_for_humans(), in_words() and to_xxx_string() methods now return unicode strings for Python 2.7.
format(), diff_for_humans(), in_words() and to_xxx_string() methods now return unicode strings for Python 2.7.now() and utcnow(). (Thanks to pganssle)from_format() raising an error with the alternative formatter.Fixed instance() behaviour with pytz offsets. (Thanks to pR0Ps)
instance() behaviour with pytz offsets. (Thanks to pR0Ps)TimezoneInfo. (Thanks to bolkedebruin)Fixed inaccuracy of in_days() method on DST transitions.
in_days() method on DST transitions.The classic formatter in from_format() is now deprecated.
from_format().timezones module attribute to expose available timezones.exact keyword to parse() which behaves exactly like strict.classic formatter in from_format() is now deprecated.th locale. (Thanks to idxn)Fixed normalization of microseconds in durations.
average(). (Thanks to ericfrederich)Nothing published for this version
Fixed behavior of some short timezones (like EST, MST or HST).
Fixed next() and previous() hanging when passed an invalid input.
next() and previous() hanging when passed an invalid input.Fixed incorrect fold attribute on Python 3.6 when not passing a timezone. (Thanks to neonquill)
fold attribute on Python 3.6 when not passing a timezone. (Thanks to neonquill)Added support for EXIF-formatted strings in parser. (Thanks to emattiza)
Fixed diff_for_humans() when crossing DST transitions.
diff_for_humans() when crossing DST transitions.Added the keep_time keyword argument to next()/previous() methods to keep time information.
keep_time keyword argument to next()/previous() methods to keep time information.diff() performance.diff_for_humans() method to display more intuitive strings on edge cases.format()) will now use the configured formatter.Adds support for external tzinfo as timezones. (Thanks to iv597)
Fixed parsing, especially for strings in the form 31-01-01.
31-01-01.What does that mean? Well, the API is now stable, so no backwards compatibility breaking changes will be introduced in any 1.x releases (pendulum will…
After more than 300 commits and 21 beta releases, pendulum finally reaches its first stable version.
What does that mean? Well, the API is now stable, so no backwards compatibility breaking changes will be introduced in any 1.x releases (pendulum will follow Semantic Versioning).
PRE_TRANSITION rule no longer produces a time in a DST gap.Pendulum instance.with_date() and with_time() are deprecated. Use on() and at() instead.
on() and at() methods which replace with_date() and with_time().strict keyword argument to parse() to get the type matching the parsed string.range() method to control the length of the gap.datetime() helper method to the Timezone class.with_date() and with_time() are deprecated. Use on() and at() instead.create_from_date() and create_from_time() are deprecated. Use create() instead.Added a int_timestamp property to the Pendulum class to retrieve the behavior of the now deprecated timestamp property.
Date class.Time class.fold attribute introduced in Python 3.6.remaining_days property to the Interval class.int_timestamp property to the Pendulum class to retrieve the behavior of the now deprecated timestamp property.start_of()/end_of() now supports hour, minute and second units.astimezone() now supports timezone strings.in_words() now displays subseconds when no other units are available.Period properties (especially years and months) are now accurate.Interval.seconds now returns the whole number of remaining seconds, like timedelta, for compatibility. Use remaining_seconds to retrieve the previous behavior.pytz. It now depends on pytzdata for its timezone database.timestamp should now be used as a method and no longer as a property. It will be a native method in the next major version.Interval properties and methods related to years and months are now deprecated.Interval.days_exclude_weeks is now deprecated. Use remaining_days instead.end_of('day') now properly sets microseconds to 999999.Period instances properties has been improved.pickle.minute_(), second_() and microsecond_() setters changing the hour unit.Fixed a memory leak in C extension. (thanks to ntoll)
Adds validation to set_week_starts_at(), set_week_ends_at() and set_weekend_days(). (thanks to kleschenko)
set_week_starts_at(), set_week_ends_at() and set_weekend_days(). (thanks to kleschenko)Timezone.utcoffset(). (thanks to regnarock)Adds support for pytz timezones in constructor.
pytz timezones in constructor.add()/subtract() methods for years, months and days when a DST transition occurs.range() behavior.Makes replace() accept the same tzinfo types as the constructor.
replace() accept the same tzinfo types as the constructor.timezone_() not setting the tzinfo properly.Fixes timezones loading on Windows
Pendulum instances can no longer be compared to strings and integers.
Pendulum instances can no longer be compared to strings and integers.Timezone._convert() method for fixed timezones.instances() for some tzinfo.Adds an option to control transition normalization behavior.
Interval.in_words() method.tzinfo instance in the instance() method.intersect() method to the Period class.create*() methods (time now defaults to 00:00:00)[Fix] Fixes local timezone loading for unix systems.
AttributeError in between method. (Thanks to iv597)[Fix] Fixes broken previous release.
[Fix] Fixes setters and modifiers (start_of/end_of) to properly apply DST transitions.
[Fix] Fixes TimezoneInfo.utcoffset() method.
TimezoneInfo.utcoffset() method.[Fix] Fixes now() not behaving properly when given a timezone.
now() not behaving properly when given a timezone.pt_BR locale. (Thanks to YomoFuno)pl locale. (Thanks to MichalTHEDUDE)This version introduces a new timezone library which improves timezone switching and behavior around DST transition times.
This version introduces a new timezone library which improves timezone switching and behavior around DST transition times.
Pendulum no longer relies on pytz.
Check the Documentation to see what has changed exactly.This version mostly brings the new Period class and improves performances overall.
This version mostly brings the new <code>Period</code> class and improves performances overall.
Period class, which basically is a datetime-aware interval.format() method support a locale keyword argument.%P becomes %_z and %t becomes %_t.
Basically, all new custom directives will be in the form %_{directive}Fixes parsing of string with partial offset.
This version causes major breaking API changes to simplify it and making it more intuitive.
This version causes major breaking API changes to simplify it and making it more intuitive.
test() contextmanager.datetime to instance() default to UTC.add_xxx()/sub_xxx() methods to add(**kwargs)/subtract(**kwargs).for_humans() method of the Interval class to in_words() to avoid confusion with the diff_for_humans() method.Pendulum().to() method to in_timezone().first_of_xxx()/last_of_xxx()/nth_of_xxx() methods to first_of(unit)/last_of(unit)/nth_of(unit, nth).start_of_xxx()/end_of_xxx() methods to start_of(unit)/end_of(unit).diff_in_xxx() methods from the Pendulum and adds in_xxx() methods to the Interval class.PendulumInterval class to simply Interval.Pendulum class immutable.Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →