@@ -950,14 +950,18 @@ cdef _timedelta_from_value_and_reso(int64_t value, NPY_DATETIMEUNIT reso):
950950 cdef:
951951 _Timedelta td_base
952952
953+ # For millisecond and second resos, we cannot actually pass int(value) because
954+ # many cases would fall outside of the pytimedelta implementation bounds.
955+ # We pass 0 instead, and override seconds, microseconds, days.
956+ # In principle we could pass 0 for ns and us too.
953957 if reso == NPY_FR_ns:
954958 td_base = _Timedelta.__new__ (Timedelta, microseconds = int (value) // 1000 )
955959 elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
956960 td_base = _Timedelta.__new__ (Timedelta, microseconds = int (value))
957961 elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
958- td_base = _Timedelta.__new__ (Timedelta, milliseconds = int (value) )
962+ td_base = _Timedelta.__new__ (Timedelta, milliseconds = 0 )
959963 elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
960- td_base = _Timedelta.__new__ (Timedelta, seconds = int (value) )
964+ td_base = _Timedelta.__new__ (Timedelta, seconds = 0 )
961965 # Other resolutions are disabled but could potentially be implemented here:
962966 # elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
963967 # td_base = _Timedelta.__new__(Timedelta, minutes=int(value))
@@ -977,6 +981,34 @@ cdef _timedelta_from_value_and_reso(int64_t value, NPY_DATETIMEUNIT reso):
977981 return td_base
978982
979983
984+ class MinMaxReso :
985+ """
986+ We need to define min/max/resolution on both the Timedelta _instance_
987+ and Timedelta class. On an instance, these depend on the object's _reso.
988+ On the class, we default to the values we would get with nanosecond _reso.
989+ """
990+ def __init__ (self , name ):
991+ self ._name = name
992+
993+ def __get__ (self , obj , type = None ):
994+ if self ._name == " min" :
995+ val = np.iinfo(np.int64).min + 1
996+ elif self ._name == " max" :
997+ val = np.iinfo(np.int64).max
998+ else :
999+ assert self ._name == " resolution"
1000+ val = 1
1001+
1002+ if obj is None :
1003+ # i.e. this is on the class, default to nanos
1004+ return Timedelta(val)
1005+ else :
1006+ return Timedelta._from_value_and_reso(val, obj._reso)
1007+
1008+ def __set__ (self , obj , value ):
1009+ raise AttributeError (f" {self._name} is not settable." )
1010+
1011+
9801012# Similar to Timestamp/datetime, this is a construction requirement for
9811013# timedeltas that we need to do object instantiation in python. This will
9821014# serve as a C extension type that shadows the Python class, where we do any
@@ -990,6 +1022,36 @@ cdef class _Timedelta(timedelta):
9901022
9911023 # higher than np.ndarray and np.matrix
9921024 __array_priority__ = 100
1025+ min = MinMaxReso(" min" )
1026+ max = MinMaxReso(" max" )
1027+ resolution = MinMaxReso(" resolution" )
1028+
1029+ @property
1030+ def days (self ) -> int: # TODO(cython3 ): make cdef property
1031+ # NB: using the python C-API PyDateTime_DELTA_GET_DAYS will fail
1032+ # (or be incorrect)
1033+ self ._ensure_components()
1034+ return self ._d
1035+
1036+ @property
1037+ def seconds (self ) -> int: # TODO(cython3 ): make cdef property
1038+ # NB: using the python C-API PyDateTime_DELTA_GET_SECONDS will fail
1039+ # (or be incorrect)
1040+ self ._ensure_components()
1041+ return self ._h * 3600 + self ._m * 60 + self ._s
1042+
1043+ @property
1044+ def microseconds (self ) -> int: # TODO(cython3 ): make cdef property
1045+ # NB: using the python C-API PyDateTime_DELTA_GET_MICROSECONDS will fail
1046+ # (or be incorrect)
1047+ self ._ensure_components()
1048+ return self ._ms * 1000 + self ._us
1049+
1050+ def total_seconds (self ) -> float:
1051+ """Total seconds in the duration."""
1052+ # We need to override bc we overrided days/seconds/microseconds
1053+ # TODO: add nanos/1e9?
1054+ return self.days * 24 * 3600 + self.seconds + self.microseconds / 1_000_000
9931055
9941056 @property
9951057 def freq(self ) -> None:
@@ -1979,9 +2041,3 @@ cdef _broadcast_floordiv_td64(
19792041 res = res.astype(' f8' )
19802042 res[mask] = np.nan
19812043 return res
1982-
1983-
1984- # resolution in ns
1985- Timedelta.min = Timedelta(np.iinfo(np.int64).min + 1 )
1986- Timedelta.max = Timedelta(np.iinfo(np.int64).max)
1987- Timedelta.resolution = Timedelta(nanoseconds = 1 )
0 commit comments