Back to Blog
Python

Python Pendulum: Parsing, Formatting, and Converting Timezone-Aware Datetimes

Learn how to parse timezone-aware datetime strings with Python Pendulum, format datetimes with custom tokens, and convert between timezones.

pendulumdatetimetimezoneparsingformattingpython
Illustration of a clock with multiple timezone hands representing Python Pendulum timezone-aware datetime parsing and formatting

Pendulum provides a more intuitive API than the Python standard library for working with timezone-aware datetimes. This article covers parsing datetime strings, custom formatting, timezone conversion, and the difference between naive and aware datetime values.

Parsing datetime strings with Pendulum

pendulum.parse() accepts most ISO 8601 strings. When the string includes an offset or timezone name, the returned DateTime is timezone-aware. The timezone_name property reports the offset when the input uses a fixed offset.

import pendulum dt = pendulum.parse('2023-01-01T12:00:00+02:00') print(dt) # 2023-01-01 12:00:00+02:00 print(dt.timezone_name) # +02:00

If the string has no timezone information, parse() applies Pendulum's default timezone rather than returning a naive datetime. If you know the exact format, use from_format() with Pendulum's own tokens:

dt = pendulum.from_format('2023-01-01 12:00', 'YYYY-MM-DD HH:mm')

from_format() is a good choice when you know the format ahead of time, because it skips format detection.

Formatting datetimes with timezone information

Use to_iso8601_string() for a standard representation:

dt = pendulum.now('Europe/Paris') print(dt.to_iso8601_string()) # e.g., 2025-03-15T14:30:00+01:00

For custom output, use format() with Pendulum tokens. Z emits the offset and z emits the timezone name:

print(dt.format('YYYY-MM-DD HH:mm:ss Z')) # e.g., 2025-03-15 14:30:00 +01:00 print(dt.format('YYYY-MM-DD HH:mm:ss z')) # e.g., 2025-03-15 14:30:00 Europe/Paris

Pendulum also offers convenience methods like to_day_datetime_string() and to_cookie_string() for common use cases.

Converting between timezones

in_timezone() (or its alias in_tz()) converts an aware datetime to another timezone without changing the instant:

dt_utc = pendulum.now('UTC') dt_new_york = dt_utc.in_timezone('America/New_York') print(dt_new_york) # same instant displayed in New York time

Pendulum objects are immutable, so conversion returns a new object and leaves the original unchanged. Microseconds are preserved.

Handling naive and aware datetimes

Use is_naive() and is_aware() to inspect a datetime. To create a truly naive datetime, use pendulum.naive(), while pendulum.datetime() creates an aware datetime when given a timezone:

naive = pendulum.naive(2023, 1, 1, 12, 0) aware = pendulum.datetime(2023, 1, 1, 12, 0, tz='UTC') print(naive.is_naive()) # True print(aware.is_aware()) # True

To attach a timezone to a naive datetime without changing its wall time, use set_timezone():

naive = pendulum.naive(2023, 1, 1, 12, 0) aware = naive.set_timezone('America/New_York') print(aware) # 2023-01-01 12:00:00-05:00

If you need to convert an aware datetime to another timezone instead, use in_timezone().

Common pitfalls in timezone parsing and formatting

Pendulum's format tokens are not strftime tokens. For example, from_format('2023-01-01 12:00', '%Y-%m-%d %H:%M') will fail; use 'YYYY-MM-DD HH:mm'. Also, Z outputs the offset (+01:00) and z outputs the timezone name (Europe/Paris); using the wrong one produces a different string.

DST transitions are another source of errors. When you construct or parse a local wall time in a spring-forward gap, the time may not exist. If you are only converting an already-valid aware datetime, in_timezone() preserves the instant and selects the correct DST offset. For local wall-time inputs, be explicit about how you want nonexistent or ambiguous times resolved. To reduce risk, prefer parsing strings that include an offset or timezone name so the result is a concrete instant.

Practical considerations

Pendulum adds a dependency, but its timezone API reduces boilerplate compared to manual pytz or zoneinfo code. When parsing a known format repeatedly, define a constant format string and reuse it with from_format(); this avoids format detection. Because Pendulum objects are immutable, methods like in_timezone() allocate a new object, which is normally fine but worth remembering in tight loops.

Pendulum in Python: Parse, Format, and Convert Timezone-Aware Datetimes | RYUSLOG DEV