Time Deltas

Note

Starting in v0.15.0, we introduce a new scalar type Timedelta, which 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.

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

Parsing

You can construct a Timedelta scalar thru various arguments:

# strings
In [1]: Timedelta('1 days')
Out[1]: Timedelta('1 days 00:00:00')

In [2]: Timedelta('1 days 00:00:00')
Out[2]: Timedelta('1 days 00:00:00')

In [3]: Timedelta('1 days 2 hours')
Out[3]: Timedelta('1 days 02:00:00')

In [4]: Timedelta('-1 days 2 min 3us')
Out[4]: Timedelta('-2 days +23:57:59.999997')

# like datetime.timedelta
# note: these MUST be specified as keyword argments
In [5]: Timedelta(days=1,seconds=1)
Out[5]: Timedelta('1 days 00:00:01')

# integers with a unit
In [6]: Timedelta(1,unit='d')
Out[6]: Timedelta('1 days 00:00:00')

# from a timedelta/np.timedelta64
In [7]: Timedelta(timedelta(days=1,seconds=1))
Out[7]: Timedelta('1 days 00:00:01')

In [8]: Timedelta(np.timedelta64(1,'ms'))
Out[8]: Timedelta('0 days 00:00:00.001000')

# negative Timedeltas have this string repr
# to be more consistent with datetime.timedelta conventions
In [9]: Timedelta('-1us')
Out[9]: Timedelta('-1 days +23:59:59.999999')

# a NaT
In [10]: Timedelta('nan')
Out[10]: NaT

In [11]: Timedelta('nat')
Out[11]: NaT

DateOffsets (Day, Hour, Minute, Second, Milli, Micro, Nano) can also be used in construction.

In [12]: Timedelta(Second(2))
Out[12]: Timedelta('0 days 00:00:02')

Further, operations among the scalars yield another scalar Timedelta

In [13]: Timedelta(Day(2)) + Timedelta(Second(2)) + Timedelta('00:00:00.000123')
Out[13]: Timedelta('2 days 00:00:02.000123')

to_timedelta

Warning

Prior to 0.15.0 pd.to_timedelta would return a Series for list-like/Series input, and a np.timedelta64 for scalar input. It will now return a TimedeltaIndex for list-like input, Series for Series input, and Timedelta for scalar input.

The arguments to pd.to_timedelta are now (arg,unit='ns',box=True), previously were (arg,box=True,unit='ns') as these are more logical.

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 will output a TimedeltaIndex

In [14]: to_timedelta('1 days 06:05:01.00003')
Out[14]: Timedelta('1 days 06:05:01.000030')

In [15]: to_timedelta('15.5us')
Out[15]: Timedelta('0 days 00:00:00.000015')

In [16]: to_timedelta(['1 days 06:05:01.00003','15.5us','nan'])
Out[16]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['1 days 06:05:01.000030', ..., NaT]
Length: 3, Freq: None

In [17]: to_timedelta(np.arange(5),unit='s')
Out[17]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['00:00:00', ..., '00:00:04']
Length: 5, Freq: None

In [18]: to_timedelta(np.arange(5),unit='d')
Out[18]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['0 days', ..., '4 days']
Length: 5, Freq: None

Operations

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

In [19]: s = Series(date_range('2012-1-1', periods=3, freq='D'))

In [20]: td = Series([ Timedelta(days=i) for i in range(3) ])

In [21]: df = DataFrame(dict(A = s, B = td))

In [22]: df
Out[22]: 
           A      B
0 2012-01-01 0 days
1 2012-01-02 1 days
2 2012-01-03 2 days

In [23]: df['C'] = df['A'] + df['B']

In [24]: df
Out[24]: 
           A      B          C
0 2012-01-01 0 days 2012-01-01
1 2012-01-02 1 days 2012-01-03
2 2012-01-03 2 days 2012-01-05

In [25]: df.dtypes
Out[25]: 
A     datetime64[ns]
B    timedelta64[ns]
C     datetime64[ns]
dtype: object

In [26]: s - s.max()
Out[26]: 
0   -2 days
1   -1 days
2    0 days
dtype: timedelta64[ns]

In [27]: s - datetime(2011,1,1,3,5)
Out[27]: 
0   364 days 20:55:00
1   365 days 20:55:00
2   366 days 20:55:00
dtype: timedelta64[ns]

In [28]: s + timedelta(minutes=5)
Out[28]: 
0   2012-01-01 00:05:00
1   2012-01-02 00:05:00
2   2012-01-03 00:05:00
dtype: datetime64[ns]

