From 4aa4aae1e7bfd4d91e1d1b1474dccddd3a603ba7 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 12 Jun 2022 12:32:15 -0700 Subject: [PATCH 1/2] ENH: get_resolution support non-nano --- pandas/_libs/tslibs/vectorized.pyi | 1 + pandas/_libs/tslibs/vectorized.pyx | 17 ++++++++++------- pandas/core/arrays/datetimes.py | 2 +- pandas/tests/tslibs/test_resolution.py | 7 +++++++ setup.py | 6 +++++- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pandas/_libs/tslibs/vectorized.pyi b/pandas/_libs/tslibs/vectorized.pyi index 8820a17ce5996..f303d51367ce7 100644 --- a/pandas/_libs/tslibs/vectorized.pyi +++ b/pandas/_libs/tslibs/vectorized.pyi @@ -28,6 +28,7 @@ def normalize_i8_timestamps( def get_resolution( stamps: npt.NDArray[np.int64], tz: tzinfo | None = ..., + reso: int = ..., # NPY_DATETIMEUNIT ) -> Resolution: ... def ints_to_pydatetime( arr: npt.NDArray[np.int64], diff --git a/pandas/_libs/tslibs/vectorized.pyx b/pandas/_libs/tslibs/vectorized.pyx index 2cab55e607f15..4a957e24ccdfd 100644 --- a/pandas/_libs/tslibs/vectorized.pyx +++ b/pandas/_libs/tslibs/vectorized.pyx @@ -33,6 +33,7 @@ from .np_datetime cimport ( NPY_FR_ns, dt64_to_dtstruct, npy_datetimestruct, + pandas_datetime_to_datetimestruct, ) from .offsets cimport BaseOffset from .period cimport get_period_ordinal @@ -226,17 +227,19 @@ cdef inline c_Resolution _reso_stamp(npy_datetimestruct *dts): @cython.wraparound(False) @cython.boundscheck(False) -def get_resolution(ndarray stamps, tzinfo tz=None) -> Resolution: +def get_resolution( + ndarray stamps, tzinfo tz=None, NPY_DATETIMEUNIT reso=NPY_FR_ns +) -> Resolution: # stamps is int64_t, any ndim cdef: - Localizer info = Localizer(tz, reso=NPY_FR_ns) + Localizer info = Localizer(tz, reso=reso) int64_t utc_val, local_val Py_ssize_t i, n = stamps.size Py_ssize_t pos = -1 # unused, avoid not-initialized warning cnp.flatiter it = cnp.PyArray_IterNew(stamps) npy_datetimestruct dts - c_Resolution reso = c_Resolution.RESO_DAY, curr_reso + c_Resolution pd_reso = c_Resolution.RESO_DAY, curr_reso for i in range(n): # Analogous to: utc_val = stamps[i] @@ -247,14 +250,14 @@ def get_resolution(ndarray stamps, tzinfo tz=None) -> Resolution: else: local_val = info.utc_val_to_local_val(utc_val, &pos) - dt64_to_dtstruct(local_val, &dts) + pandas_datetime_to_datetimestruct(local_val, reso, &dts) curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso + if curr_reso < pd_reso: + pd_reso = curr_reso cnp.PyArray_ITER_NEXT(it) - return Resolution(reso) + return Resolution(pd_reso) # ------------------------------------------------------------------------- diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index db55c165c9974..d297d3e9f8e4e 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -619,7 +619,7 @@ def is_normalized(self) -> bool: @property # NB: override with cache_readonly in immutable subclasses def _resolution_obj(self) -> Resolution: - return get_resolution(self.asi8, self.tz) + return get_resolution(self.asi8, self.tz, reso=self._reso) # ---------------------------------------------------------------- # Array-Like / EA-Interface Methods diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 15f4a9d032e5c..68599981b7654 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -4,6 +4,7 @@ Resolution, get_resolution, ) +from pandas._libs.tslibs.dtypes import NpyDatetimeUnit def test_get_resolution_nano(): @@ -11,3 +12,9 @@ def test_get_resolution_nano(): arr = np.array([1], dtype=np.int64) res = get_resolution(arr) assert res == Resolution.RESO_NS + + +def test_get_resolution_non_nano_data(): + arr = np.array([1], dtype=np.int64) + res = get_resolution(arr, None, NpyDatetimeUnit.NPY_FR_us.value) + assert res == Resolution.RESO_US diff --git a/setup.py b/setup.py index cb713e6d74392..27e6d8cb10025 100755 --- a/setup.py +++ b/setup.py @@ -551,7 +551,11 @@ def srcpath(name=None, suffix=".pyx", subdir="src"): "depends": tseries_depends, "sources": ["pandas/_libs/tslibs/src/datetime/np_datetime.c"], }, - "_libs.tslibs.vectorized": {"pyxfile": "_libs/tslibs/vectorized"}, + "_libs.tslibs.vectorized": { + "pyxfile": "_libs/tslibs/vectorized", + "depends": tseries_depends, + "sources": ["pandas/_libs/tslibs/src/datetime/np_datetime.c"], + }, "_libs.testing": {"pyxfile": "_libs/testing"}, "_libs.window.aggregations": { "pyxfile": "_libs/window/aggregations", From 82b15b736a344f69bb511199d0d81a687f051fd0 Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 13 Jun 2022 11:05:35 -0700 Subject: [PATCH 2/2] tzaware case --- pandas/tests/tslibs/test_resolution.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 68599981b7654..7b2268f16a85f 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -1,4 +1,5 @@ import numpy as np +import pytz from pandas._libs.tslibs import ( Resolution, @@ -18,3 +19,6 @@ def test_get_resolution_non_nano_data(): arr = np.array([1], dtype=np.int64) res = get_resolution(arr, None, NpyDatetimeUnit.NPY_FR_us.value) assert res == Resolution.RESO_US + + res = get_resolution(arr, pytz.UTC, NpyDatetimeUnit.NPY_FR_us.value) + assert res == Resolution.RESO_US