diff --git a/python/cudf/cudf/_lib/cpp/datetime.pxd b/python/cudf/cudf/_lib/cpp/datetime.pxd index ef97be3cf9e..c27eb324008 100644 --- a/python/cudf/cudf/_lib/cpp/datetime.pxd +++ b/python/cudf/cudf/_lib/cpp/datetime.pxd @@ -12,6 +12,17 @@ cdef extern from "cudf/datetime.hpp" namespace "cudf::datetime" nogil: cdef unique_ptr[column] extract_hour(const column_view& column) except + cdef unique_ptr[column] extract_minute(const column_view& column) except + cdef unique_ptr[column] extract_second(const column_view& column) except + + cdef unique_ptr[column] ceil_day(const column_view& column) except + + cdef unique_ptr[column] ceil_hour(const column_view& column) except + + cdef unique_ptr[column] ceil_minute(const column_view& column) except + + cdef unique_ptr[column] ceil_second(const column_view& column) except + + cdef unique_ptr[column] ceil_millisecond( + const column_view& column + ) except + + cdef unique_ptr[column] ceil_microsecond( + const column_view& column + ) except + + cdef unique_ptr[column] ceil_nanosecond(const column_view& column) except + cdef unique_ptr[column] add_calendrical_months( const column_view& timestamps, const column_view& months diff --git a/python/cudf/cudf/_lib/datetime.pyx b/python/cudf/cudf/_lib/datetime.pyx index 1b152f1a3b7..4921d1b4ace 100644 --- a/python/cudf/cudf/_lib/datetime.pyx +++ b/python/cudf/cudf/_lib/datetime.pyx @@ -59,6 +59,33 @@ def extract_datetime_component(Column col, object field): return result +def ceil_datetime(Column col, object field): + cdef unique_ptr[column] c_result + cdef column_view col_view = col.view() + + with nogil: + # https://pandas.pydata.org/pandas-docs/version/0.25.0/reference/api/pandas.Timedelta.resolution.html + if field == "D": + c_result = move(libcudf_datetime.ceil_day(col_view)) + elif field == "H": + c_result = move(libcudf_datetime.ceil_hour(col_view)) + elif field == "T": + c_result = move(libcudf_datetime.ceil_minute(col_view)) + elif field == "S": + c_result = move(libcudf_datetime.ceil_second(col_view)) + elif field == "L": + c_result = move(libcudf_datetime.ceil_millisecond(col_view)) + elif field == "U": + c_result = move(libcudf_datetime.ceil_microsecond(col_view)) + elif field == "N": + c_result = move(libcudf_datetime.ceil_nanosecond(col_view)) + else: + raise ValueError(f"Invalid resolution: '{field}'") + + result = Column.from_unique_ptr(move(c_result)) + return result + + def is_leap_year(Column col): """Returns a boolean indicator whether the year of the date is a leap year """ diff --git a/python/cudf/cudf/core/column/datetime.py b/python/cudf/cudf/core/column/datetime.py index 46ff1990ac2..ad1a2d852f1 100644 --- a/python/cudf/cudf/core/column/datetime.py +++ b/python/cudf/cudf/core/column/datetime.py @@ -170,6 +170,9 @@ def values(self): def get_dt_field(self, field: str) -> ColumnBase: return libcudf.datetime.extract_datetime_component(self, field) + def ceil(self, field: str) -> ColumnBase: + return libcudf.datetime.ceil_datetime(self, field) + def normalize_binop_value(self, other: DatetimeLikeScalar) -> ScalarLike: if isinstance(other, cudf.Scalar): return other diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index ff3b9fc68ef..e44bb18b629 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -5923,6 +5923,13 @@ def _get_dt_field(self, field): data=out_column, index=self.series._index, name=self.series.name ) + def ceil(self, field): + out_column = self.series._column.ceil(field) + + return Series( + data=out_column, index=self.series._index, name=self.series.name + ) + def strftime(self, date_format, *args, **kwargs): """ Convert to Series using specified ``date_format``. diff --git a/python/cudf/cudf/tests/test_datetime.py b/python/cudf/cudf/tests/test_datetime.py index 65e87e88f55..6e5b3c39dc4 100644 --- a/python/cudf/cudf/tests/test_datetime.py +++ b/python/cudf/cudf/tests/test_datetime.py @@ -1551,3 +1551,32 @@ def test_error_values(): match="DateTime Arrays is not yet implemented in cudf", ): s.values + + +@pytest.mark.parametrize( + "data", + [ + ( + [ + "2020-05-31 08:00:00", + "1999-12-31 18:40:10", + "2000-12-31 04:00:05", + "1900-02-28 07:00:06", + "1800-03-14 07:30:20", + "2100-03-14 07:30:20", + "1970-01-01 00:00:09", + "1969-12-31 12:59:10", + ] + ) + ], +) +@pytest.mark.parametrize("time_type", DATETIME_TYPES) +@pytest.mark.parametrize("resolution", ["D", "H", "T", "S", "L", "U", "N"]) +def test_ceil(data, time_type, resolution): + + ps = pd.Series(data, dtype=time_type) + gs = cudf.from_pandas(ps) + + expect = ps.dt.ceil(resolution) + got = gs.dt.ceil(resolution) + assert_eq(expect, got)