This is how I do it.
import numpy as np
def dt2cal(dt):
"""
Convert array of datetime64 to a calendar array of year, month, day, hour,
minute, seconds, microsecond with these quantites indexed on the last axis.
Parameters
----------
dt : datetime64 array (...)
numpy.ndarray of datetimes of arbitrary shape
Returns
-------
cal : uint32 array (..., 7)
calendar array with last axis representing year, month, day, hour,
minute, second, microsecond
"""
# allocate output
out = np.empty(dt.shape + (7,), dtype="u4")
# decompose calendar floors
Y, M, D, h, m, s = [dt.astype(f"M8[{x}]") for x in "YMDhms"]
out[..., 0] = Y + 1970 # Gregorian Year
out[..., 1] = (M - Y) + 1 # month
out[..., 2] = (D - M) + 1 # dat
out[..., 3] = (dt - D).astype("m8[h]") # hour
out[..., 4] = (dt - h).astype("m8[m]") # minute
out[..., 5] = (dt - m).astype("m8[s]") # second
out[..., 6] = (dt - s).astype("m8[us]") # microsecond
return out
It's vectorized across arbitrary input dimensions, it's fast, its intuitive, it works on numpy v1.15.4, it doesn't use pandas.
I really wish numpy supported this functionality, it's required all the time in application development. I always get super nervous when I have to roll my own stuff like this, I always feel like I'm missing an edge case.