.. _timedeltas:

{{ header }}

.. _timedeltas.timedeltas:

***********
Time deltas
***********

Timedeltas are differences in times, expressed in difference units, e.g. days, hours, minutes,
seconds. They can be both positive and negative.

``Timedelta`` is a subclass of ``datetime.timedelta``, and behaves in a similar manner,
but allows compatibility with ``np.timedelta64`` types as well as a host of custom representation,
parsing, and attributes.

Parsing
-------

You can construct a ``Timedelta`` scalar through various arguments, including `ISO 8601 Duration`_ strings.

.. ipython:: python

   import datetime

   # strings
   pd.Timedelta("1 days")
   pd.Timedelta("1 days 00:00:00")
   pd.Timedelta("1 days 2 hours")
   pd.Timedelta("-1 days 2 min 3us")

   # like datetime.timedelta
   # note: these MUST be specified as keyword arguments
   pd.Timedelta(days=1, seconds=1)

   # integers with a unit
   pd.Timedelta(1, unit="d")

   # from a datetime.timedelta/np.timedelta64
   pd.Timedelta(datetime.timedelta(days=1, seconds=1))
   pd.Timedelta(np.timedelta64(1, "ms"))

   # negative Timedeltas have this string repr
   # to be more consistent with datetime.timedelta conventions
   pd.Timedelta("-1us")

   # a NaT
   pd.Timedelta("nan")
   pd.Timedelta("nat")

   # ISO 8601 Duration strings
   pd.Timedelta("P0DT0H1M0S")
   pd.Timedelta("P0DT0H0M0.000000123S")

:ref:`DateOffsets<timeseries.offsets>` (``Day, Hour, Minute, Second, Milli, Micro, Nano``) can also be used in construction.

.. ipython:: python

   pd.Timedelta(pd.offsets.Second(2))

Further, operations among the scalars yield another scalar ``Timedelta``.

.. ipython:: python

   pd.Timedelta(pd.offsets.Day(2)) + pd.Timedelta(pd.offsets.Second(2)) + pd.Timedelta(
       "00:00:00.000123"
   )

to_timedelta
~~~~~~~~~~~~

Using the top-level ``pd.to_timedelta``, you can convert a scalar, array, list,
or Series from a recognized timedelta format / value into a ``Timedelta`` type.
It will construct Series if the input is a Series, a scalar if the input is
scalar-like, otherwise it will output a ``TimedeltaIndex``.

You can parse a single string to a Timedelta:

.. ipython:: python

   pd.to_timedelta("1 days 06:05:01.00003")
   pd.to_timedelta("15.5us")

or a list/array of strings:

.. ipython:: python

   pd.to_timedelta(["1 days 06:05:01.00003", "15.5us", "nan"])

The ``unit`` keyword argument specifies the unit of the Timedelta if the input
is numeric:

.. ipython:: python

   pd.to_timedelta(np.arange(5), unit="s")
   pd.to_timedelta(np.arange(5), unit="d")

.. warning::
    If a string or array of strings is passed as an input then the ``unit`` keyword
    argument will be ignored. If a string without units is passed then the default
    unit of nanoseconds is assumed.

.. _timedeltas.limitations:

Timedelta limitations
~~~~~~~~~~~~~~~~~~~~~

pandas represents ``Timedeltas`` in nanosecond resolution using
64 bit integers. As such, the 64 bit integer limits determine
the ``Timedelta`` limits.

.. ipython:: python

   pd.Timedelta.min
   pd.Timedelta.max

.. _timedeltas.operations:

Operations
----------

You can operate on Series/DataFrames and construct ``timedelta64[ns]`` Series through
subtraction operations on ``datetime64[ns]`` Series, or ``Timestamps``.

.. ipython:: python

   s = pd.Series(pd.date_range("2012-1-1", periods=3, freq="D"))
   td = pd.Series([pd.Timedelta(days=i) for i in range(3)])
   df = pd.DataFrame({"A": s, "B": td})
   df
   df["C"] = df["A"] + df["B"]
   df
   df.dtypes

   s - s.max()
   s - datetime.datetime(2011, 1, 1, 3, 5)
   s + datetime.timedelta(minutes=5)
   s + pd.offsets.Minute(5)
   s + pd.offsets.Minute(5) + pd.offsets.Milli(5)

Operations with scalars from a ``timedelta64[ns]`` series:

.. ipython:: python

   y = s - s[0]
   y

Series of timedeltas with ``NaT`` values are supported:

.. ipython:: python

   y = s - s.shift()
   y

Elements can be set to ``NaT`` using ``np.nan`` analogously to datetimes:

.. ipython:: python

   y[1] = np.nan
   y

Operands can also appear in a reversed order (a singular object operated with a Series):