In [29]: s + Minute(5)
Out[29]: 
0   2012-01-01 00:05:00
1   2012-01-02 00:05:00
2   2012-01-03 00:05:00
dtype: datetime64[ns]

In [30]: s + Minute(5) + Milli(5)
Out[30]: 
0   2012-01-01 00:05:00.005000
1   2012-01-02 00:05:00.005000
2   2012-01-03 00:05:00.005000
dtype: datetime64[ns]

Operations with scalars from a timedelta64[ns] series

In [31]: y = s - s[0]

In [32]: y
Out[32]: 
0   0 days
1   1 days
2   2 days
dtype: timedelta64[ns]

Series of timedeltas with NaT values are supported

In [33]: y = s - s.shift()

In [34]: y
Out[34]: 
0      NaT
1   1 days
2   1 days
dtype: timedelta64[ns]

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

In [35]: y[1] = np.nan

In [36]: y
Out[36]: 
0      NaT
1      NaT
2   1 days
dtype: timedelta64[ns]

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

In [37]: s.max() - s
Out[37]: 
0   2 days
1   1 days
2   0 days
dtype: timedelta64[ns]

In [38]: datetime(2011,1,1,3,5) - s
Out[38]: 
0   -365 days +03:05:00
1   -366 days +03:05:00
2   -367 days +03:05:00
dtype: timedelta64[ns]

In [39]: timedelta(minutes=5) + s
Out[39]: 
0   2012-01-01 00:05:00
1   2012-01-02 00:05:00
2   2012-01-03 00:05:00
dtype: datetime64[ns]

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

In [40]: A = s - Timestamp('20120101') - Timedelta('00:05:05')

In [41]: B = s - Series(date_range('2012-1-2', periods=3, freq='D'))

In [42]: df = DataFrame(dict(A=A, B=B))

In [43]: df
Out[43]: 
                  A       B
0 -1 days +23:54:55 -1 days
1   0 days 23:54:55 -1 days
2   1 days 23:54:55 -1 days

In [44]: df.min()
Out[44]: 
A   -1 days +23:54:55
B   -1 days +00:00:00
dtype: timedelta64[ns]

In [45]: df.min(axis=1)
Out[45]: 
0   -1 days
1   -1 days
2   -1 days
dtype: timedelta64[ns]

In [46]: df.idxmin()
Out[46]: 
A    0
B    0
dtype: int64

In [47]: df.idxmax()
Out[47]: 
A    2
B    0
dtype: int64

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

In [48]: df.min().max()
Out[48]: Timedelta('-1 days +23:54:55')

In [49]: df.min(axis=1).min()
Out[49]: Timedelta('-1 days +00:00:00')

In [50]: df.min().idxmax()
Out[50]: 'A'

In [51]: df.min(axis=1).idxmin()
Out[51]: 0

You can fillna on timedeltas. Integers will be interpreted as seconds. You can pass a timedelta to get a particular value.

In [52]: y.fillna(0)
Out[52]: 
0   0 days
1   0 days
2   1 days
dtype: timedelta64[ns]

In [53]: y.fillna(10)
Out[53]: 
0   0 days 00:00:10
1   0 days 00:00:10
2   1 days 00:00:00
dtype: timedelta64[ns]

In [54]: y.fillna(Timedelta('-1 days, 00:00:05'))
Out[54]: 
0   -1 days +00:00:05
1   -1 days +00:00:05
2     1 days 00:00:00
dtype: timedelta64[ns]

You can also negate, multiply and use abs on Timedeltas

In [55]: td1 = Timedelta('-1 days 2 hours 3 seconds')

In [56]: td1
Out[56]: Timedelta('-2 days +21:59:57')

In [57]: -1 * td1
Out[57]: Timedelta('1 days 02:00:03')

In [58]: - td1
Out[58]: Timedelta('1 days 02:00:03')

In [59]: abs(td1)
Out[59]: Timedelta('1 days 02:00:03')

Reductions

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

In [60]: y2 = Series(to_timedelta(['-1 days +00:00:05','nat','-1 days +00:00:05','1 days']))

In [61]: y2
Out[61]: 
0   -1 days +00:00:05
1                 NaT
2   -1 days +00:00:05
3     1 days 00:00:00
dtype: timedelta64[ns]

In [62]: y2.mean()
Out[62]: Timedelta('-1 days +16:00:03.333333')

In [63]: y2.median()
Out[63]: Timedelta('-1 days +00:00:05')

