ZonedDateTime

final class whenever.ZonedDateTime(iso_string: str, /)[source]
final class whenever.ZonedDateTime(py_dt: datetime, /)
final class whenever.ZonedDateTime(
year: int,
month: int,
day: int,
hour: int = 0,
minute: int = 0,
second: int = 0,
*,
nanosecond: int = 0,
tz: str,
disambiguate: DisambiguateStr = 'compatible',
)

A datetime associated with a timezone from the IANA database.

This is the right type when you need both the exact moment and the local date/time at a specific location. Arithmetic is fully DST-aware: the offset is always kept in sync with the timezone rules.

>>> ZonedDateTime("2024-12-08T11[Europe/Paris]")
ZonedDateTime("2024-12-08 11:00:00+01:00[Europe/Paris]")
>>> # Explicitly resolve ambiguities during DST transitions
>>> ZonedDateTime(2023, 10, 29, 1, 15, tz="Europe/London", disambiguate="earlier")
ZonedDateTime("2023-10-29 01:15:00+01:00[Europe/London]")
>>> # From a standard library datetime (must have a ZoneInfo tzinfo)
>>> ZonedDateTime(datetime(2020, 8, 15, 23, 12, tzinfo=ZoneInfo("Europe/London")))
ZonedDateTime("2020-08-15 23:12:00+01:00[Europe/London]")

Convert to other types to discard timezone information:

>>> d = ZonedDateTime(2024, 7, 1, 12, tz="Europe/Amsterdam")
>>> d.to_instant()
Instant("2024-07-01 10:00:00Z")
>>> d.to_plain()
PlainDateTime("2024-07-01 12:00:00")

Important

To use this type properly, read more about ambiguity in timezones.

classmethod from_py_datetime(d: datetime, /) _T

Create an instance from a datetime object.

Deprecated since version 0.10.0: Use the constructor instead (e.g. Instant(d), ZonedDateTime(d), etc.)

Note

The datetime is checked for validity, raising similar exceptions to the constructor. ValueError is raised if the datetime doesn’t have the correct tzinfo matching the class. For example, ZonedDateTime requires a ZoneInfo tzinfo.

Warning

No exceptions are raised if the datetime is ambiguous. Its fold attribute is used to disambiguate.

classmethod from_system_tz(
year: int,
month: int,
day: int,
hour: int = 0,
minute: int = 0,
second: int = 0,
*,
nanosecond: int = 0,
disambiguate: DisambiguateStr = 'compatible',
) ZonedDateTime[source]

Create an instance in the system timezone.

Equivalent to ZonedDateTime(..., tz=<the system timezone>), except it also works for system timezones whose corresponding IANA timezone ID is unknown.

>>> ZonedDateTime.from_system_tz(2020, 8, 15, hour=23, minute=12)
ZonedDateTime("2020-08-15 23:12:00+02:00[Europe/Berlin]")
classmethod from_timestamp(i: int | float, /, *, tz: str) ZonedDateTime[source]

Create an instance from a UNIX timestamp (in seconds).

The inverse of the timestamp() method.

classmethod from_timestamp_millis(i: int, /, *, tz: str) ZonedDateTime[source]

Create an instance from a UNIX timestamp (in milliseconds).

The inverse of the timestamp_millis() method.

classmethod from_timestamp_nanos(i: int, /, *, tz: str) ZonedDateTime[source]

Create an instance from a UNIX timestamp (in nanoseconds).

The inverse of the timestamp_nanos() method.

classmethod now(tz: str, /) ZonedDateTime[source]

Create an instance from the current time in the given timezone.

classmethod now_in_system_tz() ZonedDateTime[source]

Create an instance from the current time in the system timezone.

Equivalent to Instant.now().to_system_tz().

classmethod parse(s: str, /, *, format: str, disambiguate: DisambiguateStr = 'compatible') ZonedDateTime[source]

Parse a zoned datetime from a custom pattern string.

