⏳ Deltas (durations)

As we’ve seen earlier, you can add and subtract time units from datetimes:

>>> dt.add(hours=5, minutes=30)

However, sometimes you want to operate on these durations directly. For example, you might want to reuse it in multiple places, add 5 hours to it, or double it, for example. For this, whenever provides an API designed to help you avoid common pitfalls.

Durations are created using the duration units provided. Here is a quick demo:

>>> from whenever import years, months, days, hours, minutes
>>> # Precise units create a TimeDelta
>>> movie_runtime = hours(2) + minutes(9)
>>> movie_runtime.in_minutes()
>>> movie_runtime / 1.2  # what if we watch it at 1.2x speed?
>>> # Calendar units create a DateDelta
>>> project_estimate = months(1) + days(10)
>>> Date(2023, 1, 29) + project_estimate
>>> project_estimate * 2  # a pessimistic estimate
>>> # Mixing date and time units creates a generic DateTimeDelta
>>> project_estimate + movie_runtime
>>> # Mistakes prevented by the API:
>>> project_estimate * 1.3             # Impossible arithmetic on calendar units
>>> project_estimate.in_hours()        # Resolving calendar units without context
>>> Date(2023, 1, 29) + movie_runtime  # Adding time to a date

Types of deltas

There are three duration types in whenever:

  • TimeDelta, created by precise units hours(), minutes(), seconds(), and microseconds(). Their duration is always the same and independent of the calendar. Arithmetic on time units is straightforward. It behaves similarly to the timedelta of the standard library.

  • DateDelta, created by the calendar units years(), months(), weeks(), and days(). They don’t have a precise duration, as this depends on the context. For example, the number of days in a month varies, and a day may be longer or shorter than 24 hours due to Daylight Saving Time. This makes arithmetic on calendar units less intuitive.

  • DateTimeDelta, created when you mix time and calendar units.

This distinction determines which operations are supported:





Add to DateTime

Add to Date

multiplication (×)

⚠️ by int

⚠️ by int

division (÷)

Commutative: dt + a + b == dt + b + a

Reversible: (dt + a) - a == dt

comparison (>, >=, <, <=)


⚠️ time part

equality based on

total microseconds

individual fields

date/time parts


You can multiply time units by a number:

>>> 1.5 * hours(2)

Date units can only be multiplied by integers. “1.3 months” isn’t a well-defined concept, so it’s not supported:

>>> months(3) * 2


Only time units can be divided:

>>> hours(3) / 1.5

Date units can’t be divided. “A year divided by 11.2”, for example, can’t be defined.


The result of adding two time durations is the same, regardless of what order you add them in:

>>> dt = UTCDateTime(2020, 1, 29)
>>> dt + hours(2) + minutes(30)
UTCDateTime(2020-01-29 02:30:00Z)
>>> dt + minutes(30) + hours(2)  # same result

This is not the case for date units. The result of adding two date units depends on the order:

>>> dt + months(1) + days(3)
UTCDateTime(2021-03-03 00:00:00)
>>> dt + days(3) + months(1)
UTCDateTime(2021-03-01 00:00:00)


Adding a time duration and then subtracting it again gives you the original datetime:

>>> dt + hours(3) - hours(3) == dt

This is not the case for date units:

>>> jan30 = UTCDateTime(2020, 1, 30)
>>> jan30 + months(1)
UTCDateTime(2020-02-29 00:00:00)
>>> jan30 + months(1) - months(1)
UTCDateTime(2020-01-29 00:00:00)


You can compare time durations:

>>> hours(3) > minutes(30)

This is not the case for date units:

>>> months(1) > days(30)  # no universal answer


Time durations are always normalized:

>>> minutes(70)

Date units are not normalized:

>>> months(13)


Two time durations are equal if their sum of components is equal:

>>> hours(1) + minutes(30) == hours(2) - minutes(30)

Since date units aren’t normalized, two date duration are only equal if their individual components are equal:

>>> months(1) + days(30) == months(2) - days(1)

ISO 8601 format

The ISO 8601 standard defines formats for specifying durations, the most common being:



  • P is the period designator, and T separates date and time components.

  • nY is the number of years, nM is the number of months, etc.

  • Each n may be negative, and only seconds may have a fractional part.

For example:

  • P3Y4DT12H30M is 3 years, 4 days, 12 hours, and 30 minutes.

  • -P2M5D is -2 months, +5 days.

  • P0D is zero.

  • PT-5M4.25S is -5 minutes and +4.25 seconds.

All deltas can be converted to and from this format using the methods common_iso8601() and from_common_iso8601().

>>> hours(3).common_iso8601()
>>> (years(1) - months(3) + minutes(30.25)).common_iso8601()
>>> DateDelta.from_common_iso8601('-P2M')
>>> DateTimeDelta.from_common_iso8601('P3YT90M')


Full conformance to the ISO 8601 standard is not provided, because:

  • It allows for a lot of unnecessary flexibility (e.g. fractional components other than seconds)

  • There are different revisions with different rules

  • The full specification is not freely available

Supporting a commonly used subset is more practical. This is also what established libraries such as java.time and Nodatime do.