.. ipython:: python

   s.max() - s
   datetime.datetime(2011, 1, 1, 3, 5) - s
   datetime.timedelta(minutes=5) + s

``min, max`` and the corresponding ``idxmin, idxmax`` operations are supported on frames:

.. ipython:: python

   A = s - pd.Timestamp("20120101") - pd.Timedelta("00:05:05")
   B = s - pd.Series(pd.date_range("2012-1-2", periods=3, freq="D"))

   df = pd.DataFrame({"A": A, "B": B})
   df

   df.min()
   df.min(axis=1)

   df.idxmin()
   df.idxmax()

``min, max, idxmin, idxmax`` operations are supported on Series as well. A scalar result will be a ``Timedelta``.

.. ipython:: python

   df.min().max()
   df.min(axis=1).min()

   df.min().idxmax()
   df.min(axis=1).idxmin()

You can fillna on timedeltas, passing a timedelta to get a particular value.

.. ipython:: python

   y.fillna(pd.Timedelta(0))
   y.fillna(pd.Timedelta(10, unit="s"))
   y.fillna(pd.Timedelta("-1 days, 00:00:05"))

You can also negate, multiply and use ``abs`` on ``Timedeltas``:

.. ipython:: python

   td1 = pd.Timedelta("-1 days 2 hours 3 seconds")
   td1
   -1 * td1
   -td1
   abs(td1)

.. _timedeltas.timedeltas_reductions:

Reductions
----------

Numeric reduction operation for ``timedelta64[ns]`` will return ``Timedelta`` objects. As usual
``NaT`` are skipped during evaluation.

.. ipython:: python

   y2 = pd.Series(
       pd.to_timedelta(["-1 days +00:00:05", "nat", "-1 days +00:00:05", "1 days"])
   )
   y2
   y2.mean()
   y2.median()
   y2.quantile(0.1)
   y2.sum()

.. _timedeltas.timedeltas_convert:

Frequency conversion
--------------------

Timedelta Series and ``TimedeltaIndex``, and ``Timedelta`` can be converted to other frequencies by astyping to a specific timedelta dtype.

.. ipython:: python

   december = pd.Series(pd.date_range("20121201", periods=4))
   january = pd.Series(pd.date_range("20130101", periods=4))
   td = january - december

   td[2] += datetime.timedelta(minutes=5, seconds=3)
   td[3] = np.nan
   td

   # to seconds
   td.astype("timedelta64[s]")

For timedelta64 resolutions other than the supported "s", "ms", "us", "ns",
an alternative is to divide by another timedelta object. Note that division by the NumPy scalar is true division, while astyping is equivalent of floor division.

.. ipython:: python

   # to days
   td / np.timedelta64(1, "D")

Dividing or multiplying a ``timedelta64[ns]`` Series by an integer or integer Series
yields another ``timedelta64[ns]`` dtypes Series.

.. ipython:: python

   td * -1
   td * pd.Series([1, 2, 3, 4])

Rounded division (floor-division) of a ``timedelta64[ns]`` Series by a scalar
``Timedelta`` gives a series of integers.

.. ipython:: python

   td // pd.Timedelta(days=3, hours=4)
   pd.Timedelta(days=3, hours=4) // td

.. _timedeltas.mod_divmod:

The mod (%) and divmod operations are defined for ``Timedelta`` when operating with another timedelta-like or with a numeric argument.

.. ipython:: python

   pd.Timedelta(hours=37) % datetime.timedelta(hours=2)

   # divmod against a timedelta-like returns a pair (int, Timedelta)
   divmod(datetime.timedelta(hours=2), pd.Timedelta(minutes=11))

   # divmod against a numeric returns a pair (Timedelta, Timedelta)
   divmod(pd.Timedelta(hours=25), 86400000000000)

Attributes
----------

You can access various components of the ``Timedelta`` or ``TimedeltaIndex`` directly using the attributes ``days,seconds,microseconds,nanoseconds``. These are identical to the values returned by ``datetime.timedelta``, in that, for example, the ``.seconds`` attribute represents the number of seconds >= 0 and < 1 day. These are signed according to whether the ``Timedelta`` is signed.

These operations can also be directly accessed via the ``.dt`` property of the ``Series`` as well.

.. note::

   Note that the attributes are NOT the displayed values of the ``Timedelta``. Use ``.components`` to retrieve the displayed values.

For a ``Series``:

.. ipython:: python

   td.dt.days
   td.dt.seconds

You can access the value of the fields for a scalar ``Timedelta`` directly.

.. ipython:: python

   tds = pd.Timedelta("31 days 5 min 3 sec")
   tds.days
   tds.seconds
   (-tds).seconds

You can use the ``.components`` property to access a reduced form of the timedelta. This returns a ``DataFrame`` indexed
similarly to the ``Series``. These are the *displayed* values of the ``Timedelta``.