The pattern must include a timezone ID field (VV). An offset field (x/X) is optional but recommended for disambiguation during DST transitions. See Pattern format for details.

Tip

If your input string doesn’t include a timezone ID, parse it with PlainDateTime.parse() first, then convert using assume_tz().

>>> ZonedDateTime.parse(
...     "2024-03-15 14:30+01:00[Europe/Paris]",
...     format="YYYY-MM-DD hh:mmxxx'['VV']'",
... )
ZonedDateTime("2024-03-15 14:30:00+01:00[Europe/Paris]")
classmethod parse_iso(s: str, /) ZonedDateTime[source]

Parse from the popular ISO format YYYY-MM-DDTHH:MM:SS±HH:MM[TZ_ID]

The inverse of the format_iso() method.

>>> ZonedDateTime.parse_iso("2020-08-15T23:12:00+01:00[Europe/London]")
ZonedDateTime("2020-08-15 23:12:00+01:00[Europe/London]")

Important

The timezone ID is a recent extension to the ISO 8601 format (RFC 9557). Although it is gaining popularity, it is not yet widely supported.

__add__(
delta: TimeDelta | DateDelta | DateTimeDelta,
) ZonedDateTime[source]

Add an amount of time, accounting for timezone changes (e.g. DST).

See the docs for more information.

__eq__(other: object) bool

Check if two datetimes represent at the same moment in time

a == b is equivalent to a.to_instant() == b.to_instant()

Note

If you want to exactly compare the values on their values instead, use exact_eq().

>>> Instant.from_utc(2020, 8, 15, hour=23) == Instant.from_utc(2020, 8, 15, hour=23)
True
>>> OffsetDateTime(2020, 8, 15, hour=23, offset=1) == (
...     ZonedDateTime(2020, 8, 15, hour=18, tz="America/New_York")
... )
True
__format__(spec: str, /) str[source]

Default object formatter.

Return str(self) if format_spec is empty. Raise TypeError otherwise.

__ge__(other: Instant | OffsetDateTime | ZonedDateTime) bool

Compare two datetimes by when they occur in time

a >= b is equivalent to a.to_instant() >= b.to_instant()

>>> OffsetDateTime(2020, 8, 15, hour=19, offset=-8) >= (
...     ZoneDateTime(2020, 8, 15, hour=20, tz="Europe/Amsterdam")
... )
True
__gt__(other: Instant | OffsetDateTime | ZonedDateTime) bool

Compare two datetimes by when they occur in time

a > b is equivalent to a.to_instant() > b.to_instant()

>>> OffsetDateTime(2020, 8, 15, hour=19, offset=-8) > (
...     ZoneDateTime(2020, 8, 15, hour=20, tz="Europe/Amsterdam")
... )
True
__le__(other: Instant | OffsetDateTime | ZonedDateTime) bool

Compare two datetimes by when they occur in time

a <= b is equivalent to a.to_instant() <= b.to_instant()

>>> OffsetDateTime(2020, 8, 15, hour=23, offset=8) <= (
...     ZoneDateTime(2020, 8, 15, hour=20, tz="Europe/Amsterdam")
... )
True
__lt__(other: Instant | OffsetDateTime | ZonedDateTime) bool

Compare two datetimes by when they occur in time

a < b is equivalent to a.to_instant() < b.to_instant()

>>> OffsetDateTime(2020, 8, 15, hour=23, offset=8) < (
...     ZoneDateTime(2020, 8, 15, hour=20, tz="Europe/Amsterdam")
... )
True
__str__() str

Return str(self).

__sub__(
other: Instant | OffsetDateTime | ZonedDateTime,
) TimeDelta[source]
__sub__(other: TimeDelta) ZonedDateTime

Subtract another datetime or duration.

See the docs for more information.

