Converting between types

Between exact types

You can convert between exact types with the to_instant(), to_fixed_offset(), to_tz(), and to_system_tz() methods. These methods return a new instance of the appropriate type, representing the same moment in time. This means the results will always compare equal to the original datetime.

>>> d = ZonedDateTime(2023, 12, 28, 11, 30, tz="Europe/Amsterdam")
>>> d.to_instant()  # The underlying moment in time
Instant("2023-12-28 10:30:00Z")
>>> d.to_fixed_offset(5)  # same moment with a +5:00 offset
OffsetDateTime("2023-12-28 15:30:00+05:00")
>>> d.to_tz("America/New_York")  # same moment in New York
ZonedDateTime("2023-12-28 05:30:00-05:00[America/New_York]")
>>> d.to_system_tz()  # same moment in the system timezone (e.g. Europe/Paris)
ZonedDateTime("2023-12-28 11:30:00+01:00[Europe/Paris]")
>>> d.to_fixed_offset(4) == d
True  # always the same moment in time

To and from local time

Conversion to a “plain” datetime is easy: calling to_plain() simply retrieves the date and time part of the datetime, and discards the any timezone or offset information.

>>> d = ZonedDateTime(2023, 12, 28, 11, 30, tz="Europe/Amsterdam")
>>> n = d.to_plain()
PlainDateTime("2023-12-28 11:30:00")

You can convert from plain datetimes with the assume_utc(), assume_fixed_offset(), assume_tz(), and assume_system_tz() methods.

>>> n = PlainDateTime(2023, 12, 28, 11, 30)
>>> n.assume_utc()
Instant("2023-12-28 11:30:00Z")
>>> n.assume_tz("Europe/Amsterdam")
ZonedDateTime("2023-12-28 11:30:00+01:00[Europe/Amsterdam]")

Similarly, you can associate an OffsetDateTime with a timezone using assume_tz():

>>> o = OffsetDateTime(2023, 12, 28, 11, 30, offset=1)
>>> o.assume_tz("Europe/Amsterdam")
ZonedDateTime("2023-12-28 11:30:00+01:00[Europe/Amsterdam]")

By default, this raises an error if the offset doesn’t match the timezone. You can control what happens in case of a mismatch with the offset_mismatch argument:

>>> o = OffsetDateTime(2023, 12, 28, 11, 30, offset=5)
>>> o.assume_tz("Europe/Amsterdam", offset_mismatch="keep_instant")
ZonedDateTime("2023-12-28 07:30:00+01:00[Europe/Amsterdam]")
>>> o.assume_tz("Europe/Amsterdam", offset_mismatch="keep_local")
ZonedDateTime("2023-12-28 11:30:00+01:00[Europe/Amsterdam]")

Tip

The naming difference between to_* and assume_* methods is intentional. See the FAQ for the rationale.

```{admonition} When is assume_tz useful? :class: hint

A common scenario is receiving timestamps from an external source (API, database, log file) that only carries a fixed offset. If the timezone rules for that region change later—for example, a country abolishes DST—the stored offset may no longer be valid for arithmetic on future dates. assume_tz() lets you associate the correct timezone so that subsequent operations account for the current rules.