ItemizedDelta

final class whenever.ItemizedDelta(iso_string: str, /)[source]
final class whenever.ItemizedDelta(
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
hours: int = ...,
minutes: int = ...,
seconds: int = ...,
nanoseconds: int = ...,
)

A duration that preserves the exact fields it was created with. It closely models the ISO 8601 duration format for durations.

>>> d = ItemizedDelta(weeks=2, days=3, hours=14)
ItemizedDelta("P2w3dT14h")
>>> d = ItemizedDelta("P2w3dT14h")
>>> str(d)
'P2w3dT14h'

It behaves like a mapping where the keys are the unit names and the values are the amounts. Items are ordered from largest to smallest unit.

>>> d['weeks']
2
>>> d.get('minutes')
None
>>> dict(d)
{"weeks": 2, "days": 3, "hours": 14}
>>> list(d.keys())
["weeks", "days", "hours"]
>>> weeks, days, hours = d.values()
(2, 3, 14)

ItemizedDelta also supports other dictionary-like operations:

>>> "months" in d  # check for presence of a field
False
>>> len(d)  # number of fields set
3

Zero values are considered distinct from “missing” values:

>>> d2 = ItemizedDelta(years=2, weeks=3, hours=0)
>>> dict(d2)
{"years": 2, "weeks": 3, "hours": 0}

Additionally, no normalization is performed. Months are not rolled into years, minutes into hours, etc.

>>> d3 = ItemizedDelta(months=24, minutes=90)
ItemizedDelta("P24mT90m")

Empty durations are not allowed. At least one field must be set (but it can be zero):

>>> ItemizedDelta()
ValueError: At least one field must be set
>>> ItemizedDelta(seconds=0)
ItemizedDelta("PT0s")

Negative durations are supported, but all fields must have the same sign:

>>> d4 = ItemizedDelta(years=-1, weeks=-2, days=0)
ItemizedDelta("-P1y2w0d")
>>> ItemizedDelta(years=1, days=-3)
ValueError: All fields must have the same sign

Note

Unlike TimeDelta, ItemizedDelta does not normalize its fields. This means that ItemizedDelta(hours=90) and ItemizedDelta(days=3, hours=18) are considered different values. To convert to a normalized form, use in_units(). See also the delta documentation.

classmethod parse_iso(s: str, /) ItemizedDelta[source]

Parse the popular interpretation of the ISO 8601 duration format. Does not parse all possible ISO 8601 durations. See here for more information.

P4D        # 4 days
PT4H       # 4 hours
PT0M       # 0 minutes
PT3M40.5S  # 3 minutes and 40.5 seconds
P1W11DT90M # 1 week, 11 days, and 90 minutes
-PT7H400M  # -7 hours and -400 minutes
+PT7H4M    # 7 hours and 4 minutes (7:04:00)

Inverse of format_iso()

>>> ItemizeDelta.parse_iso("-P1W11DT4H")
ItemizeDelta("-P1w11dT4h")
__abs__() ItemizedDelta[source]

If the contents are negative, return the positive version

>>> d = ItemizedDelta(weeks=-2, days=-3)
>>> abs(d)
ItemizedDelta("P2w3d")
__bool__() bool[source]

An ItemizedDelta is considered False if its sign is 0.

>>> bool(ItemizedDelta(weeks=0))
False
>>> bool(ItemizedDelta(weeks=1))
True
__contains__(key: object) bool[source]

Check if a specific field is set.

>>> d = ItemizedDelta(weeks=1, days=3)
>>> "weeks" in d
True
>>> "hours" in d
False
__eq__(other: object) bool[source]

Compare for equality. Each field is individually compared. No normalization is performed. Zero values are considered equivalent to missing values.

Thus, ItemizedDelta(weeks=1, seconds=0) == ItemizedDelta(weeks=1)

>>> d = ItemizedDelta(weeks=2, minutes=90)
>>> d == ItemizedDelta(weeks=2, minutes=90)
True
>>> d == ItemizedDelta(weeks=2, minutes=91)
False

If you want strict equality (including presence of fields), use exact_eq().

__getitem__(key: str) int[source]

Get the value of a specific field by name.

>>> d = ItemizedDelta(weeks=1, days=3)
>>> d["weeks"]
1
>>> d["days"]
3
>>> d["hours"]
KeyError: 'hours'
__iter__() Iterator[TypeAliasForwardRef('DeltaUnitStr')][source]

Iterate over all non-missing fields, ordered from largest to smallest unit.

__len__() int[source]

Get the number of fields that are set.

>>> d = ItemizedDelta(weeks=1, days=3)
>>> len(d)
2
__neg__() ItemizedDelta[source]

Invert the sign of the contents

>>> d = ItemizedDelta(weeks=2, days=3)
>>> -d
ItemizedDelta("-P2w3d")
>>> --d
ItemizedDelta("P2w3d")
__str__(*, lowercase_units: bool = False) str

Format as the popular interpretation of the ISO 8601 duration format. May not strictly adhere to (all versions of) the standard. See here for more information.

