Skip to content

Commit

Permalink
ENH: non-nano Timestamp.timestamp, to_period (pandas-dev#46990)
Browse files Browse the repository at this point in the history
* ENH: Timestamp.timestamp non-nano

* ENH: Timestamp.to_period non-nano

* normalize prelims
  • Loading branch information
jbrockmendel authored and yehoshuadimarsky committed Jul 13, 2022
1 parent e4723c7 commit 4d49c5c
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 11 deletions.
1 change: 1 addition & 0 deletions pandas/_libs/tslibs/dtypes.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ from pandas._libs.tslibs.np_datetime cimport NPY_DATETIMEUNIT
cdef str npy_unit_to_abbrev(NPY_DATETIMEUNIT unit)
cdef NPY_DATETIMEUNIT freq_group_code_to_npy_unit(int freq) nogil
cdef int64_t periods_per_day(NPY_DATETIMEUNIT reso=*) except? -1
cdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1

cdef dict attrname_to_abbrevs

Expand Down
13 changes: 13 additions & 0 deletions pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,19 @@ cdef int64_t periods_per_day(NPY_DATETIMEUNIT reso=NPY_DATETIMEUNIT.NPY_FR_ns) e
return day_units


cdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1:
if reso == NPY_DATETIMEUNIT.NPY_FR_ns:
return 1_000_000_000
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
return 1_000_000
elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
return 1_000
elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
return 1
else:
raise NotImplementedError(reso)


cdef dict _reso_str_map = {
Resolution.RESO_NS.value: "nanosecond",
Resolution.RESO_US.value: "microsecond",
Expand Down
26 changes: 15 additions & 11 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ from pandas._libs.tslibs.conversion cimport (
convert_datetime_to_tsobject,
convert_to_tsobject,
)
from pandas._libs.tslibs.dtypes cimport npy_unit_to_abbrev
from pandas._libs.tslibs.dtypes cimport (
npy_unit_to_abbrev,
periods_per_day,
periods_per_second,
)
from pandas._libs.tslibs.util cimport (
is_array,
is_datetime64_object,
Expand Down Expand Up @@ -811,11 +815,12 @@ cdef class _Timestamp(ABCTimestamp):
cdef:
local_val = self._maybe_convert_value_to_local()
int64_t normalized
int64_t ppd = periods_per_day(self._reso)

if self._reso != NPY_FR_ns:
raise NotImplementedError(self._reso)

normalized = normalize_i8_stamp(local_val)
normalized = normalize_i8_stamp(local_val, ppd)
return Timestamp(normalized).tz_localize(self.tzinfo)

# -----------------------------------------------------------------
Expand All @@ -834,8 +839,8 @@ cdef class _Timestamp(ABCTimestamp):

if len(state) == 3:
# pre-non-nano pickle
# TODO: no tests get here 2022-05-10
reso = NPY_FR_ns
assert False # checking for coverage
else:
reso = state[4]
self._reso = reso
Expand Down Expand Up @@ -982,10 +987,10 @@ cdef class _Timestamp(ABCTimestamp):
"""
# GH 17329
# Note: Naive timestamps will not match datetime.stdlib
if self._reso != NPY_FR_ns:
raise NotImplementedError(self._reso)

return round(self.value / 1e9, 6)
denom = periods_per_second(self._reso)

return round(self.value / denom, 6)

cpdef datetime to_pydatetime(_Timestamp self, bint warn=True):
"""
Expand Down Expand Up @@ -1080,9 +1085,6 @@ cdef class _Timestamp(ABCTimestamp):
"""
from pandas import Period

if self._reso != NPY_FR_ns:
raise NotImplementedError(self._reso)

if self.tz is not None:
# GH#21333
warnings.warn(
Expand Down Expand Up @@ -2252,16 +2254,18 @@ Timestamp.resolution = Timedelta(nanoseconds=1) # GH#21336, GH#21365


@cython.cdivision(False)
cdef inline int64_t normalize_i8_stamp(int64_t local_val) nogil:
cdef inline int64_t normalize_i8_stamp(int64_t local_val, int64_t ppd) nogil:
"""
Round the localized nanosecond timestamp down to the previous midnight.
Parameters
----------
local_val : int64_t
ppd : int64_t
Periods per day in the Timestamp's resolution.
Returns
-------
int64_t
"""
return local_val - (local_val % ccalendar.DAY_NANOS)
return local_val - (local_val % ppd)
8 changes: 8 additions & 0 deletions pandas/tests/scalar/timestamp/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,3 +840,11 @@ def test_to_datetime64(self, dt64, ts):
res = ts.to_datetime64()
assert res == dt64
assert res.dtype == dt64.dtype

def test_timestamp(self, dt64, ts):
alt = Timestamp(dt64)
assert ts.timestamp() == alt.timestamp()

def test_to_period(self, dt64, ts):
alt = Timestamp(dt64)
assert ts.to_period("D") == alt.to_period("D")

0 comments on commit 4d49c5c

Please sign in to comment.