add(
d: DateTimeDelta | TimeDelta | DateDelta | ItemizedDelta | ItemizedDateDelta,
/,
*,
disambiguate: DisambiguateStr = ...,
) ZonedDateTime[source]
add(
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
hours: float = ...,
minutes: float = ...,
seconds: float = ...,
milliseconds: float = ...,
microseconds: float = ...,
nanoseconds: int = ...,
disambiguate: DisambiguateStr = ...,
) ZonedDateTime

Return a new ZonedDateTime shifted by the given time amounts

Important

Shifting by calendar units (e.g. months, weeks) may result in an ambiguous time (e.g. during a DST transition). Therefore, when adding calendar units, it’s recommended to specify how to handle such a situation using the disambiguate argument.

See the documentation for more information.

date() Date

The date part of the datetime

>>> d = PlaineDateTime("2020-01-02 03:04:05")
>>> d.date()
Date("2021-01-02")

To perform the inverse, use Date.at() and a method like assume_utc() or assume_tz():

>>> date.at(time).assume_tz("Europe/London")
ZonedDateTime("2021-01-02T03:04:05+00:00[Europe/London]")
day_length() TimeDelta[source]

The duration between the start of the current day and the next. This is usually 24 hours, but may be different due to timezone transitions.

>>> ZonedDateTime(2020, 8, 15, tz="Europe/London").day_length()
TimeDelta(24:00:00)
>>> ZonedDateTime(2023, 10, 29, tz="Europe/Amsterdam").day_length()
TimeDelta(25:00:00)
difference(
other: Instant | OffsetDateTime | ZonedDateTime,
/,
) TimeDelta

Calculate the difference between two instants in time.

Deprecated since version 0.10.0: Use the subtraction operator instead

dst_offset() TimeDelta[source]

The DST offset (adjustment) as a TimeDelta.

>>> ZonedDateTime(2020, 8, 15, tz="Europe/London").dst_offset()
TimeDelta("PT1h")
>>> ZonedDateTime(2020, 1, 15, tz="Europe/London").dst_offset()
TimeDelta("PT0s")

This value is TimeDelta.ZERO when DST is not active:

>>> if zoned_dt.dst_offset():
...     print("DST is active")

Note

Some timezones have unusual DST rules. For example, Europe/Dublin defines its standard time as IST (UTC+1) and uses “negative DST” in winter. In such cases, this method returns a negative value during winter.

exact_eq(other: ZonedDateTime, /) bool[source]

Compare objects by their values (instead of whether they represent the same instant). Different types are never equal.

>>> a = OffsetDateTime(2020, 8, 15, hour=12, offset=1)
>>> b = OffsetDateTime(2020, 8, 15, hour=13, offset=2)
>>> a == b
True  # equivalent instants
>>> a.exact_eq(b)
False  # different values (hour and offset)
>>> a.exact_eq(Instant.now())
TypeError  # different types

Note

If a.exact_eq(b) is true, then a == b is also true, but the converse is not necessarily true.

format(pattern: str, /) str[source]

Format as a custom pattern string.

See Pattern format for details.

>>> ZonedDateTime(2024, 3, 15, 14, 30, tz="Europe/Paris").format(
...     "YYYY-MM-DD hh:mmxxx'['VV']'"
... )
'2024-03-15 14:30+01:00[Europe/Paris]'
format_iso(
*,
unit: Literal['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond', 'auto'] = 'auto',
basic: bool = False,
sep: Literal['T', ' '] = 'T',
tz: Literal['always', 'never', 'auto'] = 'always',
) str[source]

Convert to the popular ISO format YYYY-MM-DDTHH:MM:SS±HH:MM[TZ_ID].

The inverse of the parse_iso() method.

>>> zdt = ZonedDateTime(2020, 8, 15, hour=23, minute=12, tz="Europe/London")
>>> zdt.format_iso(unit="minute", basic=True)
"20200815T2312+0100[Europe/London]"
Parameters:
  • unit – The smallest unit to include in the output. "auto" is the same as "nanosecond", except that trailing zeroes are omitted from the time part.

  • basic – Whether to use the basic ISO format (without separators) instead of the extended one.

  • sep – The separator between the date and time parts.

  • tz – Whether to include the timezone ID in the output. "always" (default) raises an error if the timezone ID is not available (in practice, this should only happen for some system timezones without a corresponding IANA timezone ID). "auto" includes the ID if available, and omits it otherwise. "never" always omits the ID.