Inverse of parse_iso().

The format is:

P(nY)(nM)(nW)(nD)T(nH)(nM)(nS)
>>> d = ItemizedDelta(
...     weeks=1,
...     days=11,
...     hours=4,
...     seconds=1,
...     nanoseconds=12_000,
... )
>>> d.format_iso()
'P1W11DT4H1.000012S'
add(
other: ItemizedDelta,
/,
*,
relative_to: ZonedDateTime,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) ItemizedDelta[source]
add(
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
hours: int = ...,
minutes: int = ...,
seconds: int = ...,
nanoseconds: int = ...,
relative_to: ZonedDateTime,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) ItemizedDelta

Add time to this delta, returning a new delta

date_and_time_parts() tuple[ItemizedDateDelta | None, TimeDelta | None][source]

Split into date and time parts.

Either part may be None if no fields were set of that type. At least one part will be non-None, since at least one field must be set.

>>> d = ItemizedDelta(
...     years=1,
...     months=2,
...     weeks=3,
...     days=4,
...     hours=5,
...     minutes=6,
...     seconds=7,
...     nanoseconds=8,
... )
>>> date_part, time_part = d.date_and_time_parts()
>>> date_part
ItemizedDateDelta("P1y2m3w4d")
>>> time_part
TimeDelta("P5h6m7.000000008s")
>>> ItemizedDelta(weeks=2).date_and_time_parts()
(ItemizedDateDelta("P2w"), None)
exact_eq(other: ItemizedDelta, /) bool[source]

Check for strict equality. All fields and their presence must match.

format_iso(*, lowercase_units: bool = False) str[source]

Format as the popular interpretation of the ISO 8601 duration format. May not strictly adhere to (all versions of) the standard. See here for more information.

Inverse of parse_iso().

The format is:

P(nY)(nM)(nW)(nD)T(nH)(nM)(nS)
>>> d = ItemizedDelta(
...     weeks=1,
...     days=11,
...     hours=4,
...     seconds=1,
...     nanoseconds=12_000,
... )
>>> d.format_iso()
'P1W11DT4H1.000012S'
get(key: DeltaUnitStr, /) int | None[source]
get(key: DeltaUnitStr, default: int, /) int

Get the value of a specific field by name, or return default if not set.

Part of the mapping protocol

in_units(
units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
/,
*,
relative_to: ZonedDateTime | PlainDateTime | OffsetDateTime,
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) ItemizedDelta[source]

Convert this delta into the specified units. A relative_to datetime is required to resolve calendar units.

>>> d = ItemizedDelta(years=1, months=8, minutes=1000)
>>> d.in_units(["weeks", "hours"], relative_to=ZonedDateTime(2020, 6, 30, 12, tz="Asia/Tokyo"))
ItemizedDelta("P86w160h")
Parameters:

relative_to

A ZonedDateTime, PlainDateTime, or OffsetDateTime reference point.

items() ItemsView[TypeAliasForwardRef('DeltaUnitStr'), int][source]

Return all defined fields as (unit, value) pairs ordered from largest to smallest unit.

>>> d = ItemizedDelta(years=3, hours=12, days=0)
>>> list(d.items())
[('years', 3), ('days', 0), ('hours', 12)]

Part of the mapping protocol

keys() KeysView[TypeAliasForwardRef('DeltaUnitStr')][source]

The names of all defined fields, in order of largest to smallest unit.

Part of the mapping protocol

replace(
*,
years: int | None = ...,
months: int | None = ...,
weeks: int | None = ...,
days: int | None = ...,
hours: int | None = ...,
minutes: int | None = ...,
seconds: int | None = ...,
nanoseconds: int | None = ...,
) ItemizedDelta[source]

Return a new delta with specific fields replaced. Fields set to None will be removed.

All normal validation rules apply.

>>> d = ItemizedDelta(years=1, months=2, hours=3)
>>> d.replace(months=None, hours=2)
ItemizedDelta("P1yT2h")
sign() Literal[1, 0, -1][source]

The sign of the delta, 1, 0, or -1

subtract(
other: ItemizedDelta,
/,
*,
relative_to: ZonedDateTime,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) ItemizedDelta[source]
subtract(
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
hours: int = ...,
minutes: int = ...,
seconds: int = ...,
nanoseconds: int = ...,
relative_to: ZonedDateTime,
in_units: Sequence[TypeAliasForwardRef('DeltaUnitStr')],
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) ItemizedDelta

Inverse of add().

total(
unit: DeltaUnitStr,
/,
*,
relative_to: ZonedDateTime | PlainDateTime | OffsetDateTime,
) float[source]

Return the total duration expressed in the specified unit as a float

Parameters:

relative_to

A ZonedDateTime, PlainDateTime, or OffsetDateTime reference point.

values() ValuesView[int][source]

Return all defined field values, in order of largest to smallest unit.

>>> d = ItemizedDelta(years=3, hours=12, days=0)
>>> years, days, hours = d.values()
(3, 0, 12)
>>> list(d.values())
[3, 0, 12]

Part of the mapping protocol