Repository: modin-project/modin

Issue:
Support different datetime extractions
Extract_year already supported, we need to support other similar operations. 

Note that so far we use parameter shape_hint="column" during construction of Series object to prevent redundant frame materialization. This parameter so far is in omnisci-on-ray branch only


Discussion:
Need to add more datetime functions in omniscidb.
Also OmniSci backend has different behavior from pandas in functions such as `dt.dayofweek`, `dt.microsecond` and `dt.nanosecond`.
In `dt.dayofweek`, we have different start for a week (Monday for pandas and Sunday for OmniSci).
In `dt.microsecond` and `dt.nanosecond` we have different results for pandas (number of micro or nanoseconds) and OmniSci (micro or nanoseconds + seconds)


Resolving patch:
diff --git a/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py b/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
--- a/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
+++ b/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
@@ -1397,4 +1397,11 @@ def build_dt_expr(dt_operation, col_expr):
 
     res = OpExpr("PG_EXTRACT", [operation, col_expr], _get_dtype("int32"))
 
+    if dt_operation == "isodow":
+        res = res.sub(LiteralExpr(1))
+    elif dt_operation == "microsecond":
+        res = res.mod(LiteralExpr(1000000))
+    elif dt_operation == "nanosecond":
+        res = res.mod(LiteralExpr(1000))
+
     return res
diff --git a/modin/experimental/core/storage_formats/hdk/query_compiler.py b/modin/experimental/core/storage_formats/hdk/query_compiler.py
--- a/modin/experimental/core/storage_formats/hdk/query_compiler.py
+++ b/modin/experimental/core/storage_formats/hdk/query_compiler.py
@@ -635,6 +635,46 @@ def dt_hour(self):
             self._modin_frame.dt_extract("hour"), self._shape_hint
         )
 
+    def dt_minute(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("minute"), self._shape_hint
+        )
+
+    def dt_second(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("second"), self._shape_hint
+        )
+
+    def dt_microsecond(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("microsecond"), self._shape_hint
+        )
+
+    def dt_nanosecond(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("nanosecond"), self._shape_hint
+        )
+
+    def dt_quarter(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("quarter"), self._shape_hint
+        )
+
+    def dt_dayofweek(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("isodow"), self._shape_hint
+        )
+
+    def dt_weekday(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("isodow"), self._shape_hint
+        )
+
+    def dt_dayofyear(self):
+        return self.__constructor__(
+            self._modin_frame.dt_extract("doy"), self._shape_hint
+        )
+
     def _bin_op(self, other, op_name, **kwargs):
         """
         Perform a binary operation on a frame.


Test patch:
diff --git a/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py b/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
--- a/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
+++ b/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
@@ -1989,7 +1989,7 @@ class TestDateTime:
             [
                 "2018-10-26 12:00",
                 "2018-10-26 13:00:15",
-                "2020-10-26 04:00:15",
+                "2020-10-26 04:00:15.000000002",
                 "2020-10-26",
             ],
             format="mixed",
@@ -2020,6 +2020,54 @@ def dt_hour(df, **kwargs):
 
         run_and_compare(dt_hour, data=self.datetime_data)
 
+    def test_dt_minute(self):
+        def dt_minute(df, **kwargs):
+            return df["d"].dt.minute
+
+        run_and_compare(dt_minute, data=self.datetime_data)
+
+    def test_dt_second(self):
+        def dt_second(df, **kwargs):
+            return df["d"].dt.second
+
+        run_and_compare(dt_second, data=self.datetime_data)
+
+    def test_dt_microsecond(self):
+        def dt_microsecond(df, **kwargs):
+            return df["d"].dt.microsecond
+
+        run_and_compare(dt_microsecond, data=self.datetime_data)
+
+    def test_dt_nanosecond(self):
+        def dt_nanosecond(df, **kwargs):
+            return df["d"].dt.nanosecond
+
+        run_and_compare(dt_nanosecond, data=self.datetime_data)
+
+    def test_dt_quarter(self):
+        def dt_quarter(df, **kwargs):
+            return df["c"].dt.quarter
+
+        run_and_compare(dt_quarter, data=self.datetime_data)
+
+    def test_dt_dayofweek(self):
+        def dt_dayofweek(df, **kwargs):
+            return df["c"].dt.dayofweek
+
+        run_and_compare(dt_dayofweek, data=self.datetime_data)
+
+    def test_dt_weekday(self):
+        def dt_weekday(df, **kwargs):
+            return df["c"].dt.weekday
+
+        run_and_compare(dt_weekday, data=self.datetime_data)
+
+    def test_dt_dayofyear(self):
+        def dt_dayofyear(df, **kwargs):
+            return df["c"].dt.dayofyear
+
+        run_and_compare(dt_dayofyear, data=self.datetime_data)
+
     @pytest.mark.parametrize("cast", [True, False])
     @pytest.mark.parametrize("unit", CalciteSerializer._TIMESTAMP_PRECISION.keys())
     def test_dt_serialization(self, cast, unit):