Skip to content

ENH: datetime/timedelta arithmetic with python datetime/timedelta scalars #30985

Open

Description

@randolf-scholz

Proposed new feature or change:

I have noticed some inconsistency with how numpy handles datetime and timedelta arithmetic, compared to regular numeric types. With regular numerical types, addition between numpy scalars and corresponding python scalars always yields numpy scalars:

>>> import numpy as np
>>> np.float64(1.0) + 1.0
np.float64(2.0)
>>> 1.0 + np.float32(1.0)
np.float32(2.0)

However, timedelta and datetime seem to be the odd ones out and return python scalars instead:

>>> import datetime as dt
>>> import numpy as np
>>> np.timedelta64(1, "h") - dt.timedelta(seconds=5)
datetime.timedelta(seconds=3595)
>>> dt.datetime.now() + np.timedelta64(1, "h")
datetime.datetime(2026, 3, 10, 10, 51, 33, 518449)

Moreover, numerical arrays allow arithmetic with python scalars:

>>> import numpy as np
>>> np.array([1.0, 3.2]) + 2.8
array([3.8, 6. ])
>>> 1+2j + np.array([1.0, 3.2])
array([2. +2.j, 4.2+2.j])

However, datetime and timedelta arrays apparently not:

>>> import numpy as np
>>> import datetime as dt
>>> np.array([np.timedelta64(1, "h")]) + dt.timedelta(seconds=5)
numpy._core._exceptions._UFuncBinaryResolutionError: ufunc 'add' cannot use operands with types dtype('<m8[h]') and dtype('O')

Suggestions

  1. Make arithmetic between numpy datetime/timedelta scalars and python datetime/timedelta scalars return numpy scalars, just as with numeric types
    • this is obviously a breaking change, but the current behavior is unexpected, given the semantics of regular numeric types
    • document the behavior with mixed arithmetic here
  2. Supports arithmetic between numpy datetime/timedelta arrays and python datetime/timedelta scalars.
    • just like with regular numeric types.

Activity

  1. changed the title [-]ENH: datetime/timedelta arithmetic with python datetime/tiemdelta scalars[/-] [+]ENH: datetime/timedelta arithmetic with python datetime/timedelta scalars[/+] on Mar 10, 2026
  2. ngoldbaum commented on Mar 10, 2026

    @ngoldbaum
    Member

    I'm not sure how load-bearing this behavior is in Pandas, but also I'm not sure how you could check that without making the change.

  3. seberg commented on Mar 11, 2026

    @seberg
    Member

    FWIW, I could imagine this is fine, all of this behavior comes down to np.asarray(dt.timedelta(seconds=5)) not picking an m8[ns] or similar default.

    Is that something one can decide on? Because if, things would be a lot more straight-forward.

    Without that, I am afraid, implementing np_timedelta + python_timdelta will leave the array paths still dangling.
    gh-28596 would fix that partially, the other approach might be more promotion specialization, but I don't really like that thought.
    (In fact gh-28596 is interesting, in that I am not sure if it proposed to add the scalar operations, since without that mixing fails.)

  4. jbrockmendel commented on Mar 28, 2026

    @jbrockmendel
    Contributor

    I'm not sure how load-bearing this behavior is in Pandas

    Not at all. We wrap dt64/td64 arrays/scalars and use our own code specifically because of the weird behaviors numpy has.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions