ItemizedDateDelta

class whenever.ItemizedDateDelta(*, years: int = ..., months: int = ..., weeks: int = ..., days: int = ...)

A date delta that preserves the components it was given. It closely models the ISO 8601 duration format for date-only durations.

>>> d = ItemizedDateDelta(years=2, weeks=3)
ItemizedDateDelta("P2y3w")
>>> str(ItemizedDateDelta("P22W"))
'P22W'

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']
3
>>> print(d.get('days'))
None
>>> dict(d)
{"years": 2, "weeks": 3}
>>> list(d.keys())
["years", "weeks"]
>>> years, weeks = d.values()
(2, 3)

ItemizedDateDelta also supports other dictionary-like operations:

>>> "days" in d  # check for presence of a component
False
>>> len(d)  # number of components present
2

An explicit zero is a present component, distinct from an absent one:

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

Additionally, no normalization is performed. Months are not rolled into years, weeks into days, etc.

>>> d3 = ItemizedDateDelta(months=24, days=100)
ItemizedDateDelta("P24m100d")

An empty delta is not allowed: at least one component must be present, but it may be zero:

>>> ItemizedDateDelta()
ValueError: at least one component must be present
>>> ItemizedDateDelta(days=0)
ItemizedDateDelta("P0d")

Negative deltas are supported, but all components must have the same sign:

>>> d4 = ItemizedDateDelta(years=-1, weeks=-2, days=0)
ItemizedDateDelta("-P1y2w0d")
>>> ItemizedDateDelta(years=1, days=-3)
ValueError: mixed sign in delta

Note

ItemizedDateDelta does not normalize its components. This means that ItemizedDateDelta(months=14) and ItemizedDateDelta(years=1, months=2) are considered different values. To convert to a normalized form, use in_units(). See also the delta documentation.

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

Parse the popular interpretation of the ISO 8601 duration format. Inverse of format_iso()

>>> ItemizedDateDelta.parse_iso("-P1W11D")
ItemizedDateDelta("-P1w11d")

You can also use the constructor ItemizedDateDelta(s) which is equivalent to ItemizedDateDelta.parse_iso(s).

Note

Does not parse all possible ISO 8601 durations. In particular, it doesn’t allow fractional values. See here for more information.

__abs__() → ItemizedDateDelta[source]

If the components are negative, return the positive version

>>> d = ItemizedDateDelta(weeks=-2, days=-3)
>>> abs(d)
ItemizedDateDelta("P2w3d")
__add__(
other: ItemizedDateDelta | ItemizedDelta | Date | ZonedDateTime | PlainDateTime | OffsetDateTime,
/,
) → ItemizedDateDelta | ItemizedDelta | Date | ZonedDateTime | PlainDateTime | OffsetDateTime[source]

Compose component-wise with another itemized delta, or shift a date or datetime by this delta.

>>> ItemizedDateDelta(months=1) + ItemizedDateDelta(days=3)
ItemizedDateDelta("P1m3d")
>>> ItemizedDateDelta(months=1) + Date(2023, 1, 31)
Date("2023-02-28")

The operands decide the result type: an ItemizedDelta gives an ItemizedDelta. Nonzero years or months in either operand emit MonthCompositionWarning, which add() accepts with month_composition_ok=True.

__bool__() → bool[source]

An ItemizedDateDelta is considered False if its sign is 0.

>>> d = ItemizedDateDelta(weeks=0)
>>> bool(d)
False
>>> d = ItemizedDateDelta(weeks=1)
>>> bool(d)
True
__contains__(key: object, /) → bool[source]

Whether a specific component is present.

>>> d = ItemizedDateDelta(weeks=1, days=0)
>>> "weeks" in d
True
>>> "days" in d
True
>>> "months" in d
False
__eq__(other: object, /) → bool[source]

Compare each component for equality, under the following rules:

  • No normalization is performed. 12 months is not equal to 1 year, etc.

  • An explicit zero is equivalent to an absent component.

  • An ItemizedDelta with the same components is equal.

If you want strict equality (including presence of components and the type), use strict_eq().

>>> d = ItemizedDateDelta(weeks=2, days=3)
>>> d == ItemizedDateDelta(weeks=2, days=3, months=0)
True
>>> d == ItemizedDateDelta(weeks=2, days=4)
False
>>> d == ItemizedDelta(weeks=2, days=3, hours=0)
True
__getitem__(key: DateDeltaUnitStr, /) → int[source]

Get the value of a specific component by name.

>>> d = ItemizedDateDelta(weeks=1, days=0)
>>> d["weeks"]
1
>>> d["days"]
0
>>> d["years"]
KeyError: 'years'
__iter__() → Iterator[DateDeltaUnitStr][source]

Iterate over the present components, ordered from largest to smallest unit.

__len__() → int[source]

The number of present components.

>>> d = ItemizedDateDelta(weeks=1, days=0)
>>> len(d)
2
__neg__() → ItemizedDateDelta[source]

Invert the sign of the components

>>> d = ItemizedDateDelta(weeks=2, days=3)
>>> -d
ItemizedDateDelta("-P2w3d")
>>> --d
ItemizedDateDelta("P2w3d")
__str__() → str[source]

Return str(self).

__sub__(other: ItemizedDateDelta | ItemizedDelta, /) → ItemizedDateDelta | ItemizedDelta[source]

Compose component-wise by subtracting another itemized delta.

>>> ItemizedDateDelta(months=1, days=5) - ItemizedDateDelta(days=3)
ItemizedDateDelta("P1m2d")

The same rules apply as for __add__().

add(
delta: ItemizedDateDelta | ItemizedDelta = ...,
/,
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
month_composition_ok: bool = ...,
**kwargs: Any,
) → ItemizedDateDelta | ItemizedDelta[source]

Add a delta to this one component-wise, returning a new delta.

Pass either a delta or its components as keywords, not both. The operands decide the result type: an ItemizedDateDelta or date components give an ItemizedDateDelta, an ItemizedDelta gives an ItemizedDelta.

The result isn’t balanced: 1 hour plus 90 minutes is 1 hour 90 minutes, and 1 month plus 30 days is 1 month 30 days. To balance it, call in_units() on the result.

Deprecated since version 0.11: relative_to=, with in_units=, round_mode=, round_increment=, naive_arithmetic_ok=, and stale_offset_ok=. Call in_units() on the result instead. cal_unit_composition_ok= is now month_composition_ok=.

>>> ItemizedDateDelta(weeks=1).add(days=3)
ItemizedDateDelta("P1w3d")
>>> d = ItemizedDateDelta(months=1)
>>> d.add(days=3, month_composition_ok=True)
ItemizedDateDelta("P1m3d")
>>> d.add(ItemizedDelta(hours=3), month_composition_ok=True)
ItemizedDelta("P1mT3h")
Parameters:
  • delta – The delta to add, or else its components as keywords, named as in the constructor.

  • month_composition_ok – Accepts the MonthCompositionWarning that nonzero years or months in either operand emits.

exact_eq(other: ItemizedDateDelta, /) → bool[source]

Deprecated alias for strict_eq().

Deprecated since version 0.11: Use strict_eq() instead.

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

Convert to the canonical ISO 8601 string representation:

P(nY)(nM)(nW)(nD)

You can also use str(d) which is equivalent to d.format_iso().

Inverse of parse_iso().

>>> d = ItemizedDateDelta(weeks=1, days=11)
>>> d.format_iso()
'P1W11D'
Parameters:

lowercase_units – Write the unit designators in lowercase, as repr() does; parse_iso() reads both cases.

Note

Negative deltas are prefixed with a minus sign, which is not part of the ISO 8601 standard, but is a common extension. See here for more information.

get(key: DateDeltaUnitStr, /) → int | None[source]
get(key: DateDeltaUnitStr, default: int, /) → int

Get the value of a specific component by name, or return default if absent.

Part of the mapping protocol

in_units(
units: Sequence[DateDeltaUnitStr],
/,
*,
relative_to: Date | ZonedDateTime | PlainDateTime | OffsetDateTime,
round_mode: RoundModeStr = 'trunc',
round_increment: int = 1,
) → ItemizedDateDelta[source]

Convert this delta into the given units.

>>> d = ItemizedDateDelta(years=1, months=8)
>>> d.in_units(["weeks", "days"], relative_to=Date(2020, 6, 30))
ItemizedDateDelta("P86w6d")
Parameters:
  • units – The units of the result, largest first.

  • relative_to – The reference the calendar units are resolved against: a Date, or a datetime of which only the date is read, without a warning.

  • round_mode – The rounding mode for the smallest unit in units, as on since().

  • round_increment – The rounding increment for that unit.

items() → ItemsView[DateDeltaUnitStr, int][source]

The present components as (unit, value) pairs, ordered from largest to smallest unit.

>>> d = ItemizedDateDelta(years=3, days=12, months=0)
>>> list(d.items())
[('years', 3), ('months', 0), ('days', 12)]
keys() → KeysView[DateDeltaUnitStr][source]

The names of the present components, ordered from largest to smallest unit.

Part of the mapping protocol

replace(
*,
years: int | None = ...,
months: int | None = ...,
weeks: int | None = ...,
days: int | None = ...,
) → ItemizedDateDelta[source]

Create a new delta with the given components replaced

A component given as None is removed. Removing the last one raises ValueError, as does a mixed sign.

>>> d = ItemizedDateDelta(years=1, months=2, weeks=3)
>>> d.replace(months=None, weeks=4)
ItemizedDateDelta("P1y4w")
sign() → Literal[1, 0, -1][source]

The sign of the delta, whether it’s positive, negative, or zero.

>>> ItemizedDateDelta(weeks=2).sign()
1
>>> ItemizedDateDelta(days=-3).sign()
-1
>>> ItemizedDateDelta(weeks=0).sign()
0
strict_eq(other: ItemizedDateDelta, /) → bool[source]

Compare two deltas, including what == ignores.

== equates an ItemizedDateDelta with an ItemizedDelta of the same components, and ignores whether a component was given explicitly as zero. strict_eq() takes only an ItemizedDateDelta: another type raises TypeError.

>>> d = ItemizedDateDelta(weeks=2, days=3)
>>> d == ItemizedDateDelta(weeks=2, days=3, months=0)
True
>>> d.strict_eq(ItemizedDateDelta(weeks=2, days=3, months=0))
False

See Strict equality for the rules on every type.

subtract(
delta: ItemizedDateDelta | ItemizedDelta = ...,
/,
*,
years: int = ...,
months: int = ...,
weeks: int = ...,
days: int = ...,
month_composition_ok: bool = ...,
**kwargs: Any,
) → ItemizedDateDelta | ItemizedDelta[source]

Subtract a delta from this one component-wise, returning a new delta.

The inverse of add(), with the same parameters and rules.

>>> ItemizedDateDelta(months=1, days=5).subtract(days=3, month_composition_ok=True)
ItemizedDateDelta("P1m2d")
total(
unit: DateDeltaUnitStr,
/,
*,
relative_to: Date | ZonedDateTime | PlainDateTime | OffsetDateTime,
) → float[source]

The total duration in the given unit, as a float.

>>> ItemizedDateDelta(years=1, months=6).total("months", relative_to=Date(2020, 1, 31))
18.0
>>> ItemizedDateDelta(days=1000).total("years", relative_to=Date(2020, 4, 10))
2.73972602739726
Parameters:
  • unit – The unit to sum into.

  • relative_to – The reference the calendar units are resolved against: a Date, or a datetime of which only the date is read, without a warning.

values() → ValuesView[int][source]

The values of the present components, in order of largest to smallest unit.

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