In [64]: y2.quantile(.1)
Out[64]: Timedelta('-1 days +00:00:05')

In [65]: y2.sum()
Out[65]: Timedelta('-1 days +00:00:10')

Frequency Conversion

New in version 0.13.

Timedelta Series, TimedeltaIndex, and Timedelta scalars can be converted to other ‘frequencies’ by dividing by another timedelta, or by astyping to a specific timedelta type. These operations yield Series and propogate NaT -> nan. Note that division by the numpy scalar is true division, while astyping is equivalent of floor division.

In [66]: td = Series(date_range('20130101',periods=4)) - \
   ....:      Series(date_range('20121201',periods=4))
   ....: 

In [67]: td[2] += timedelta(minutes=5,seconds=3)

In [68]: td[3] = np.nan

In [69]: td
Out[69]: 
0   31 days 00:00:00
1   31 days 00:00:00
2   31 days 00:05:03
3                NaT
dtype: timedelta64[ns]

# to days
In [70]: td / np.timedelta64(1,'D')
Out[70]: 
0    31.000000
1    31.000000
2    31.003507
3          NaN
dtype: float64

In [71]: td.astype('timedelta64[D]')
Out[71]: 
0    31
1    31
2    31
3   NaN
dtype: float64

# to seconds
In [72]: td / np.timedelta64(1,'s')
Out[72]: 
0    2678400
1    2678400
2    2678703
3        NaN
dtype: float64

In [73]: td.astype('timedelta64[s]')
Out[73]: 
0    2678400
1    2678400
2    2678703
3        NaN
dtype: float64

# to months (these are constant months)
In [74]: td / np.timedelta64(1,'M')
Out[74]: 
0    1.018501
1    1.018501
2    1.018617
3         NaN
dtype: float64

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

In [75]: td * -1
Out[75]: 
0   -31 days +00:00:00
1   -31 days +00:00:00
2   -32 days +23:54:57
3                  NaT
dtype: timedelta64[ns]

In [76]: td * Series([1,2,3,4])
Out[76]: 
0   31 days 00:00:00
1   62 days 00:00:00
2   93 days 00:15:09
3                NaT
dtype: timedelta64[ns]

Attributes

You can access various components of the Timedelta or TimedeltaIndex directly using the attributes days,hours,minutes,seconds,milliseconds,microseconds,nanoseconds. These operations can be directly accessed via the .dt property of the Series as well. These return an integer representing that interval (which is signed according to whether the Timedelta is signed).

For a Series

In [77]: td.dt.days
Out[77]: 
0    31
1    31
2    31
3   NaN
dtype: float64

In [78]: td.dt.seconds
Out[78]: 
0     0
1     0
2     3
3   NaN
dtype: float64

You can access the component field for a scalar Timedelta directly.

In [79]: tds = Timedelta('31 days 5 min 3 sec')

In [80]: tds.days
Out[80]: 31L

In [81]: tds.seconds
Out[81]: 3L

In [82]: (-tds).seconds
Out[82]: 57L

You can use the .components property to access a reduced form of the timedelta. This returns a DataFrame indexed similarly to the Series

In [83]: td.dt.components
Out[83]: 
   days  hours  minutes  seconds  milliseconds  microseconds  nanoseconds
0    31      0        0        0             0             0            0
1    31      0        0        0             0             0            0
2    31      0        5        3             0             0            0
3   NaN    NaN      NaN      NaN           NaN           NaN          NaN

Warning

Timedelta scalars (and TimedeltaIndex) component fields are not the same as the component fields on a datetime.timedelta object. For example, .seconds on a datetime.timedelta object returns the total number of seconds combined between hours, minutes and seconds. In contrast, the pandas Timedelta breaks out hours, minutes, microseconds and nanoseconds separately.

# Timedelta accessor
In [84]: tds = Timedelta('31 days 5 min 3 sec')

In [85]: tds.minutes
Out[85]: 5L

In [86]: tds.seconds
Out[86]: 3L

# datetime.timedelta accessor
# this is 5 minutes * 60 + 3 seconds
In [87]: tds.to_pytimedelta().seconds
Out[87]: 303

TimedeltaIndex

New in version 0.15.0.

To generate an index with time delta, you can use either the TimedeltaIndex or the 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.

In [88]: TimedeltaIndex(['1 days','1 days, 00:00:05',
   ....:                 np.timedelta64(2,'D'),timedelta(days=2,seconds=2)])
   ....: 
Out[88]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['1 days 00:00:00', ..., '2 days 00:00:02']
Length: 4, Freq: None

