1"""
2timedelta support tools
3"""
4
5from __future__ import annotations
6
7from typing import (
8 TYPE_CHECKING,
9 Any,
10 overload,
11)
12
13import numpy as np
14
15from pandas._libs import lib
16from pandas._libs.tslibs import (
17 NaT,
18 NaTType,
19)
20from pandas._libs.tslibs.timedeltas import (
21 Timedelta,
22 disallow_ambiguous_unit,
23 parse_timedelta_unit,
24)
25from pandas.util._decorators import set_module
26
27from pandas.core.dtypes.common import is_list_like
28from pandas.core.dtypes.dtypes import ArrowDtype
29from pandas.core.dtypes.generic import (
30 ABCIndex,
31 ABCSeries,
32)
33
34from pandas.core.arrays.timedeltas import sequence_to_td64ns
35
36if TYPE_CHECKING:
37 from collections.abc import Hashable
38 from datetime import timedelta
39
40 from pandas._libs.tslibs.timedeltas import UnitChoices
41 from pandas._typing import (
42 ArrayLike,
43 DateTimeErrorChoices,
44 )
45
46 from pandas import (
47 Index,
48 Series,
49 TimedeltaIndex,
50 )
51
52
53@overload
54def to_timedelta(
55 arg: str | float | timedelta,
56 unit: UnitChoices | None = ...,
57 errors: DateTimeErrorChoices = ...,
58) -> Timedelta: ...
59
60
61@overload
62def to_timedelta(
63 arg: Series,
64 unit: UnitChoices | None = ...,
65 errors: DateTimeErrorChoices = ...,
66) -> Series: ...
67
68
69@overload
70def to_timedelta(
71 arg: list | tuple | range | ArrayLike | Index,
72 unit: UnitChoices | None = ...,
73 errors: DateTimeErrorChoices = ...,
74) -> TimedeltaIndex: ...
75
76
77@set_module("pandas")
78def to_timedelta(
79 arg: str
80 | int
81 | float
82 | timedelta
83 | list
84 | tuple
85 | range
86 | ArrayLike
87 | Index
88 | Series,
89 unit: UnitChoices | None = None,
90 errors: DateTimeErrorChoices = "raise",
91) -> Timedelta | TimedeltaIndex | Series | NaTType | Any:
92 """
93 Convert argument to timedelta.
94
95 Timedeltas are absolute differences in times, expressed in difference
96 units (e.g. days, hours, minutes, seconds). This method converts
97 an argument from a recognized timedelta format / value into
98 a Timedelta type.
99
100 Parameters
101 ----------
102 arg : str, timedelta, list-like or Series
103 The data to be converted to timedelta.
104
105 .. versionchanged:: 2.0
106 Strings with units 'M', 'Y' and 'y' do not represent
107 unambiguous timedelta values and will raise an exception.
108
109 unit : str, optional
110 Denotes the unit of the arg for numeric `arg`. Defaults to ``"ns"``.
111
112 Possible values:
113
114 * 'W'
115 * 'D' / 'days' / 'day'
116 * 'hours' / 'hour' / 'hr' / 'h'
117 * 'm' / 'minute' / 'min' / 'minutes'
118 * 's' / 'seconds' / 'sec' / 'second'
119 * 'ms' / 'milliseconds' / 'millisecond' / 'milli' / 'millis'
120 * 'us' / 'microseconds' / 'microsecond' / 'micro' / 'micros'
121 * 'ns' / 'nanoseconds' / 'nano' / 'nanos' / 'nanosecond'
122
123 Must not be specified when `arg` contains strings and ``errors="raise"``.
124
125 errors : {'raise', 'coerce'}, default 'raise'
126 - If 'raise', then invalid parsing will raise an exception.
127 - If 'coerce', then invalid parsing will be set as NaT.
128
129 Returns
130 -------
131 timedelta
132 If parsing succeeded.
133 Return type depends on input:
134
135 - list-like: TimedeltaIndex of timedelta64 dtype
136 - Series: Series of timedelta64 dtype
137 - scalar: Timedelta
138
139 See Also
140 --------
141 DataFrame.astype : Cast argument to a specified dtype.
142 to_datetime : Convert argument to datetime.
143 convert_dtypes : Convert dtypes.
144
145 Notes
146 -----
147 If the precision is higher than nanoseconds, the precision of the duration is
148 truncated to nanoseconds for string inputs.
149
150 Examples
151 --------
152 Parsing a single string to a Timedelta:
153
154 >>> pd.to_timedelta("1 days 06:05:01.00003")
155 Timedelta('1 days 06:05:01.000030')
156 >>> pd.to_timedelta("15.5us")
157 Timedelta('0 days 00:00:00.000015500')
158
159 Parsing a list or array of strings:
160
161 >>> pd.to_timedelta(["1 days 06:05:01.00003", "15.5us", "nan"])
162 TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015500', NaT],
163 dtype='timedelta64[ns]', freq=None)
164
165 Converting numbers by specifying the `unit` keyword argument:
166
167 >>> pd.to_timedelta(np.arange(5), unit="s")
168 TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02',
169 '0 days 00:00:03', '0 days 00:00:04'],
170 dtype='timedelta64[s]', freq=None)
171 >>> pd.to_timedelta(np.arange(5), unit="D")
172 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
173 dtype='timedelta64[s]', freq=None)
174 """
175 if unit is not None:
176 unit = parse_timedelta_unit(unit)
177 disallow_ambiguous_unit(unit)
178
179 if errors not in ("raise", "coerce"):
180 raise ValueError("errors must be one of 'raise', or 'coerce'.")
181
182 if arg is None:
183 return NaT
184 elif isinstance(arg, ABCSeries):
185 values = _convert_listlike(arg._values, unit=unit, errors=errors)
186 return arg._constructor(values, index=arg.index, name=arg.name)
187 elif isinstance(arg, ABCIndex):
188 return _convert_listlike(arg, unit=unit, errors=errors, name=arg.name)
189 elif isinstance(arg, np.ndarray) and arg.ndim == 0:
190 # extract array scalar and process below
191 # error: Incompatible types in assignment (expression has type "object",
192 # variable has type "Union[str, int, float, timedelta, List[Any],
193 # Tuple[Any, ...], Union[Union[ExtensionArray, ndarray[Any, Any]], Index,
194 # Series]]") [assignment]
195 arg = lib.item_from_zerodim(arg) # type: ignore[assignment]
196 elif is_list_like(arg) and getattr(arg, "ndim", 1) == 1:
197 return _convert_listlike(arg, unit=unit, errors=errors)
198 elif getattr(arg, "ndim", 1) > 1:
199 raise TypeError(
200 "arg must be a string, timedelta, list, tuple, 1-d array, or Series"
201 )
202
203 if isinstance(arg, str) and unit is not None:
204 raise ValueError("unit must not be specified if the input is/contains a str")
205
206 # ...so it must be a scalar value. Return scalar.
207 return _coerce_scalar_to_timedelta_type(arg, unit=unit, errors=errors)
208
209
210def _coerce_scalar_to_timedelta_type(
211 r, unit: UnitChoices | None = "ns", errors: DateTimeErrorChoices = "raise"
212) -> Timedelta | NaTType:
213 """Convert string 'r' to a timedelta object."""
214 result: Timedelta | NaTType
215
216 try:
217 result = Timedelta(r, unit)
218 except ValueError:
219 if errors == "raise":
220 raise
221 # coerce
222 result = NaT
223
224 return result
225
226
227def _convert_listlike(
228 arg,
229 unit: UnitChoices | None = None,
230 errors: DateTimeErrorChoices = "raise",
231 name: Hashable | None = None,
232):
233 """Convert a list of objects to a timedelta index object."""
234 arg_dtype = getattr(arg, "dtype", None)
235 if isinstance(arg, (list, tuple)) or arg_dtype is None:
236 arg = np.array(arg, dtype=object)
237 elif isinstance(arg_dtype, ArrowDtype) and arg_dtype.kind == "m":
238 return arg
239
240 td64arr = sequence_to_td64ns(arg, unit=unit, errors=errors, copy=False)[0]
241
242 from pandas import TimedeltaIndex
243
244 copy = td64arr is arg or np.may_share_memory(arg, td64arr)
245 value = TimedeltaIndex(td64arr, name=name, copy=copy)
246 return value