timedeltas.pyx 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171
  1. import collections
  2. import warnings
  3. cimport cython
  4. from cpython.object cimport (
  5. Py_EQ,
  6. Py_GE,
  7. Py_GT,
  8. Py_LE,
  9. Py_LT,
  10. Py_NE,
  11. PyObject,
  12. PyObject_RichCompare,
  13. )
  14. import numpy as np
  15. cimport numpy as cnp
  16. from numpy cimport (
  17. int64_t,
  18. ndarray,
  19. )
  20. cnp.import_array()
  21. from cpython.datetime cimport (
  22. PyDateTime_Check,
  23. PyDelta_Check,
  24. import_datetime,
  25. timedelta,
  26. )
  27. import_datetime()
  28. cimport pandas._libs.tslibs.util as util
  29. from pandas._libs.tslibs.base cimport ABCTimestamp
  30. from pandas._libs.tslibs.conversion cimport (
  31. cast_from_unit,
  32. precision_from_unit,
  33. )
  34. from pandas._libs.tslibs.dtypes cimport (
  35. get_supported_reso,
  36. npy_unit_to_abbrev,
  37. )
  38. from pandas._libs.tslibs.nattype cimport (
  39. NPY_NAT,
  40. c_NaT as NaT,
  41. c_nat_strings as nat_strings,
  42. checknull_with_nat,
  43. )
  44. from pandas._libs.tslibs.np_datetime cimport (
  45. NPY_DATETIMEUNIT,
  46. NPY_FR_ns,
  47. cmp_dtstructs,
  48. cmp_scalar,
  49. convert_reso,
  50. get_datetime64_unit,
  51. get_timedelta64_value,
  52. get_unit_from_dtype,
  53. npy_datetimestruct,
  54. pandas_datetime_to_datetimestruct,
  55. pandas_timedelta_to_timedeltastruct,
  56. pandas_timedeltastruct,
  57. )
  58. from pandas._libs.tslibs.np_datetime import (
  59. OutOfBoundsDatetime,
  60. OutOfBoundsTimedelta,
  61. )
  62. from pandas._libs.tslibs.offsets cimport is_tick_object
  63. from pandas._libs.tslibs.util cimport (
  64. is_array,
  65. is_datetime64_object,
  66. is_float_object,
  67. is_integer_object,
  68. is_timedelta64_object,
  69. )
  70. from pandas._libs.tslibs.fields import (
  71. RoundTo,
  72. round_nsint64,
  73. )
  74. # ----------------------------------------------------------------------
  75. # Constants
  76. # components named tuple
  77. Components = collections.namedtuple(
  78. "Components",
  79. [
  80. "days",
  81. "hours",
  82. "minutes",
  83. "seconds",
  84. "milliseconds",
  85. "microseconds",
  86. "nanoseconds",
  87. ],
  88. )
  89. # This should be kept consistent with UnitChoices in pandas/_libs/tslibs/timedeltas.pyi
  90. cdef dict timedelta_abbrevs = {
  91. "Y": "Y",
  92. "y": "Y",
  93. "M": "M",
  94. "W": "W",
  95. "w": "W",
  96. "D": "D",
  97. "d": "D",
  98. "days": "D",
  99. "day": "D",
  100. "hours": "h",
  101. "hour": "h",
  102. "hr": "h",
  103. "h": "h",
  104. "m": "m",
  105. "minute": "m",
  106. "min": "m",
  107. "minutes": "m",
  108. "t": "m",
  109. "s": "s",
  110. "seconds": "s",
  111. "sec": "s",
  112. "second": "s",
  113. "ms": "ms",
  114. "milliseconds": "ms",
  115. "millisecond": "ms",
  116. "milli": "ms",
  117. "millis": "ms",
  118. "l": "ms",
  119. "us": "us",
  120. "microseconds": "us",
  121. "microsecond": "us",
  122. "µs": "us",
  123. "micro": "us",
  124. "micros": "us",
  125. "u": "us",
  126. "ns": "ns",
  127. "nanoseconds": "ns",
  128. "nano": "ns",
  129. "nanos": "ns",
  130. "nanosecond": "ns",
  131. "n": "ns",
  132. }
  133. _no_input = object()
  134. # ----------------------------------------------------------------------
  135. # API
  136. @cython.boundscheck(False)
  137. @cython.wraparound(False)
  138. def ints_to_pytimedelta(ndarray m8values, box=False):
  139. """
  140. convert an i8 repr to an ndarray of timedelta or Timedelta (if box ==
  141. True)
  142. Parameters
  143. ----------
  144. arr : ndarray[timedelta64]
  145. box : bool, default False
  146. Returns
  147. -------
  148. result : ndarray[object]
  149. array of Timedelta or timedeltas objects
  150. """
  151. cdef:
  152. NPY_DATETIMEUNIT reso = get_unit_from_dtype(m8values.dtype)
  153. Py_ssize_t i, n = m8values.size
  154. int64_t value
  155. object res_val
  156. # Note that `result` (and thus `result_flat`) is C-order and
  157. # `it` iterates C-order as well, so the iteration matches
  158. # See discussion at
  159. # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
  160. ndarray result = cnp.PyArray_EMPTY(
  161. m8values.ndim, m8values.shape, cnp.NPY_OBJECT, 0
  162. )
  163. object[::1] res_flat = result.ravel() # should NOT be a copy
  164. ndarray arr = m8values.view("i8")
  165. cnp.flatiter it = cnp.PyArray_IterNew(arr)
  166. for i in range(n):
  167. # Analogous to: value = arr[i]
  168. value = (<int64_t*>cnp.PyArray_ITER_DATA(it))[0]
  169. if value == NPY_NAT:
  170. res_val = <object>NaT
  171. else:
  172. if box:
  173. res_val = _timedelta_from_value_and_reso(Timedelta, value, reso=reso)
  174. elif reso == NPY_DATETIMEUNIT.NPY_FR_ns:
  175. res_val = timedelta(microseconds=int(value) / 1000)
  176. elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
  177. res_val = timedelta(microseconds=value)
  178. elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
  179. res_val = timedelta(milliseconds=value)
  180. elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
  181. res_val = timedelta(seconds=value)
  182. elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
  183. res_val = timedelta(minutes=value)
  184. elif reso == NPY_DATETIMEUNIT.NPY_FR_h:
  185. res_val = timedelta(hours=value)
  186. elif reso == NPY_DATETIMEUNIT.NPY_FR_D:
  187. res_val = timedelta(days=value)
  188. elif reso == NPY_DATETIMEUNIT.NPY_FR_W:
  189. res_val = timedelta(weeks=value)
  190. else:
  191. # Month, Year, NPY_FR_GENERIC, pico, femto, atto
  192. raise NotImplementedError(reso)
  193. # Note: we can index result directly instead of using PyArray_MultiIter_DATA
  194. # like we do for the other functions because result is known C-contiguous
  195. # and is the first argument to PyArray_MultiIterNew2. The usual pattern
  196. # does not seem to work with object dtype.
  197. # See discussion at
  198. # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
  199. res_flat[i] = res_val
  200. cnp.PyArray_ITER_NEXT(it)
  201. return result
  202. # ----------------------------------------------------------------------
  203. cpdef int64_t delta_to_nanoseconds(
  204. delta,
  205. NPY_DATETIMEUNIT reso=NPY_FR_ns,
  206. bint round_ok=True,
  207. ) except? -1:
  208. # Note: this will raise on timedelta64 with Y or M unit
  209. cdef:
  210. NPY_DATETIMEUNIT in_reso
  211. int64_t n
  212. if is_tick_object(delta):
  213. n = delta.n
  214. in_reso = delta._creso
  215. elif isinstance(delta, _Timedelta):
  216. n = delta._value
  217. in_reso = delta._creso
  218. elif is_timedelta64_object(delta):
  219. in_reso = get_datetime64_unit(delta)
  220. if in_reso == NPY_DATETIMEUNIT.NPY_FR_Y or in_reso == NPY_DATETIMEUNIT.NPY_FR_M:
  221. raise ValueError(
  222. "delta_to_nanoseconds does not support Y or M units, "
  223. "as their duration in nanoseconds is ambiguous."
  224. )
  225. n = get_timedelta64_value(delta)
  226. elif PyDelta_Check(delta):
  227. in_reso = NPY_DATETIMEUNIT.NPY_FR_us
  228. try:
  229. n = (
  230. delta.days * 24 * 3600 * 1_000_000
  231. + delta.seconds * 1_000_000
  232. + delta.microseconds
  233. )
  234. except OverflowError as err:
  235. raise OutOfBoundsTimedelta(*err.args) from err
  236. else:
  237. raise TypeError(type(delta))
  238. try:
  239. return convert_reso(n, in_reso, reso, round_ok=round_ok)
  240. except (OutOfBoundsDatetime, OverflowError) as err:
  241. # Catch OutOfBoundsDatetime bc convert_reso can call check_dts_bounds
  242. # for Y/M-resolution cases
  243. unit_str = npy_unit_to_abbrev(reso)
  244. raise OutOfBoundsTimedelta(
  245. f"Cannot cast {str(delta)} to unit={unit_str} without overflow."
  246. ) from err
  247. @cython.overflowcheck(True)
  248. cdef object ensure_td64ns(object ts):
  249. """
  250. Overflow-safe implementation of td64.astype("m8[ns]")
  251. Parameters
  252. ----------
  253. ts : np.timedelta64
  254. Returns
  255. -------
  256. np.timedelta64[ns]
  257. """
  258. cdef:
  259. NPY_DATETIMEUNIT td64_unit
  260. int64_t td64_value, mult
  261. str unitstr
  262. td64_unit = get_datetime64_unit(ts)
  263. if (
  264. td64_unit != NPY_DATETIMEUNIT.NPY_FR_ns
  265. and td64_unit != NPY_DATETIMEUNIT.NPY_FR_GENERIC
  266. ):
  267. unitstr = npy_unit_to_abbrev(td64_unit)
  268. td64_value = get_timedelta64_value(ts)
  269. mult = precision_from_unit(unitstr)[0]
  270. try:
  271. # NB: cython#1381 this cannot be *=
  272. td64_value = td64_value * mult
  273. except OverflowError as err:
  274. raise OutOfBoundsTimedelta(ts) from err
  275. return np.timedelta64(td64_value, "ns")
  276. return ts
  277. cdef convert_to_timedelta64(object ts, str unit):
  278. """
  279. Convert an incoming object to a timedelta64 if possible.
  280. Before calling, unit must be standardized to avoid repeated unit conversion
  281. Handle these types of objects:
  282. - timedelta/Timedelta
  283. - timedelta64
  284. - an offset
  285. - np.int64 (with unit providing a possible modifier)
  286. - None/NaT
  287. Return an ns based int64
  288. """
  289. # Caller is responsible for checking unit not in ["Y", "y", "M"]
  290. if checknull_with_nat(ts):
  291. return np.timedelta64(NPY_NAT, "ns")
  292. elif isinstance(ts, _Timedelta):
  293. # already in the proper format
  294. if ts._creso != NPY_FR_ns:
  295. ts = ts.as_unit("ns").asm8
  296. else:
  297. ts = np.timedelta64(ts._value, "ns")
  298. elif is_timedelta64_object(ts):
  299. ts = ensure_td64ns(ts)
  300. elif is_integer_object(ts):
  301. if ts == NPY_NAT:
  302. return np.timedelta64(NPY_NAT, "ns")
  303. else:
  304. ts = _maybe_cast_from_unit(ts, unit)
  305. elif is_float_object(ts):
  306. ts = _maybe_cast_from_unit(ts, unit)
  307. elif isinstance(ts, str):
  308. if (len(ts) > 0 and ts[0] == "P") or (len(ts) > 1 and ts[:2] == "-P"):
  309. ts = parse_iso_format_string(ts)
  310. else:
  311. ts = parse_timedelta_string(ts)
  312. ts = np.timedelta64(ts, "ns")
  313. elif is_tick_object(ts):
  314. ts = np.timedelta64(ts.nanos, "ns")
  315. if PyDelta_Check(ts):
  316. ts = np.timedelta64(delta_to_nanoseconds(ts), "ns")
  317. elif not is_timedelta64_object(ts):
  318. raise TypeError(f"Invalid type for timedelta scalar: {type(ts)}")
  319. return ts.astype("timedelta64[ns]")
  320. cdef _maybe_cast_from_unit(ts, str unit):
  321. # caller is responsible for checking
  322. # assert unit not in ["Y", "y", "M"]
  323. try:
  324. ts = cast_from_unit(ts, unit)
  325. except OutOfBoundsDatetime as err:
  326. raise OutOfBoundsTimedelta(
  327. f"Cannot cast {ts} from {unit} to 'ns' without overflow."
  328. ) from err
  329. ts = np.timedelta64(ts, "ns")
  330. return ts
  331. @cython.boundscheck(False)
  332. @cython.wraparound(False)
  333. def array_to_timedelta64(
  334. ndarray values, str unit=None, str errors="raise"
  335. ) -> ndarray:
  336. # values is object-dtype, may be 2D
  337. """
  338. Convert an ndarray to an array of timedeltas. If errors == 'coerce',
  339. coerce non-convertible objects to NaT. Otherwise, raise.
  340. Returns
  341. -------
  342. np.ndarray[timedelta64ns]
  343. """
  344. # Caller is responsible for checking
  345. assert unit not in ["Y", "y", "M"]
  346. cdef:
  347. Py_ssize_t i, n = values.size
  348. ndarray result = np.empty((<object>values).shape, dtype="m8[ns]")
  349. object item
  350. int64_t ival
  351. cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, values)
  352. cnp.flatiter it
  353. if values.descr.type_num != cnp.NPY_OBJECT:
  354. # raise here otherwise we segfault below
  355. raise TypeError("array_to_timedelta64 'values' must have object dtype")
  356. if errors not in {"ignore", "raise", "coerce"}:
  357. raise ValueError("errors must be one of {'ignore', 'raise', or 'coerce'}")
  358. if unit is not None and errors != "coerce":
  359. it = cnp.PyArray_IterNew(values)
  360. for i in range(n):
  361. # Analogous to: item = values[i]
  362. item = cnp.PyArray_GETITEM(values, cnp.PyArray_ITER_DATA(it))
  363. if isinstance(item, str):
  364. raise ValueError(
  365. "unit must not be specified if the input contains a str"
  366. )
  367. cnp.PyArray_ITER_NEXT(it)
  368. # Usually, we have all strings. If so, we hit the fast path.
  369. # If this path fails, we try conversion a different way, and
  370. # this is where all of the error handling will take place.
  371. try:
  372. for i in range(n):
  373. # Analogous to: item = values[i]
  374. item = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
  375. ival = _item_to_timedelta64_fastpath(item)
  376. # Analogous to: iresult[i] = ival
  377. (<int64_t*>cnp.PyArray_MultiIter_DATA(mi, 0))[0] = ival
  378. cnp.PyArray_MultiIter_NEXT(mi)
  379. except (TypeError, ValueError):
  380. cnp.PyArray_MultiIter_RESET(mi)
  381. parsed_unit = parse_timedelta_unit(unit or "ns")
  382. for i in range(n):
  383. item = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
  384. ival = _item_to_timedelta64(item, parsed_unit, errors)
  385. (<int64_t*>cnp.PyArray_MultiIter_DATA(mi, 0))[0] = ival
  386. cnp.PyArray_MultiIter_NEXT(mi)
  387. return result
  388. cdef int64_t _item_to_timedelta64_fastpath(object item) except? -1:
  389. """
  390. See array_to_timedelta64.
  391. """
  392. if item is NaT:
  393. # we allow this check in the fast-path because NaT is a C-object
  394. # so this is an inexpensive check
  395. return NPY_NAT
  396. else:
  397. return parse_timedelta_string(item)
  398. cdef int64_t _item_to_timedelta64(
  399. object item,
  400. str parsed_unit,
  401. str errors
  402. ) except? -1:
  403. """
  404. See array_to_timedelta64.
  405. """
  406. try:
  407. return get_timedelta64_value(convert_to_timedelta64(item, parsed_unit))
  408. except ValueError as err:
  409. if errors == "coerce":
  410. return NPY_NAT
  411. elif "unit abbreviation w/o a number" in str(err):
  412. # re-raise with more pertinent message
  413. msg = f"Could not convert '{item}' to NumPy timedelta"
  414. raise ValueError(msg) from err
  415. else:
  416. raise
  417. @cython.cpow(True)
  418. cdef int64_t parse_timedelta_string(str ts) except? -1:
  419. """
  420. Parse a regular format timedelta string. Return an int64_t (in ns)
  421. or raise a ValueError on an invalid parse.
  422. """
  423. cdef:
  424. unicode c
  425. bint neg = 0, have_dot = 0, have_value = 0, have_hhmmss = 0
  426. object current_unit = None
  427. int64_t result = 0, m = 0, r
  428. list number = [], frac = [], unit = []
  429. # neg : tracks if we have a leading negative for the value
  430. # have_dot : tracks if we are processing a dot (either post hhmmss or
  431. # inside an expression)
  432. # have_value : track if we have at least 1 leading unit
  433. # have_hhmmss : tracks if we have a regular format hh:mm:ss
  434. if len(ts) == 0 or ts in nat_strings:
  435. return NPY_NAT
  436. for c in ts:
  437. # skip whitespace / commas
  438. if c == " " or c == ",":
  439. pass
  440. # positive signs are ignored
  441. elif c == "+":
  442. pass
  443. # neg
  444. elif c == "-":
  445. if neg or have_value or have_hhmmss:
  446. raise ValueError("only leading negative signs are allowed")
  447. neg = 1
  448. # number (ascii codes)
  449. elif ord(c) >= 48 and ord(c) <= 57:
  450. if have_dot:
  451. # we found a dot, but now its just a fraction
  452. if len(unit):
  453. number.append(c)
  454. have_dot = 0
  455. else:
  456. frac.append(c)
  457. elif not len(unit):
  458. number.append(c)
  459. else:
  460. r = timedelta_from_spec(number, frac, unit)
  461. unit, number, frac = [], [c], []
  462. result += timedelta_as_neg(r, neg)
  463. # hh:mm:ss.
  464. elif c == ":":
  465. # we flip this off if we have a leading value
  466. if have_value:
  467. neg = 0
  468. # we are in the pattern hh:mm:ss pattern
  469. if len(number):
  470. if current_unit is None:
  471. current_unit = "h"
  472. m = 1000000000 * 3600
  473. elif current_unit == "h":
  474. current_unit = "m"
  475. m = 1000000000 * 60
  476. elif current_unit == "m":
  477. current_unit = "s"
  478. m = 1000000000
  479. r = <int64_t>int("".join(number)) * m
  480. result += timedelta_as_neg(r, neg)
  481. have_hhmmss = 1
  482. else:
  483. raise ValueError(f"expecting hh:mm:ss format, received: {ts}")
  484. unit, number = [], []
  485. # after the decimal point
  486. elif c == ".":
  487. if len(number) and current_unit is not None:
  488. # by definition we had something like
  489. # so we need to evaluate the final field from a
  490. # hh:mm:ss (so current_unit is 'm')
  491. if current_unit != "m":
  492. raise ValueError("expected hh:mm:ss format before .")
  493. m = 1000000000
  494. r = <int64_t>int("".join(number)) * m
  495. result += timedelta_as_neg(r, neg)
  496. have_value = 1
  497. unit, number, frac = [], [], []
  498. have_dot = 1
  499. # unit
  500. else:
  501. unit.append(c)
  502. have_value = 1
  503. have_dot = 0
  504. # we had a dot, but we have a fractional
  505. # value since we have an unit
  506. if have_dot and len(unit):
  507. r = timedelta_from_spec(number, frac, unit)
  508. result += timedelta_as_neg(r, neg)
  509. # we have a dot as part of a regular format
  510. # e.g. hh:mm:ss.fffffff
  511. elif have_dot:
  512. if ((len(number) or len(frac)) and not len(unit)
  513. and current_unit is None):
  514. raise ValueError("no units specified")
  515. if len(frac) > 0 and len(frac) <= 3:
  516. m = 10**(3 -len(frac)) * 1000 * 1000
  517. elif len(frac) > 3 and len(frac) <= 6:
  518. m = 10**(6 -len(frac)) * 1000
  519. elif len(frac) > 6 and len(frac) <= 9:
  520. m = 10**(9 -len(frac))
  521. else:
  522. m = 1
  523. frac = frac[:9]
  524. r = <int64_t>int("".join(frac)) * m
  525. result += timedelta_as_neg(r, neg)
  526. # we have a regular format
  527. # we must have seconds at this point (hence the unit is still 'm')
  528. elif current_unit is not None:
  529. if current_unit != "m":
  530. raise ValueError("expected hh:mm:ss format")
  531. m = 1000000000
  532. r = <int64_t>int("".join(number)) * m
  533. result += timedelta_as_neg(r, neg)
  534. # we have a last abbreviation
  535. elif len(unit):
  536. if len(number):
  537. r = timedelta_from_spec(number, frac, unit)
  538. result += timedelta_as_neg(r, neg)
  539. else:
  540. raise ValueError("unit abbreviation w/o a number")
  541. # we only have symbols and no numbers
  542. elif len(number) == 0:
  543. raise ValueError("symbols w/o a number")
  544. # treat as nanoseconds
  545. # but only if we don't have anything else
  546. else:
  547. if have_value:
  548. raise ValueError("have leftover units")
  549. if len(number):
  550. r = timedelta_from_spec(number, frac, "ns")
  551. result += timedelta_as_neg(r, neg)
  552. return result
  553. cdef int64_t timedelta_as_neg(int64_t value, bint neg):
  554. """
  555. Parameters
  556. ----------
  557. value : int64_t of the timedelta value
  558. neg : bool if the a negative value
  559. """
  560. if neg:
  561. return -value
  562. return value
  563. cdef timedelta_from_spec(object number, object frac, object unit):
  564. """
  565. Parameters
  566. ----------
  567. number : a list of number digits
  568. frac : a list of frac digits
  569. unit : a list of unit characters
  570. """
  571. cdef:
  572. str n
  573. unit = "".join(unit)
  574. if unit in ["M", "Y", "y"]:
  575. raise ValueError(
  576. "Units 'M', 'Y' and 'y' do not represent unambiguous timedelta "
  577. "values and are not supported."
  578. )
  579. unit = parse_timedelta_unit(unit)
  580. n = "".join(number) + "." + "".join(frac)
  581. return cast_from_unit(float(n), unit)
  582. cpdef inline str parse_timedelta_unit(str unit):
  583. """
  584. Parameters
  585. ----------
  586. unit : str or None
  587. Returns
  588. -------
  589. str
  590. Canonical unit string.
  591. Raises
  592. ------
  593. ValueError : on non-parseable input
  594. """
  595. if unit is None:
  596. return "ns"
  597. elif unit == "M":
  598. return unit
  599. try:
  600. return timedelta_abbrevs[unit.lower()]
  601. except KeyError:
  602. raise ValueError(f"invalid unit abbreviation: {unit}")
  603. # ----------------------------------------------------------------------
  604. # Timedelta ops utilities
  605. cdef bint _validate_ops_compat(other):
  606. # return True if we are compat with operating
  607. if checknull_with_nat(other):
  608. return True
  609. elif is_any_td_scalar(other):
  610. return True
  611. elif isinstance(other, str):
  612. return True
  613. return False
  614. def _op_unary_method(func, name):
  615. def f(self):
  616. new_value = func(self._value)
  617. return _timedelta_from_value_and_reso(Timedelta, new_value, self._creso)
  618. f.__name__ = name
  619. return f
  620. def _binary_op_method_timedeltalike(op, name):
  621. # define a binary operation that only works if the other argument is
  622. # timedelta like or an array of timedeltalike
  623. def f(self, other):
  624. if other is NaT:
  625. return NaT
  626. elif is_datetime64_object(other) or (
  627. PyDateTime_Check(other) and not isinstance(other, ABCTimestamp)
  628. ):
  629. # this case is for a datetime object that is specifically
  630. # *not* a Timestamp, as the Timestamp case will be
  631. # handled after `_validate_ops_compat` returns False below
  632. from pandas._libs.tslibs.timestamps import Timestamp
  633. return op(self, Timestamp(other))
  634. # We are implicitly requiring the canonical behavior to be
  635. # defined by Timestamp methods.
  636. elif is_array(other):
  637. if other.ndim == 0:
  638. # see also: item_from_zerodim
  639. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  640. return f(self, item)
  641. elif other.dtype.kind in ["m", "M"]:
  642. return op(self.to_timedelta64(), other)
  643. elif other.dtype.kind == "O":
  644. return np.array([op(self, x) for x in other])
  645. else:
  646. return NotImplemented
  647. elif not _validate_ops_compat(other):
  648. # Includes any of our non-cython classes
  649. return NotImplemented
  650. try:
  651. other = Timedelta(other)
  652. except ValueError:
  653. # failed to parse as timedelta
  654. return NotImplemented
  655. if other is NaT:
  656. # e.g. if original other was timedelta64('NaT')
  657. return NaT
  658. # Matching numpy, we cast to the higher resolution. Unlike numpy,
  659. # we raise instead of silently overflowing during this casting.
  660. if self._creso < other._creso:
  661. self = (<_Timedelta>self)._as_creso(other._creso, round_ok=True)
  662. elif self._creso > other._creso:
  663. other = (<_Timedelta>other)._as_creso(self._creso, round_ok=True)
  664. res = op(self._value, other._value)
  665. if res == NPY_NAT:
  666. # e.g. test_implementation_limits
  667. # TODO: more generally could do an overflowcheck in op?
  668. return NaT
  669. return _timedelta_from_value_and_reso(Timedelta, res, reso=self._creso)
  670. f.__name__ = name
  671. return f
  672. # ----------------------------------------------------------------------
  673. # Timedelta Construction
  674. cdef int64_t parse_iso_format_string(str ts) except? -1:
  675. """
  676. Extracts and cleanses the appropriate values from a match object with
  677. groups for each component of an ISO 8601 duration
  678. Parameters
  679. ----------
  680. ts: str
  681. ISO 8601 Duration formatted string
  682. Returns
  683. -------
  684. ns: int64_t
  685. Precision in nanoseconds of matched ISO 8601 duration
  686. Raises
  687. ------
  688. ValueError
  689. If ``ts`` cannot be parsed
  690. """
  691. cdef:
  692. unicode c
  693. int64_t result = 0, r
  694. int p = 0, sign = 1
  695. object dec_unit = "ms", err_msg
  696. bint have_dot = 0, have_value = 0, neg = 0
  697. list number = [], unit = []
  698. err_msg = f"Invalid ISO 8601 Duration format - {ts}"
  699. if ts[0] == "-":
  700. sign = -1
  701. ts = ts[1:]
  702. for c in ts:
  703. # number (ascii codes)
  704. if 48 <= ord(c) <= 57:
  705. have_value = 1
  706. if have_dot:
  707. if p == 3 and dec_unit != "ns":
  708. unit.append(dec_unit)
  709. if dec_unit == "ms":
  710. dec_unit = "us"
  711. elif dec_unit == "us":
  712. dec_unit = "ns"
  713. p = 0
  714. p += 1
  715. if not len(unit):
  716. number.append(c)
  717. else:
  718. r = timedelta_from_spec(number, "0", unit)
  719. result += timedelta_as_neg(r, neg)
  720. neg = 0
  721. unit, number = [], [c]
  722. else:
  723. if c == "P" or c == "T":
  724. pass # ignore marking characters P and T
  725. elif c == "-":
  726. if neg or have_value:
  727. raise ValueError(err_msg)
  728. else:
  729. neg = 1
  730. elif c == "+":
  731. pass
  732. elif c in ["W", "D", "H", "M"]:
  733. if c in ["H", "M"] and len(number) > 2:
  734. raise ValueError(err_msg)
  735. if c == "M":
  736. c = "min"
  737. unit.append(c)
  738. r = timedelta_from_spec(number, "0", unit)
  739. result += timedelta_as_neg(r, neg)
  740. neg = 0
  741. unit, number = [], []
  742. elif c == ".":
  743. # append any seconds
  744. if len(number):
  745. r = timedelta_from_spec(number, "0", "S")
  746. result += timedelta_as_neg(r, neg)
  747. unit, number = [], []
  748. have_dot = 1
  749. elif c == "S":
  750. if have_dot: # ms, us, or ns
  751. if not len(number) or p > 3:
  752. raise ValueError(err_msg)
  753. # pad to 3 digits as required
  754. pad = 3 - p
  755. while pad > 0:
  756. number.append("0")
  757. pad -= 1
  758. r = timedelta_from_spec(number, "0", dec_unit)
  759. result += timedelta_as_neg(r, neg)
  760. else: # seconds
  761. r = timedelta_from_spec(number, "0", "S")
  762. result += timedelta_as_neg(r, neg)
  763. else:
  764. raise ValueError(err_msg)
  765. if not have_value:
  766. # Received string only - never parsed any values
  767. raise ValueError(err_msg)
  768. return sign*result
  769. cdef _to_py_int_float(v):
  770. # Note: This used to be defined inside Timedelta.__new__
  771. # but cython will not allow `cdef` functions to be defined dynamically.
  772. if is_integer_object(v):
  773. return int(v)
  774. elif is_float_object(v):
  775. return float(v)
  776. raise TypeError(f"Invalid type {type(v)}. Must be int or float.")
  777. def _timedelta_unpickle(value, reso):
  778. return _timedelta_from_value_and_reso(Timedelta, value, reso)
  779. cdef _timedelta_from_value_and_reso(cls, int64_t value, NPY_DATETIMEUNIT reso):
  780. # Could make this a classmethod if/when cython supports cdef classmethods
  781. cdef:
  782. _Timedelta td_base
  783. assert value != NPY_NAT
  784. # For millisecond and second resos, we cannot actually pass int(value) because
  785. # many cases would fall outside of the pytimedelta implementation bounds.
  786. # We pass 0 instead, and override seconds, microseconds, days.
  787. # In principle we could pass 0 for ns and us too.
  788. if reso == NPY_FR_ns:
  789. td_base = _Timedelta.__new__(cls, microseconds=int(value) // 1000)
  790. elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
  791. td_base = _Timedelta.__new__(cls, microseconds=int(value))
  792. elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
  793. td_base = _Timedelta.__new__(cls, milliseconds=0)
  794. elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
  795. td_base = _Timedelta.__new__(cls, seconds=0)
  796. # Other resolutions are disabled but could potentially be implemented here:
  797. # elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
  798. # td_base = _Timedelta.__new__(Timedelta, minutes=int(value))
  799. # elif reso == NPY_DATETIMEUNIT.NPY_FR_h:
  800. # td_base = _Timedelta.__new__(Timedelta, hours=int(value))
  801. # elif reso == NPY_DATETIMEUNIT.NPY_FR_D:
  802. # td_base = _Timedelta.__new__(Timedelta, days=int(value))
  803. else:
  804. raise NotImplementedError(
  805. "Only resolutions 's', 'ms', 'us', 'ns' are supported."
  806. )
  807. td_base._value= value
  808. td_base._is_populated = 0
  809. td_base._creso = reso
  810. return td_base
  811. class MinMaxReso:
  812. """
  813. We need to define min/max/resolution on both the Timedelta _instance_
  814. and Timedelta class. On an instance, these depend on the object's _reso.
  815. On the class, we default to the values we would get with nanosecond _reso.
  816. """
  817. def __init__(self, name):
  818. self._name = name
  819. def __get__(self, obj, type=None):
  820. if self._name == "min":
  821. val = np.iinfo(np.int64).min + 1
  822. elif self._name == "max":
  823. val = np.iinfo(np.int64).max
  824. else:
  825. assert self._name == "resolution"
  826. val = 1
  827. if obj is None:
  828. # i.e. this is on the class, default to nanos
  829. return Timedelta(val)
  830. else:
  831. return Timedelta._from_value_and_reso(val, obj._creso)
  832. def __set__(self, obj, value):
  833. raise AttributeError(f"{self._name} is not settable.")
  834. # Similar to Timestamp/datetime, this is a construction requirement for
  835. # timedeltas that we need to do object instantiation in python. This will
  836. # serve as a C extension type that shadows the Python class, where we do any
  837. # heavy lifting.
  838. cdef class _Timedelta(timedelta):
  839. # cdef readonly:
  840. # int64_t value # nanoseconds
  841. # bint _is_populated # are my components populated
  842. # int64_t _d, _h, _m, _s, _ms, _us, _ns
  843. # NPY_DATETIMEUNIT _reso
  844. # higher than np.ndarray and np.matrix
  845. __array_priority__ = 100
  846. min = MinMaxReso("min")
  847. max = MinMaxReso("max")
  848. resolution = MinMaxReso("resolution")
  849. @property
  850. def value(self):
  851. try:
  852. return convert_reso(self._value, self._creso, NPY_FR_ns, False)
  853. except OverflowError:
  854. raise OverflowError(
  855. "Cannot convert Timedelta to nanoseconds without overflow. "
  856. "Use `.asm8.view('i8')` to cast represent Timedelta in its own "
  857. f"unit (here, {self.unit})."
  858. )
  859. @property
  860. def _unit(self) -> str:
  861. """
  862. The abbreviation associated with self._creso.
  863. """
  864. return npy_unit_to_abbrev(self._creso)
  865. @property
  866. def days(self) -> int: # TODO(cython3): make cdef property
  867. """
  868. Returns the days of the timedelta.
  869. Returns
  870. -------
  871. int
  872. Examples
  873. --------
  874. >>> td = pd.Timedelta(1, "d")
  875. >>> td.days
  876. 1
  877. >>> td = pd.Timedelta('4 min 3 us 42 ns')
  878. >>> td.days
  879. 0
  880. """
  881. # NB: using the python C-API PyDateTime_DELTA_GET_DAYS will fail
  882. # (or be incorrect)
  883. self._ensure_components()
  884. return self._d
  885. @property
  886. def seconds(self) -> int: # TODO(cython3): make cdef property
  887. """
  888. Return the total hours, minutes, and seconds of the timedelta as seconds.
  889. Timedelta.seconds = hours * 3600 + minutes * 60 + seconds.
  890. Returns
  891. -------
  892. int
  893. Number of seconds.
  894. See Also
  895. --------
  896. Timedelta.components : Return all attributes with assigned values
  897. (i.e. days, hours, minutes, seconds, milliseconds, microseconds,
  898. nanoseconds).
  899. Examples
  900. --------
  901. **Using string input**
  902. >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns')
  903. >>> td.seconds
  904. 120
  905. **Using integer input**
  906. >>> td = pd.Timedelta(42, unit='s')
  907. >>> td.seconds
  908. 42
  909. """
  910. # NB: using the python C-API PyDateTime_DELTA_GET_SECONDS will fail
  911. # (or be incorrect)
  912. self._ensure_components()
  913. return self._h * 3600 + self._m * 60 + self._s
  914. @property
  915. def microseconds(self) -> int: # TODO(cython3): make cdef property
  916. # NB: using the python C-API PyDateTime_DELTA_GET_MICROSECONDS will fail
  917. # (or be incorrect)
  918. self._ensure_components()
  919. return self._ms * 1000 + self._us
  920. def total_seconds(self) -> float:
  921. """Total seconds in the duration."""
  922. # We need to override bc we overrided days/seconds/microseconds
  923. # TODO: add nanos/1e9?
  924. return self.days * 24 * 3600 + self.seconds + self.microseconds / 1_000_000
  925. @property
  926. def unit(self) -> str:
  927. return npy_unit_to_abbrev(self._creso)
  928. def __hash__(_Timedelta self):
  929. if self._has_ns():
  930. # Note: this does *not* satisfy the invariance
  931. # td1 == td2 \\Rightarrow hash(td1) == hash(td2)
  932. # if td1 and td2 have different _resos. timedelta64 also has this
  933. # non-invariant behavior.
  934. # see GH#44504
  935. return hash(self._value)
  936. elif self._is_in_pytimedelta_bounds() and (
  937. self._creso == NPY_FR_ns or self._creso == NPY_DATETIMEUNIT.NPY_FR_us
  938. ):
  939. # If we can defer to timedelta.__hash__, do so, as that
  940. # ensures the hash is invariant to our _reso.
  941. # We can only defer for ns and us, as for these two resos we
  942. # call _Timedelta.__new__ with the correct input in
  943. # _timedelta_from_value_and_reso; so timedelta.__hash__
  944. # will be correct
  945. return timedelta.__hash__(self)
  946. else:
  947. # We want to ensure that two equivalent Timedelta objects
  948. # have the same hash. So we try downcasting to the next-lowest
  949. # resolution.
  950. try:
  951. obj = (<_Timedelta>self)._as_creso(<NPY_DATETIMEUNIT>(self._creso + 1))
  952. except OverflowError:
  953. # Doesn't fit, so we're off the hook
  954. return hash(self._value)
  955. else:
  956. return hash(obj)
  957. def __richcmp__(_Timedelta self, object other, int op):
  958. cdef:
  959. _Timedelta ots
  960. if isinstance(other, _Timedelta):
  961. ots = other
  962. elif is_any_td_scalar(other):
  963. try:
  964. ots = Timedelta(other)
  965. except OutOfBoundsTimedelta as err:
  966. # GH#49021 pytimedelta.max overflows
  967. if not PyDelta_Check(other):
  968. # TODO: handle this case
  969. raise
  970. ltup = (self.days, self.seconds, self.microseconds, self.nanoseconds)
  971. rtup = (other.days, other.seconds, other.microseconds, 0)
  972. if op == Py_EQ:
  973. return ltup == rtup
  974. elif op == Py_NE:
  975. return ltup != rtup
  976. elif op == Py_LT:
  977. return ltup < rtup
  978. elif op == Py_LE:
  979. return ltup <= rtup
  980. elif op == Py_GT:
  981. return ltup > rtup
  982. elif op == Py_GE:
  983. return ltup >= rtup
  984. elif other is NaT:
  985. return op == Py_NE
  986. elif util.is_array(other):
  987. if other.dtype.kind == "m":
  988. return PyObject_RichCompare(self.asm8, other, op)
  989. elif other.dtype.kind == "O":
  990. # operate element-wise
  991. return np.array(
  992. [PyObject_RichCompare(self, x, op) for x in other],
  993. dtype=bool,
  994. )
  995. if op == Py_EQ:
  996. return np.zeros(other.shape, dtype=bool)
  997. elif op == Py_NE:
  998. return np.ones(other.shape, dtype=bool)
  999. return NotImplemented # let other raise TypeError
  1000. else:
  1001. return NotImplemented
  1002. if self._creso == ots._creso:
  1003. return cmp_scalar(self._value, ots._value, op)
  1004. return self._compare_mismatched_resos(ots, op)
  1005. # TODO: re-use/share with Timestamp
  1006. cdef bint _compare_mismatched_resos(self, _Timedelta other, op):
  1007. # Can't just dispatch to numpy as they silently overflow and get it wrong
  1008. cdef:
  1009. npy_datetimestruct dts_self
  1010. npy_datetimestruct dts_other
  1011. # dispatch to the datetimestruct utils instead of writing new ones!
  1012. pandas_datetime_to_datetimestruct(self._value, self._creso, &dts_self)
  1013. pandas_datetime_to_datetimestruct(other._value, other._creso, &dts_other)
  1014. return cmp_dtstructs(&dts_self, &dts_other, op)
  1015. cdef bint _has_ns(self):
  1016. if self._creso == NPY_FR_ns:
  1017. return self._value % 1000 != 0
  1018. elif self._creso < NPY_FR_ns:
  1019. # i.e. seconds, millisecond, microsecond
  1020. return False
  1021. else:
  1022. raise NotImplementedError(self._creso)
  1023. cdef bint _is_in_pytimedelta_bounds(self):
  1024. """
  1025. Check if we are within the bounds of datetime.timedelta.
  1026. """
  1027. self._ensure_components()
  1028. return -999999999 <= self._d and self._d <= 999999999
  1029. cdef _ensure_components(_Timedelta self):
  1030. """
  1031. compute the components
  1032. """
  1033. if self._is_populated:
  1034. return
  1035. cdef:
  1036. pandas_timedeltastruct tds
  1037. pandas_timedelta_to_timedeltastruct(self._value, self._creso, &tds)
  1038. self._d = tds.days
  1039. self._h = tds.hrs
  1040. self._m = tds.min
  1041. self._s = tds.sec
  1042. self._ms = tds.ms
  1043. self._us = tds.us
  1044. self._ns = tds.ns
  1045. self._seconds = tds.seconds
  1046. self._microseconds = tds.microseconds
  1047. self._is_populated = 1
  1048. cpdef timedelta to_pytimedelta(_Timedelta self):
  1049. """
  1050. Convert a pandas Timedelta object into a python ``datetime.timedelta`` object.
  1051. Timedelta objects are internally saved as numpy datetime64[ns] dtype.
  1052. Use to_pytimedelta() to convert to object dtype.
  1053. Returns
  1054. -------
  1055. datetime.timedelta or numpy.array of datetime.timedelta
  1056. See Also
  1057. --------
  1058. to_timedelta : Convert argument to Timedelta type.
  1059. Notes
  1060. -----
  1061. Any nanosecond resolution will be lost.
  1062. """
  1063. if self._creso == NPY_FR_ns:
  1064. return timedelta(microseconds=int(self._value) / 1000)
  1065. # TODO(@WillAyd): is this the right way to use components?
  1066. self._ensure_components()
  1067. return timedelta(
  1068. days=self._d, seconds=self._seconds, microseconds=self._microseconds
  1069. )
  1070. def to_timedelta64(self) -> np.timedelta64:
  1071. """
  1072. Return a numpy.timedelta64 object with 'ns' precision.
  1073. """
  1074. cdef:
  1075. str abbrev = npy_unit_to_abbrev(self._creso)
  1076. # TODO: way to create a np.timedelta64 obj with the reso directly
  1077. # instead of having to get the abbrev?
  1078. return np.timedelta64(self._value, abbrev)
  1079. def to_numpy(self, dtype=None, copy=False) -> np.timedelta64:
  1080. """
  1081. Convert the Timedelta to a NumPy timedelta64.
  1082. This is an alias method for `Timedelta.to_timedelta64()`. The dtype and
  1083. copy parameters are available here only for compatibility. Their values
  1084. will not affect the return value.
  1085. Returns
  1086. -------
  1087. numpy.timedelta64
  1088. See Also
  1089. --------
  1090. Series.to_numpy : Similar method for Series.
  1091. """
  1092. if dtype is not None or copy is not False:
  1093. raise ValueError(
  1094. "Timedelta.to_numpy dtype and copy arguments are ignored"
  1095. )
  1096. return self.to_timedelta64()
  1097. def view(self, dtype):
  1098. """
  1099. Array view compatibility.
  1100. Parameters
  1101. ----------
  1102. dtype : str or dtype
  1103. The dtype to view the underlying data as.
  1104. """
  1105. return np.timedelta64(self._value).view(dtype)
  1106. @property
  1107. def components(self):
  1108. """
  1109. Return a components namedtuple-like.
  1110. Examples
  1111. --------
  1112. >>> td = pd.Timedelta('2 day 4 min 3 us 42 ns')
  1113. >>> td.components
  1114. Components(days=2, hours=0, minutes=4, seconds=0, milliseconds=0,
  1115. microseconds=3, nanoseconds=42)
  1116. """
  1117. self._ensure_components()
  1118. # return the named tuple
  1119. return Components(self._d, self._h, self._m, self._s,
  1120. self._ms, self._us, self._ns)
  1121. @property
  1122. def asm8(self) -> np.timedelta64:
  1123. """
  1124. Return a numpy timedelta64 array scalar view.
  1125. Provides access to the array scalar view (i.e. a combination of the
  1126. value and the units) associated with the numpy.timedelta64().view(),
  1127. including a 64-bit integer representation of the timedelta in
  1128. nanoseconds (Python int compatible).
  1129. Returns
  1130. -------
  1131. numpy timedelta64 array scalar view
  1132. Array scalar view of the timedelta in nanoseconds.
  1133. Examples
  1134. --------
  1135. >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns')
  1136. >>> td.asm8
  1137. numpy.timedelta64(86520000003042,'ns')
  1138. >>> td = pd.Timedelta('2 min 3 s')
  1139. >>> td.asm8
  1140. numpy.timedelta64(123000000000,'ns')
  1141. >>> td = pd.Timedelta('3 ms 5 us')
  1142. >>> td.asm8
  1143. numpy.timedelta64(3005000,'ns')
  1144. >>> td = pd.Timedelta(42, unit='ns')
  1145. >>> td.asm8
  1146. numpy.timedelta64(42,'ns')
  1147. """
  1148. return self.to_timedelta64()
  1149. @property
  1150. def resolution_string(self) -> str:
  1151. """
  1152. Return a string representing the lowest timedelta resolution.
  1153. Each timedelta has a defined resolution that represents the lowest OR
  1154. most granular level of precision. Each level of resolution is
  1155. represented by a short string as defined below:
  1156. Resolution: Return value
  1157. * Days: 'D'
  1158. * Hours: 'H'
  1159. * Minutes: 'T'
  1160. * Seconds: 'S'
  1161. * Milliseconds: 'L'
  1162. * Microseconds: 'U'
  1163. * Nanoseconds: 'N'
  1164. Returns
  1165. -------
  1166. str
  1167. Timedelta resolution.
  1168. Examples
  1169. --------
  1170. >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns')
  1171. >>> td.resolution_string
  1172. 'N'
  1173. >>> td = pd.Timedelta('1 days 2 min 3 us')
  1174. >>> td.resolution_string
  1175. 'U'
  1176. >>> td = pd.Timedelta('2 min 3 s')
  1177. >>> td.resolution_string
  1178. 'S'
  1179. >>> td = pd.Timedelta(36, unit='us')
  1180. >>> td.resolution_string
  1181. 'U'
  1182. """
  1183. self._ensure_components()
  1184. if self._ns:
  1185. return "N"
  1186. elif self._us:
  1187. return "U"
  1188. elif self._ms:
  1189. return "L"
  1190. elif self._s:
  1191. return "S"
  1192. elif self._m:
  1193. return "T"
  1194. elif self._h:
  1195. return "H"
  1196. else:
  1197. return "D"
  1198. @property
  1199. def nanoseconds(self):
  1200. """
  1201. Return the number of nanoseconds (n), where 0 <= n < 1 microsecond.
  1202. Returns
  1203. -------
  1204. int
  1205. Number of nanoseconds.
  1206. See Also
  1207. --------
  1208. Timedelta.components : Return all attributes with assigned values
  1209. (i.e. days, hours, minutes, seconds, milliseconds, microseconds,
  1210. nanoseconds).
  1211. Examples
  1212. --------
  1213. **Using string input**
  1214. >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns')
  1215. >>> td.nanoseconds
  1216. 42
  1217. **Using integer input**
  1218. >>> td = pd.Timedelta(42, unit='ns')
  1219. >>> td.nanoseconds
  1220. 42
  1221. """
  1222. self._ensure_components()
  1223. return self._ns
  1224. def _repr_base(self, format=None) -> str:
  1225. """
  1226. Parameters
  1227. ----------
  1228. format : None|all|sub_day|long
  1229. Returns
  1230. -------
  1231. converted : string of a Timedelta
  1232. """
  1233. cdef:
  1234. str sign, fmt
  1235. dict comp_dict
  1236. object subs
  1237. self._ensure_components()
  1238. if self._d < 0:
  1239. sign = " +"
  1240. else:
  1241. sign = " "
  1242. if format == "all":
  1243. fmt = ("{days} days{sign}{hours:02}:{minutes:02}:{seconds:02}."
  1244. "{milliseconds:03}{microseconds:03}{nanoseconds:03}")
  1245. else:
  1246. # if we have a partial day
  1247. subs = (self._h or self._m or self._s or
  1248. self._ms or self._us or self._ns)
  1249. if self._ms or self._us or self._ns:
  1250. seconds_fmt = "{seconds:02}.{milliseconds:03}{microseconds:03}"
  1251. if self._ns:
  1252. # GH#9309
  1253. seconds_fmt += "{nanoseconds:03}"
  1254. else:
  1255. seconds_fmt = "{seconds:02}"
  1256. if format == "sub_day" and not self._d:
  1257. fmt = "{hours:02}:{minutes:02}:" + seconds_fmt
  1258. elif subs or format == "long":
  1259. fmt = "{days} days{sign}{hours:02}:{minutes:02}:" + seconds_fmt
  1260. else:
  1261. fmt = "{days} days"
  1262. comp_dict = self.components._asdict()
  1263. comp_dict["sign"] = sign
  1264. return fmt.format(**comp_dict)
  1265. def __repr__(self) -> str:
  1266. repr_based = self._repr_base(format="long")
  1267. return f"Timedelta('{repr_based}')"
  1268. def __str__(self) -> str:
  1269. return self._repr_base(format="long")
  1270. def __bool__(self) -> bool:
  1271. return self._value!= 0
  1272. def isoformat(self) -> str:
  1273. """
  1274. Format the Timedelta as ISO 8601 Duration.
  1275. ``P[n]Y[n]M[n]DT[n]H[n]M[n]S``, where the ``[n]`` s are replaced by the
  1276. values. See https://en.wikipedia.org/wiki/ISO_8601#Durations.
  1277. Returns
  1278. -------
  1279. str
  1280. See Also
  1281. --------
  1282. Timestamp.isoformat : Function is used to convert the given
  1283. Timestamp object into the ISO format.
  1284. Notes
  1285. -----
  1286. The longest component is days, whose value may be larger than
  1287. 365.
  1288. Every component is always included, even if its value is 0.
  1289. Pandas uses nanosecond precision, so up to 9 decimal places may
  1290. be included in the seconds component.
  1291. Trailing 0's are removed from the seconds component after the decimal.
  1292. We do not 0 pad components, so it's `...T5H...`, not `...T05H...`
  1293. Examples
  1294. --------
  1295. >>> td = pd.Timedelta(days=6, minutes=50, seconds=3,
  1296. ... milliseconds=10, microseconds=10, nanoseconds=12)
  1297. >>> td.isoformat()
  1298. 'P6DT0H50M3.010010012S'
  1299. >>> pd.Timedelta(hours=1, seconds=10).isoformat()
  1300. 'P0DT1H0M10S'
  1301. >>> pd.Timedelta(days=500.5).isoformat()
  1302. 'P500DT12H0M0S'
  1303. """
  1304. components = self.components
  1305. seconds = (f"{components.seconds}."
  1306. f"{components.milliseconds:0>3}"
  1307. f"{components.microseconds:0>3}"
  1308. f"{components.nanoseconds:0>3}")
  1309. # Trim unnecessary 0s, 1.000000000 -> 1
  1310. seconds = seconds.rstrip("0").rstrip(".")
  1311. tpl = (f"P{components.days}DT{components.hours}"
  1312. f"H{components.minutes}M{seconds}S")
  1313. return tpl
  1314. # ----------------------------------------------------------------
  1315. # Constructors
  1316. @classmethod
  1317. def _from_value_and_reso(cls, int64_t value, NPY_DATETIMEUNIT reso):
  1318. # exposing as classmethod for testing
  1319. return _timedelta_from_value_and_reso(cls, value, reso)
  1320. def as_unit(self, str unit, bint round_ok=True):
  1321. """
  1322. Convert the underlying int64 representaton to the given unit.
  1323. Parameters
  1324. ----------
  1325. unit : {"ns", "us", "ms", "s"}
  1326. round_ok : bool, default True
  1327. If False and the conversion requires rounding, raise.
  1328. Returns
  1329. -------
  1330. Timedelta
  1331. """
  1332. dtype = np.dtype(f"m8[{unit}]")
  1333. reso = get_unit_from_dtype(dtype)
  1334. return self._as_creso(reso, round_ok=round_ok)
  1335. @cython.cdivision(False)
  1336. cdef _Timedelta _as_creso(self, NPY_DATETIMEUNIT reso, bint round_ok=True):
  1337. cdef:
  1338. int64_t value
  1339. if reso == self._creso:
  1340. return self
  1341. try:
  1342. value = convert_reso(self._value, self._creso, reso, round_ok=round_ok)
  1343. except OverflowError as err:
  1344. unit = npy_unit_to_abbrev(reso)
  1345. raise OutOfBoundsTimedelta(
  1346. f"Cannot cast {self} to unit='{unit}' without overflow."
  1347. ) from err
  1348. return type(self)._from_value_and_reso(value, reso=reso)
  1349. cpdef _maybe_cast_to_matching_resos(self, _Timedelta other):
  1350. """
  1351. If _resos do not match, cast to the higher resolution, raising on overflow.
  1352. """
  1353. if self._creso > other._creso:
  1354. other = other._as_creso(self._creso)
  1355. elif self._creso < other._creso:
  1356. self = self._as_creso(other._creso)
  1357. return self, other
  1358. # Python front end to C extension type _Timedelta
  1359. # This serves as the box for timedelta64
  1360. class Timedelta(_Timedelta):
  1361. """
  1362. Represents a duration, the difference between two dates or times.
  1363. Timedelta is the pandas equivalent of python's ``datetime.timedelta``
  1364. and is interchangeable with it in most cases.
  1365. Parameters
  1366. ----------
  1367. value : Timedelta, timedelta, np.timedelta64, str, or int
  1368. unit : str, default 'ns'
  1369. Denote the unit of the input, if input is an integer.
  1370. Possible values:
  1371. * 'W', 'D', 'T', 'S', 'L', 'U', or 'N'
  1372. * 'days' or 'day'
  1373. * 'hours', 'hour', 'hr', or 'h'
  1374. * 'minutes', 'minute', 'min', or 'm'
  1375. * 'seconds', 'second', or 'sec'
  1376. * 'milliseconds', 'millisecond', 'millis', or 'milli'
  1377. * 'microseconds', 'microsecond', 'micros', or 'micro'
  1378. * 'nanoseconds', 'nanosecond', 'nanos', 'nano', or 'ns'.
  1379. **kwargs
  1380. Available kwargs: {days, seconds, microseconds,
  1381. milliseconds, minutes, hours, weeks}.
  1382. Values for construction in compat with datetime.timedelta.
  1383. Numpy ints and floats will be coerced to python ints and floats.
  1384. Notes
  1385. -----
  1386. The constructor may take in either both values of value and unit or
  1387. kwargs as above. Either one of them must be used during initialization
  1388. The ``.value`` attribute is always in ns.
  1389. If the precision is higher than nanoseconds, the precision of the duration is
  1390. truncated to nanoseconds.
  1391. Examples
  1392. --------
  1393. Here we initialize Timedelta object with both value and unit
  1394. >>> td = pd.Timedelta(1, "d")
  1395. >>> td
  1396. Timedelta('1 days 00:00:00')
  1397. Here we initialize the Timedelta object with kwargs
  1398. >>> td2 = pd.Timedelta(days=1)
  1399. >>> td2
  1400. Timedelta('1 days 00:00:00')
  1401. We see that either way we get the same result
  1402. """
  1403. _req_any_kwargs_new = {"weeks", "days", "hours", "minutes", "seconds",
  1404. "milliseconds", "microseconds", "nanoseconds"}
  1405. def __new__(cls, object value=_no_input, unit=None, **kwargs):
  1406. if value is _no_input:
  1407. if not len(kwargs):
  1408. raise ValueError("cannot construct a Timedelta without a "
  1409. "value/unit or descriptive keywords "
  1410. "(days,seconds....)")
  1411. kwargs = {key: _to_py_int_float(kwargs[key]) for key in kwargs}
  1412. unsupported_kwargs = set(kwargs)
  1413. unsupported_kwargs.difference_update(cls._req_any_kwargs_new)
  1414. if unsupported_kwargs or not cls._req_any_kwargs_new.intersection(kwargs):
  1415. raise ValueError(
  1416. "cannot construct a Timedelta from the passed arguments, "
  1417. "allowed keywords are "
  1418. "[weeks, days, hours, minutes, seconds, "
  1419. "milliseconds, microseconds, nanoseconds]"
  1420. )
  1421. # GH43764, convert any input to nanoseconds first and then
  1422. # create the timestamp. This ensures that any potential
  1423. # nanosecond contributions from kwargs parsed as floats
  1424. # are taken into consideration.
  1425. seconds = int((
  1426. (
  1427. (kwargs.get("days", 0) + kwargs.get("weeks", 0) * 7) * 24
  1428. + kwargs.get("hours", 0)
  1429. ) * 3600
  1430. + kwargs.get("minutes", 0) * 60
  1431. + kwargs.get("seconds", 0)
  1432. ) * 1_000_000_000
  1433. )
  1434. value = np.timedelta64(
  1435. int(kwargs.get("nanoseconds", 0))
  1436. + int(kwargs.get("microseconds", 0) * 1_000)
  1437. + int(kwargs.get("milliseconds", 0) * 1_000_000)
  1438. + seconds
  1439. )
  1440. if unit in {"Y", "y", "M"}:
  1441. raise ValueError(
  1442. "Units 'M', 'Y', and 'y' are no longer supported, as they do not "
  1443. "represent unambiguous timedelta values durations."
  1444. )
  1445. # GH 30543 if pd.Timedelta already passed, return it
  1446. # check that only value is passed
  1447. if isinstance(value, _Timedelta):
  1448. # 'unit' is benign in this case, but e.g. days or seconds
  1449. # doesn't make sense here.
  1450. if len(kwargs):
  1451. # GH#48898
  1452. raise ValueError(
  1453. "Cannot pass both a Timedelta input and timedelta keyword "
  1454. "arguments, got "
  1455. f"{list(kwargs.keys())}"
  1456. )
  1457. return value
  1458. elif isinstance(value, str):
  1459. if unit is not None:
  1460. raise ValueError("unit must not be specified if the value is a str")
  1461. if (len(value) > 0 and value[0] == "P") or (
  1462. len(value) > 1 and value[:2] == "-P"
  1463. ):
  1464. value = parse_iso_format_string(value)
  1465. else:
  1466. value = parse_timedelta_string(value)
  1467. value = np.timedelta64(value)
  1468. elif PyDelta_Check(value):
  1469. # pytimedelta object -> microsecond resolution
  1470. new_value = delta_to_nanoseconds(
  1471. value, reso=NPY_DATETIMEUNIT.NPY_FR_us
  1472. )
  1473. return cls._from_value_and_reso(
  1474. new_value, reso=NPY_DATETIMEUNIT.NPY_FR_us
  1475. )
  1476. elif is_timedelta64_object(value):
  1477. # Retain the resolution if possible, otherwise cast to the nearest
  1478. # supported resolution.
  1479. new_value = get_timedelta64_value(value)
  1480. if new_value == NPY_NAT:
  1481. # i.e. np.timedelta64("NaT")
  1482. return NaT
  1483. reso = get_datetime64_unit(value)
  1484. new_reso = get_supported_reso(reso)
  1485. if reso != NPY_DATETIMEUNIT.NPY_FR_GENERIC:
  1486. try:
  1487. new_value = convert_reso(
  1488. new_value,
  1489. reso,
  1490. new_reso,
  1491. round_ok=True,
  1492. )
  1493. except (OverflowError, OutOfBoundsDatetime) as err:
  1494. raise OutOfBoundsTimedelta(value) from err
  1495. return cls._from_value_and_reso(new_value, reso=new_reso)
  1496. elif is_tick_object(value):
  1497. new_reso = get_supported_reso(value._creso)
  1498. new_value = delta_to_nanoseconds(value, reso=new_reso)
  1499. return cls._from_value_and_reso(new_value, reso=new_reso)
  1500. elif is_integer_object(value) or is_float_object(value):
  1501. # unit=None is de-facto 'ns'
  1502. unit = parse_timedelta_unit(unit)
  1503. value = convert_to_timedelta64(value, unit)
  1504. elif checknull_with_nat(value):
  1505. return NaT
  1506. else:
  1507. raise ValueError(
  1508. "Value must be Timedelta, string, integer, "
  1509. f"float, timedelta or convertible, not {type(value).__name__}"
  1510. )
  1511. if is_timedelta64_object(value):
  1512. value = value.view("i8")
  1513. # nat
  1514. if value == NPY_NAT:
  1515. return NaT
  1516. return _timedelta_from_value_and_reso(cls, value, NPY_FR_ns)
  1517. def __setstate__(self, state):
  1518. if len(state) == 1:
  1519. # older pickle, only supported nanosecond
  1520. value = state[0]
  1521. reso = NPY_FR_ns
  1522. else:
  1523. value, reso = state
  1524. self._value= value
  1525. self._creso = reso
  1526. def __reduce__(self):
  1527. object_state = self._value, self._creso
  1528. return (_timedelta_unpickle, object_state)
  1529. @cython.cdivision(True)
  1530. def _round(self, freq, mode):
  1531. cdef:
  1532. int64_t result, unit
  1533. ndarray[int64_t] arr
  1534. from pandas._libs.tslibs.offsets import to_offset
  1535. to_offset(freq).nanos # raises on non-fixed freq
  1536. unit = delta_to_nanoseconds(to_offset(freq), self._creso)
  1537. arr = np.array([self._value], dtype="i8")
  1538. result = round_nsint64(arr, mode, unit)[0]
  1539. return Timedelta._from_value_and_reso(result, self._creso)
  1540. def round(self, freq):
  1541. """
  1542. Round the Timedelta to the specified resolution.
  1543. Parameters
  1544. ----------
  1545. freq : str
  1546. Frequency string indicating the rounding resolution.
  1547. Returns
  1548. -------
  1549. a new Timedelta rounded to the given resolution of `freq`
  1550. Raises
  1551. ------
  1552. ValueError if the freq cannot be converted
  1553. """
  1554. return self._round(freq, RoundTo.NEAREST_HALF_EVEN)
  1555. def floor(self, freq):
  1556. """
  1557. Return a new Timedelta floored to this resolution.
  1558. Parameters
  1559. ----------
  1560. freq : str
  1561. Frequency string indicating the flooring resolution.
  1562. """
  1563. return self._round(freq, RoundTo.MINUS_INFTY)
  1564. def ceil(self, freq):
  1565. """
  1566. Return a new Timedelta ceiled to this resolution.
  1567. Parameters
  1568. ----------
  1569. freq : str
  1570. Frequency string indicating the ceiling resolution.
  1571. """
  1572. return self._round(freq, RoundTo.PLUS_INFTY)
  1573. # ----------------------------------------------------------------
  1574. # Arithmetic Methods
  1575. # TODO: Can some of these be defined in the cython class?
  1576. __neg__ = _op_unary_method(lambda x: -x, "__neg__")
  1577. __pos__ = _op_unary_method(lambda x: x, "__pos__")
  1578. __abs__ = _op_unary_method(lambda x: abs(x), "__abs__")
  1579. __add__ = _binary_op_method_timedeltalike(lambda x, y: x + y, "__add__")
  1580. __radd__ = _binary_op_method_timedeltalike(lambda x, y: x + y, "__radd__")
  1581. __sub__ = _binary_op_method_timedeltalike(lambda x, y: x - y, "__sub__")
  1582. __rsub__ = _binary_op_method_timedeltalike(lambda x, y: y - x, "__rsub__")
  1583. def __mul__(self, other):
  1584. if is_integer_object(other) or is_float_object(other):
  1585. if util.is_nan(other):
  1586. # np.nan * timedelta -> np.timedelta64("NaT"), in this case NaT
  1587. return NaT
  1588. return _timedelta_from_value_and_reso(
  1589. Timedelta,
  1590. <int64_t>(other * self._value),
  1591. reso=self._creso,
  1592. )
  1593. elif is_array(other):
  1594. if other.ndim == 0:
  1595. # see also: item_from_zerodim
  1596. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  1597. return self.__mul__(item)
  1598. return other * self.to_timedelta64()
  1599. return NotImplemented
  1600. __rmul__ = __mul__
  1601. def __truediv__(self, other):
  1602. if _should_cast_to_timedelta(other):
  1603. # We interpret NaT as timedelta64("NaT")
  1604. other = Timedelta(other)
  1605. if other is NaT:
  1606. return np.nan
  1607. if other._creso != self._creso:
  1608. self, other = self._maybe_cast_to_matching_resos(other)
  1609. return self._value/ float(other._value)
  1610. elif is_integer_object(other) or is_float_object(other):
  1611. # integers or floats
  1612. if util.is_nan(other):
  1613. return NaT
  1614. return Timedelta._from_value_and_reso(
  1615. <int64_t>(self._value/ other), self._creso
  1616. )
  1617. elif is_array(other):
  1618. if other.ndim == 0:
  1619. # see also: item_from_zerodim
  1620. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  1621. return self.__truediv__(item)
  1622. return self.to_timedelta64() / other
  1623. return NotImplemented
  1624. def __rtruediv__(self, other):
  1625. if _should_cast_to_timedelta(other):
  1626. # We interpret NaT as timedelta64("NaT")
  1627. other = Timedelta(other)
  1628. if other is NaT:
  1629. return np.nan
  1630. if self._creso != other._creso:
  1631. self, other = self._maybe_cast_to_matching_resos(other)
  1632. return float(other._value) / self._value
  1633. elif is_array(other):
  1634. if other.ndim == 0:
  1635. # see also: item_from_zerodim
  1636. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  1637. return self.__rtruediv__(item)
  1638. elif other.dtype.kind == "O":
  1639. # GH#31869
  1640. return np.array([x / self for x in other])
  1641. # TODO: if other.dtype.kind == "m" and other.dtype != self.asm8.dtype
  1642. # then should disallow for consistency with scalar behavior; requires
  1643. # deprecation cycle. (or changing scalar behavior)
  1644. return other / self.to_timedelta64()
  1645. return NotImplemented
  1646. def __floordiv__(self, other):
  1647. # numpy does not implement floordiv for timedelta64 dtype, so we cannot
  1648. # just defer
  1649. if _should_cast_to_timedelta(other):
  1650. # We interpret NaT as timedelta64("NaT")
  1651. other = Timedelta(other)
  1652. if other is NaT:
  1653. return np.nan
  1654. if self._creso != other._creso:
  1655. self, other = self._maybe_cast_to_matching_resos(other)
  1656. return self._value// other._value
  1657. elif is_integer_object(other) or is_float_object(other):
  1658. if util.is_nan(other):
  1659. return NaT
  1660. return type(self)._from_value_and_reso(self._value// other, self._creso)
  1661. elif is_array(other):
  1662. if other.ndim == 0:
  1663. # see also: item_from_zerodim
  1664. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  1665. return self.__floordiv__(item)
  1666. if other.dtype.kind == "m":
  1667. # also timedelta-like
  1668. with warnings.catch_warnings():
  1669. warnings.filterwarnings(
  1670. "ignore",
  1671. "invalid value encountered in floor_divide",
  1672. RuntimeWarning
  1673. )
  1674. result = self.asm8 // other
  1675. mask = other.view("i8") == NPY_NAT
  1676. if mask.any():
  1677. # We differ from numpy here
  1678. result = result.astype("f8")
  1679. result[mask] = np.nan
  1680. return result
  1681. elif other.dtype.kind in ["i", "u", "f"]:
  1682. if other.ndim == 0:
  1683. return self // other.item()
  1684. else:
  1685. return self.to_timedelta64() // other
  1686. raise TypeError(f"Invalid dtype {other.dtype} for __floordiv__")
  1687. return NotImplemented
  1688. def __rfloordiv__(self, other):
  1689. # numpy does not implement floordiv for timedelta64 dtype, so we cannot
  1690. # just defer
  1691. if _should_cast_to_timedelta(other):
  1692. # We interpret NaT as timedelta64("NaT")
  1693. other = Timedelta(other)
  1694. if other is NaT:
  1695. return np.nan
  1696. if self._creso != other._creso:
  1697. self, other = self._maybe_cast_to_matching_resos(other)
  1698. return other._value// self._value
  1699. elif is_array(other):
  1700. if other.ndim == 0:
  1701. # see also: item_from_zerodim
  1702. item = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other), other)
  1703. return self.__rfloordiv__(item)
  1704. if other.dtype.kind == "m":
  1705. # also timedelta-like
  1706. with warnings.catch_warnings():
  1707. warnings.filterwarnings(
  1708. "ignore",
  1709. "invalid value encountered in floor_divide",
  1710. RuntimeWarning
  1711. )
  1712. result = other // self.asm8
  1713. mask = other.view("i8") == NPY_NAT
  1714. if mask.any():
  1715. # We differ from numpy here
  1716. result = result.astype("f8")
  1717. result[mask] = np.nan
  1718. return result
  1719. # Includes integer array // Timedelta, disallowed in GH#19761
  1720. raise TypeError(f"Invalid dtype {other.dtype} for __floordiv__")
  1721. return NotImplemented
  1722. def __mod__(self, other):
  1723. # Naive implementation, room for optimization
  1724. return self.__divmod__(other)[1]
  1725. def __rmod__(self, other):
  1726. # Naive implementation, room for optimization
  1727. return self.__rdivmod__(other)[1]
  1728. def __divmod__(self, other):
  1729. # Naive implementation, room for optimization
  1730. div = self // other
  1731. return div, self - div * other
  1732. def __rdivmod__(self, other):
  1733. # Naive implementation, room for optimization
  1734. div = other // self
  1735. return div, other - div * self
  1736. def truediv_object_array(ndarray left, ndarray right):
  1737. cdef:
  1738. ndarray[object] result = np.empty((<object>left).shape, dtype=object)
  1739. object td64 # really timedelta64 if we find a way to declare that
  1740. object obj, res_value
  1741. _Timedelta td
  1742. Py_ssize_t i
  1743. for i in range(len(left)):
  1744. td64 = left[i]
  1745. obj = right[i]
  1746. if get_timedelta64_value(td64) == NPY_NAT:
  1747. # td here should be interpreted as a td64 NaT
  1748. if _should_cast_to_timedelta(obj):
  1749. res_value = np.nan
  1750. else:
  1751. # if its a number then let numpy handle division, otherwise
  1752. # numpy will raise
  1753. res_value = td64 / obj
  1754. else:
  1755. td = Timedelta(td64)
  1756. res_value = td / obj
  1757. result[i] = res_value
  1758. return result
  1759. def floordiv_object_array(ndarray left, ndarray right):
  1760. cdef:
  1761. ndarray[object] result = np.empty((<object>left).shape, dtype=object)
  1762. object td64 # really timedelta64 if we find a way to declare that
  1763. object obj, res_value
  1764. _Timedelta td
  1765. Py_ssize_t i
  1766. for i in range(len(left)):
  1767. td64 = left[i]
  1768. obj = right[i]
  1769. if get_timedelta64_value(td64) == NPY_NAT:
  1770. # td here should be interpreted as a td64 NaT
  1771. if _should_cast_to_timedelta(obj):
  1772. res_value = np.nan
  1773. else:
  1774. # if its a number then let numpy handle division, otherwise
  1775. # numpy will raise
  1776. res_value = td64 // obj
  1777. else:
  1778. td = Timedelta(td64)
  1779. res_value = td // obj
  1780. result[i] = res_value
  1781. return result
  1782. cdef bint is_any_td_scalar(object obj):
  1783. """
  1784. Cython equivalent for `isinstance(obj, (timedelta, np.timedelta64, Tick))`
  1785. Parameters
  1786. ----------
  1787. obj : object
  1788. Returns
  1789. -------
  1790. bool
  1791. """
  1792. return (
  1793. PyDelta_Check(obj) or is_timedelta64_object(obj) or is_tick_object(obj)
  1794. )
  1795. cdef bint _should_cast_to_timedelta(object obj):
  1796. """
  1797. Should we treat this object as a Timedelta for the purpose of a binary op
  1798. """
  1799. return (
  1800. is_any_td_scalar(obj) or obj is None or obj is NaT or isinstance(obj, str)
  1801. )