Similarly to date_range, you can construct regular ranges of a TimedeltaIndex:

In [89]: timedelta_range(start='1 days',periods=5,freq='D')
Out[89]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['1 days', ..., '5 days']
Length: 5, Freq: <Day>

In [90]: timedelta_range(start='1 days',end='2 days',freq='30T')
Out[90]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['1 days 00:00:00', ..., '2 days 00:00:00']
Length: 49, Freq: <30 * Minutes>

Using the TimedeltaIndex

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

In [91]: s = Series(np.arange(100),
   ....:            index=timedelta_range('1 days',periods=100,freq='h'))
   ....: 

In [92]: s
Out[92]: 
1 days 00:00:00    0
1 days 01:00:00    1
1 days 02:00:00    2
1 days 03:00:00    3
1 days 04:00:00    4
...
4 days 22:00:00    94
4 days 23:00:00    95
5 days 00:00:00    96
5 days 01:00:00    97
5 days 02:00:00    98
5 days 03:00:00    99
Freq: <Hour>, Length: 100

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

In [93]: s['1 day':'2 day']
Out[93]: 
1 days 00:00:00    0
1 days 01:00:00    1
1 days 02:00:00    2
1 days 03:00:00    3
1 days 04:00:00    4
...
2 days 18:00:00    42
2 days 19:00:00    43
2 days 20:00:00    44
2 days 21:00:00    45
2 days 22:00:00    46
2 days 23:00:00    47
Length: 48

In [94]: s['1 day 01:00:00']
Out[94]: 1

In [95]: s[Timedelta('1 day 1h')]
Out[95]: 1

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

In [96]: s['1 day':'1 day 5 hours']
Out[96]: 
1 days 00:00:00    0
1 days 01:00:00    1
1 days 02:00:00    2
1 days 03:00:00    3
1 days 04:00:00    4
1 days 05:00:00    5
dtype: int32

Operations

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

In [97]: tdi = TimedeltaIndex(['1 days',pd.NaT,'2 days'])

In [98]: tdi.tolist()
Out[98]: [Timedelta('1 days 00:00:00'), NaT, Timedelta('2 days 00:00:00')]

In [99]: dti = date_range('20130101',periods=3)

In [100]: dti.tolist()
Out[100]: 
[Timestamp('2013-01-01 00:00:00', offset='D'),
 Timestamp('2013-01-02 00:00:00', offset='D'),
 Timestamp('2013-01-03 00:00:00', offset='D')]

In [101]: (dti + tdi).tolist()
Out[101]: [Timestamp('2013-01-02 00:00:00'), NaT, Timestamp('2013-01-05 00:00:00')]

In [102]: (dti - tdi).tolist()
Out[102]: [Timestamp('2012-12-31 00:00:00'), NaT, Timestamp('2013-01-01 00:00:00')]

Conversions

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

In [103]: tdi / np.timedelta64(1,'s')
Out[103]: Float64Index([86400.0, nan, 172800.0], dtype='float64')

In [104]: tdi.astype('timedelta64[s]')
Out[104]: Float64Index([86400.0, nan, 172800.0], dtype='float64')

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

# adding or timedelta and date -> datelike
In [105]: tdi + Timestamp('20130101')
Out[105]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-01-02, ..., 2013-01-03]
Length: 3, Freq: None, Timezone: None

# subtraction of a date and a timedelta -> datelike
# note that trying to subtract a date from a Timedelta will raise an exception
In [106]: (Timestamp('20130101') - tdi).tolist()
Out[106]: [Timestamp('2012-12-31 00:00:00'), NaT, Timestamp('2012-12-30 00:00:00')]

# timedelta + timedelta -> timedelta
In [107]: tdi + Timedelta('10 days')
Out[107]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['11 days', ..., '12 days']
Length: 3, Freq: None

# division can result in a Timedelta if the divisor is an integer
In [108]: tdi / 2
Out[108]: 
<class 'pandas.tseries.tdi.TimedeltaIndex'>
['0 days 12:00:00', ..., '1 days 00:00:00']
Length: 3, Freq: None

# or a Float64Index if the divisor is a Timedelta
In [109]: tdi / tdi[0]
Out[109]: Float64Index([1.0, nan, 2.0], dtype='float64')

Resampling

Similar to timeseries resampling, we can resample with a TimedeltaIndex.

In [110]: s.resample('D')
Out[110]: 
1 days    11.5
2 days    35.5
3 days    59.5
4 days    83.5
5 days    97.5
dtype: float64