Important

The timezone ID is a recent extension to the ISO 8601 format (RFC 9557). Although it is gaining popularity, it is not yet widely supported by ISO 8601 parsers.

is_ambiguous() bool[source]

Whether the date and time-of-day are ambiguous, e.g. due to a DST transition.

>>> ZonedDateTime(2020, 8, 15, 23, tz="Europe/London").is_ambiguous()
False
>>> ZonedDateTime(2023, 10, 29, 2, 15, tz="Europe/Amsterdam").is_ambiguous()
True
py_datetime() datetime

Convert to a standard library datetime

Deprecated since version 0.10.0: Use to_stdlib() instead.

replace(
year: int = ...,
month: int = ...,
day: int = ...,
hour: int = ...,
minute: int = ...,
second: int = ...,
*,
nanosecond: int = ...,
tz: str = ...,
disambiguate: DisambiguateStr = ...,
) ZonedDateTime[source]

Construct a new instance with the given fields replaced.

Important

Replacing fields of a ZonedDateTime may result in an ambiguous time (e.g. during a DST transition). Therefore, it’s recommended to specify how to handle such a situation using the disambiguate argument.

By default, if the tz remains the same, the offset is used to disambiguate if possible, falling back to the “compatible” strategy if needed.

See the documentation for more information.

replace_date(date: Date, /, disambiguate: DisambiguateStr = ...) ZonedDateTime[source]

Construct a new instance with the date replaced.

See the replace() method for more information.

replace_time(time: Time, /, disambiguate: DisambiguateStr = ...) ZonedDateTime[source]

Construct a new instance with the time replaced.

See the replace() method for more information.

round(
unit: Literal['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'] | TimeDelta = 'second',
/,
*,
increment: int = 1,
mode: RoundModeStr = 'half_even',
) ZonedDateTime[source]

Round the datetime to the specified unit and increment, or to a multiple of a TimeDelta. Different rounding modes are available.

>>> d = ZonedDateTime("2020-08-15 23:24:18+02:00[Europe/Paris]")
>>> d.round("day")
ZonedDateTime("2020-08-16 00:00:00+02:00[Europe/Paris]")
>>> d.round("minute", increment=15, mode="floor")
ZonedDateTime("2020-08-15 23:15:00+02:00[Europe/Paris]")

Notes

  • In the rare case that rounding results in a repeated time, the offset is preserved if possible. Otherwise, ambiguity is resolved according to the “compatible” strategy.

  • Rounding in “day” mode may be affected by DST transitions. i.e. on 23-hour days, 11:31 AM is rounded up.

  • This method has similar behavior to the round() method of Temporal objects in JavaScript.

since(b: ZonedDateTime, /, *, total: DeltaUnitStr) float[source]
since(
b: ZonedDateTime,
/,
*,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = ...,
round_increment: int = ...,
) ItemizedDelta

Calculate the duration since another ZonedDateTime, in terms of the specified units.

>>> d1 = ZonedDateTime("2020-08-15T23:12:00+01:00[Europe/London]")
>>> d2 = ZonedDateTime("2020-08-14T22:00:00+09:00[Asia/Tokyo]")
>>> d1.since(d2, in_units=["hours", "minutes"],
...          round_increment=15,
...          round_mode="ceil")
ItemizedDelta("PT33h15m")

When calculating calendar units (years, months, weeks, days), both datetimes must have the same timezone.

start_of_day() ZonedDateTime[source]

The start of the current calendar day.

This is almost always at midnight the same day, but may be different for timezones which transition at—and thus skip over—midnight.

