Repository navigation
Description
Describe the issue:
The logic for timedelta64 comparisons involving a generic timedelta64 is surprising, and leads to non-transitivity of something that I'd expect to be a total ordering. In the snippet below, x < y and y < z are both True, but x < z is False.
>>> import numpy as np
>>> x, y, z = np.timedelta64(1, "ms"), np.timedelta64(2), np.timedelta64(5, "ns")
>>> x < y < z
np.True_
>>> x < z
np.False_The x < z result is what I expect. For x < y and y < z, I was expecting either a consistent default for the units for y (though I'm not sure what default I was expecting - any of days / seconds / nanoseconds would seem plausible), or to get an error or warning about an ill-defined comparison.
It appears that the actual behaviour is that y is interpreted as being in milliseconds for the x < y comparison, and then in nanoseconds for the y < z comparison.
The same lack of transitivity applies to equality comparisons:
>>> x, y, z = np.timedelta64(1, "ms"), np.timedelta64(1), np.timedelta64(1, "ns")
>>> x == y == z
np.True_
>>> x == z
np.False_(Though it looks like we're protected from peculiar dict / set containment consequences by the fact that y isn't hashable.)
Meta: "BUG" may be a stretch here - I don't doubt that the behaviour described is intentional, but it's still surprising, and as far as I can tell undocumented (I couldn't find any hints to this behaviour at https://numpy.org/doc/stable/reference/arrays.datetime.html). In which case this is at best a doc bug.
Reproduce the code example:
import numpy as np
x, y, z = np.timedelta64(1, "ms"), np.timedelta64(2), np.timedelta64(5, "ns")
print("x < y < z:", x < y < z)
print("x < z:", x < z)Error message:
Python and NumPy Versions:
NumPy 2.2.2
Python 3.13.1 (main, Dec 7 2024, 10:29:14) [Clang 16.0.0 (clang-1600.0.26.4)]
Runtime Environment:
No response
Context for the issue:
This doesn't affect my work - we've already worked around this issue in our own code. (I think the lesson learned is "to avoid surprises, always provide a unit when using np.timedelta64".)
Activity
mdickinson commentedon Feb 6, 2025
#14622 seems tangentially related - it looks as though this is where the non-hashability of the generic
timedelta64was introduced.seberg commentedon Feb 7, 2025
The same problem exists with
np.timedelta64(3, "s") == 3. I would agree with changing that behavior, although it has come up before that for this change we may have to go to an error.I think once we decide to do something about that case, it could make sense to work on (slowly or not) effecitvely getting rid of naive timedelta.
(There is a reason that things like
datetime(..., "s") + 1000magically work via naive timedelta. But we do a better job about it now, and over-all I don't think that magic is even relied on in practice.)Maybe the solution is even to go to an error but allow
np.errstate(...)to change it (i.e. to disable the error and havenp.timedelta64(3, "s") == 3returnFalse).MedAmine-SUDO commentedon Feb 10, 2025
Hello, I would like to work on this issue
So, the I guess the issue comes from this function: get_datetime_conversion_factor.
How we would like to proceed ? raising and error and not allow this ? or raising a warning and keep the actual behavior ?
VascoConceicao commentedon Mar 7, 2025
Hello! I'm a first comer to this repository and I was wondering if I could work on this issue? It is my first time ever contributing to an open-source program and I believe it could be a good way to test and improve my programming skills.
rkern commentedon Mar 7, 2025
This is probably not a good first issue.
VascoConceicao commentedon Mar 10, 2025
I would like to try to solve it anyway, if that's okay
seberg commentedon Mar 11, 2025
You can work on whatever you like, but don't be surprised if this is a very deep change (i.e. in code but also in what you need to understand) and also one that requires more discussion than programming to make progress.
VascoConceicao commentedon Mar 11, 2025
Thank you for your answer. I will be working on another issue then.
riku-sakamoto commentedon Aug 16, 2025
After investigating the source code, I propose raising a
RuntimeWarningwhen casting from a unitlessnp.timedelta64(generic) to a unitfulnp.timedelta64as a temporary solution. This is intended to encourage users to pay attention to the behavior.Details
RuntimeWarningat unit conversion function (below)numpy/numpy/_core/src/multiarray/datetime.c
Lines 1037 to 1042 in 8b89d0f
view_offsetwhen the unit isgeneric.np.timedelta64object has agenericunit, the unit conversion function will not be called becauseview_offsetis set toTrue.numpy/numpy/_core/src/multiarray/datetime.c
Lines 3889 to 3893 in 8b89d0f
numpy/numpy/_core/src/umath/ufunc_object.c
Lines 787 to 790 in 8b89d0f
If it makes sense, I'll submit a PR for that.
This issue is about 6 months old. If it would be more appropriate to open a new issue for this proposal, please let me know and I’ll be happy to do so.
ENH: raise RuntimeWarning when casting from unitless timedelta to uni…
[ENH] Raise RuntimeWarning when casting from a unitless `np.timedelta…
[ENH] Raise RuntimeWarning when casting from a unitless `np.timedelta…
ENH: Raise RuntimeWarning when casting from a unitless `np.timedelta6…
4 remaining items
riku-sakamoto commentedon Sep 6, 2025
@seberg
I think it would be ideal to encourage users to define "NaT" with a specific unit, such as
np.timedelta64("NaT", "ns").But, if this turns out to be too inconvenient in practice and we have to address it, I have come up with two options:
genericunit internally as a private unit and only allow "NaT" to be created with thegenericunit.Personally, I prefer completly removing the
genericunit.but I think option 2 is a reasonable compromise in terms of codebase simplicity. What do you think?On second thought, since
np.datetime64currently allows unitless objects only for NaT,timedelta64should follow the same approach. That corresponds to the first option, which seems the most straightforward.ENH: Raise RuntimeWarning when casting from a unitless `np.timedelta6…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
riku-sakamoto commentedon Sep 7, 2025
@seberg
I updated the PR to reflect the latest changes based on the decisions so far.
I also marked some undecided behavior as future work.
Could you please take a look?
#29619
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
ENH: Raise RuntimeWarning when casting from a unitless `np.timedelta6…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
ENH: Raise RuntimeWarning when casting from a unitless `np.timedelta6…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
ENH: Raise RuntimeWarning when casting from a unitless `np.timedelta6…
DEP: deprecate the ``generic`` unit in `numpy.timedelta64` and `numpy…
feat(wasm): add the fix-candidate output pane to five more recipes
feat(wasm): add the fix-candidate output pane to five more recipes (#361