.. ipython:: python

   td.dt.components
   td.dt.components.seconds

.. _timedeltas.isoformat:

You can convert a ``Timedelta`` to an `ISO 8601 Duration`_ string with the
``.isoformat`` method

.. ipython:: python

    pd.Timedelta(
        days=6, minutes=50, seconds=3, milliseconds=10, microseconds=10, nanoseconds=12
    ).isoformat()

.. _ISO 8601 Duration: https://en.wikipedia.org/wiki/ISO_8601#Durations

.. _timedeltas.index:

TimedeltaIndex
--------------

To generate an index with time delta, you can use either the :class:`TimedeltaIndex` or
the :func:`timedelta_range` constructor.

Using ``TimedeltaIndex`` you can pass string-like, ``Timedelta``, ``timedelta``,
or ``np.timedelta64`` objects. Passing ``np.nan/pd.NaT/nat`` will represent missing values.

.. ipython:: python

   pd.TimedeltaIndex(
       [
           "1 days",
           "1 days, 00:00:05",
           np.timedelta64(2, "D"),
           datetime.timedelta(days=2, seconds=2),
       ]
   )

The string 'infer' can be passed in order to set the frequency of the index as the
inferred frequency upon creation:

.. ipython:: python

   pd.TimedeltaIndex(["0 days", "10 days", "20 days"], freq="infer")

Generating ranges of time deltas
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Similar to :func:`date_range`, you can construct regular ranges of a ``TimedeltaIndex``
using :func:`timedelta_range`.  The default frequency for ``timedelta_range`` is
calendar day:

.. ipython:: python

   pd.timedelta_range(start="1 days", periods=5)

Various combinations of ``start``, ``end``, and ``periods`` can be used with
``timedelta_range``:

.. ipython:: python

   pd.timedelta_range(start="1 days", end="5 days")

   pd.timedelta_range(end="10 days", periods=4)

The ``freq`` parameter can passed a variety of :ref:`frequency aliases <timeseries.offset_aliases>`:

.. ipython:: python

   pd.timedelta_range(start="1 days", end="2 days", freq="30T")

   pd.timedelta_range(start="1 days", periods=5, freq="2D5H")


Specifying ``start``, ``end``, and ``periods`` will generate a range of evenly spaced
timedeltas from ``start`` to ``end`` inclusively, with ``periods`` number of elements
in the resulting ``TimedeltaIndex``:

.. ipython:: python

   pd.timedelta_range("0 days", "4 days", periods=5)

   pd.timedelta_range("0 days", "4 days", periods=10)

Using the TimedeltaIndex
~~~~~~~~~~~~~~~~~~~~~~~~

Similarly to other of the datetime-like indices, ``DatetimeIndex`` and ``PeriodIndex``, you can use
``TimedeltaIndex`` as the index of pandas objects.

.. ipython:: python

   s = pd.Series(
       np.arange(100),
       index=pd.timedelta_range("1 days", periods=100, freq="h"),
   )
   s

Selections work similarly, with coercion on string-likes and slices:

.. ipython:: python

   s["1 day":"2 day"]
   s["1 day 01:00:00"]
   s[pd.Timedelta("1 day 1h")]

Furthermore you can use partial string selection and the range will be inferred:

.. ipython:: python

   s["1 day":"1 day 5 hours"]

Operations
~~~~~~~~~~

Finally, the combination of ``TimedeltaIndex`` with ``DatetimeIndex`` allow certain combination operations that are NaT preserving:

.. ipython:: python

   tdi = pd.TimedeltaIndex(["1 days", pd.NaT, "2 days"])
   tdi.to_list()
   dti = pd.date_range("20130101", periods=3)
   dti.to_list()
   (dti + tdi).to_list()
   (dti - tdi).to_list()

Conversions
~~~~~~~~~~~

Similarly to frequency conversion on a ``Series`` above, you can convert these indices to yield another Index.

.. ipython:: python

   tdi / np.timedelta64(1, "s")
   tdi.astype("timedelta64[s]")

Scalars type ops work as well. These can potentially return a *different* type of index.

.. ipython:: python

   # adding or timedelta and date -> datelike
   tdi + pd.Timestamp("20130101")

   # subtraction of a date and a timedelta -> datelike
   # note that trying to subtract a date from a Timedelta will raise an exception
   (pd.Timestamp("20130101") - tdi).to_list()

   # timedelta + timedelta -> timedelta
   tdi + pd.Timedelta("10 days")

   # division can result in a Timedelta if the divisor is an integer
   tdi / 2

   # or a float64 Index if the divisor is a Timedelta
   tdi / tdi[0]

.. _timedeltas.resampling:

Resampling
----------

Similar to :ref:`timeseries resampling <timeseries.resampling>`, we can resample with a ``TimedeltaIndex``.

.. ipython:: python

   s.resample("D").mean()