subtract(
d: DateTimeDelta | TimeDelta | DateDelta | ItemizedDelta | ItemizedDateDelta,
/,
*,
disambiguate: DisambiguateStr = ...,
) ZonedDateTime[source]
subtract(
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
hours: float = ...,
minutes: float = ...,
seconds: float = ...,
milliseconds: float = ...,
microseconds: float = ...,
nanoseconds: int = ...,
disambiguate: DisambiguateStr = ...,
) ZonedDateTime

The inverse of the add() method. See add() for more information.

time() Time

The time-of-day part of the datetime

>>> d = ZonedDateTime("2021-01-02T03:04:05+01:00[Europe/Paris])"
>>> d.time()
Time(03:04:05)

To perform the inverse, use Time.on() and a method like assume_utc() or assume_tz():

>>> time.on(date).assume_tz("Europe/Paris")
ZonedDateTime("2021-01-02T03:04:05+01:00[Europe/Paris]")
timestamp() int

The UNIX timestamp for this datetime. Inverse of from_timestamp().

>>> Instant.from_utc(1970, 1, 1).timestamp()
0
>>> ts = 1_123_000_000
>>> Instant.from_timestamp(ts).timestamp() == ts
True

Note

In contrast to the standard library, this method always returns an integer, not a float. This is because floating point timestamps are not precise enough to represent all instants to nanosecond precision. This decision is consistent with other modern date-time libraries.

timestamp_millis() int

Like timestamp(), but with millisecond precision.

timestamp_nanos() int

Like timestamp(), but with nanosecond precision.

to_fixed_offset(offset: int | TimeDelta = ..., /) OffsetDateTime

Convert to an OffsetDateTime that represents the same moment in time.

If not offset is given, the offset is taken from the original datetime.

to_instant() Instant

Get the underlying instant in time

>>> d = ZonedDateTime(2020, 8, 15, hour=23, tz="Europe/Amsterdam")
>>> d.to_instant()
Instant("2020-08-15 21:00:00Z")
to_plain() PlainDateTime

Get the underlying date and time without offset or timezone

As an inverse, PlainDateTime has methods assume_utc(), assume_fixed_offset() , assume_tz(), and assume_system_tz().

to_stdlib() datetime[source]

Convert to a standard library datetime

Note

  • Nanoseconds are truncated to microseconds. If you wish to customize the rounding behavior, use the round() method first.

  • For ZonedDateTime linked to a system timezone without a IANA timezone ID, the returned Python datetime will have a fixed offset (timezone tzinfo)

to_system_tz() ZonedDateTime

Convert to a ZonedDateTime of the system’s timezone.

to_tz(tz: str, /) ZonedDateTime[source]

Convert to a ZonedDateTime that represents the same moment in time.

Raises:

TimeZoneNotFoundError – If the timezone ID is not found in the timezone database.

tz_abbrev() str[source]

The timezone abbreviation (e.g. "EST", "CEST").

>>> ZonedDateTime(2020, 8, 15, tz="Europe/London").tz_abbrev()
'BST'
>>> ZonedDateTime(2020, 1, 15, tz="Europe/London").tz_abbrev()
'GMT'

Warning

The abbreviation is often ambiguous and may not be unique, but it is commonly used in human-readable formats. Use the timezone ID (e.g. "Europe/London") for unambiguous identification of timezones.

until(b: ZonedDateTime, /, *, total: DeltaUnitStr) float[source]
until(
b: ZonedDateTime,
/,
*,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = ...,
round_increment: int = ...,
) ItemizedDelta

Inverse of the since() method. See since() for more information.

property day: int

The day component of the datetime

property hour: int

The hour component of the datetime

property minute: int

The minute component of the datetime

property month: int

The month component of the datetime

property nanosecond: int

The nanosecond component of the datetime

property offset: TimeDelta

The UTC offset of the datetime

property second: int

The second component of the datetime

property tz: str | None

The timezone ID. In rare cases, this may be None, if the ZonedDateTime was created from a system timezone without a known IANA key.

property year: int

The year component of the datetime