Repository: modin-project/modin

Issue:
read_csv with OmniSci backend handles incorrectly date fields
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Ubuntu 20.04
- **Modin version** : a2ecf3190dad78e43f77d6132d835ef22dfb3382
- **Python version**: 3.8.6
- **Code we can use to reproduce**:
```
import os

os.environ["MODIN_EXPERIMENTAL"] = "True"
os.environ["MODIN_BACKEND"] = "omnisci"

import modin.pandas as pd
import pandas

from modin.pandas.test.utils import df_equals

row_size = 64
test_filename = "test_filename.csv"

dates = pandas.date_range("2000", freq="h", periods=row_size)
data = [str(x.date()) for x in dates]

df_data = pandas.DataFrame(data)

try:
    df_data.to_csv(test_filename, index=False)

    df_pandas = pandas.read_csv(test_filename)
    df_modin = pd.read_csv(test_filename)

    df_equals(df_pandas, df_modin)
finally:
    os.remove(test_filename)
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->
Pandas handles date fields as strings since 1.2.0 version.

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->
```
Traceback (most recent call last):
  File "test.py", line 46, in <module>
    df_equals(df_pandas, df_modin)
  File "/modin/modin/pandas/test/utils.py", line 542, in df_equals
    check_categorical=False,
  File "/miniconda3/envs/modin_omnisci/lib/python3.7/site-packages/pandas/_testing.py", line 1704, in assert_frame_equal
    atol=atol,
  File "/miniconda3/envs/modin_omnisci/lib/python3.7/site-packages/pandas/_testing.py", line 1427, in assert_series_equal
    raise AssertionError(msg)
AssertionError: [datetimelike_compat=True] ['2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01'
 '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01'
 '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01'
 '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01'
 '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-01' '2000-01-02'
 '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02'
 '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02'
 '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02'
 '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-02'
 '2000-01-02' '2000-01-02' '2000-01-02' '2000-01-03' '2000-01-03'
 '2000-01-03' '2000-01-03' '2000-01-03' '2000-01-03' '2000-01-03'
 '2000-01-03' '2000-01-03' '2000-01-03' '2000-01-03' '2000-01-03'
 '2000-01-03' '2000-01-03' '2000-01-03' '2000-01-03'] is not equal to <DatetimeArray>
['2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-01 00:00:00', '2000-01-01 00:00:00', '2000-01-01 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-02 00:00:00', '2000-01-02 00:00:00', '2000-01-02 00:00:00',
 '2000-01-03 00:00:00', '2000-01-03 00:00:00', '2000-01-03 00:00:00',
 '2000-01-03 00:00:00', '2000-01-03 00:00:00', '2000-01-03 00:00:00',
 '2000-01-03 00:00:00', '2000-01-03 00:00:00', '2000-01-03 00:00:00',
 '2000-01-03 00:00:00', '2000-01-03 00:00:00', '2000-01-03 00:00:00',
 '2000-01-03 00:00:00', '2000-01-03 00:00:00', '2000-01-03 00:00:00',
 '2000-01-03 00:00:00']
Length: 64, dtype: datetime64[ns].
```


Discussion:

Resolving patch:
diff --git a/modin/experimental/engines/omnisci_on_ray/io.py b/modin/experimental/engines/omnisci_on_ray/io.py
--- a/modin/experimental/engines/omnisci_on_ray/io.py
+++ b/modin/experimental/engines/omnisci_on_ray/io.py
@@ -186,6 +186,9 @@ def read_csv(
                 null_values=None,
                 true_values=None,
                 false_values=None,
+                # timestamp fields should be handled as strings if parse_dates
+                # didn't passed explicitly as an array or a dict
+                timestamp_parsers=[""] if isinstance(parse_dates, bool) else None,
                 strings_can_be_null=None,
                 include_columns=None,
                 include_missing_columns=None,


Test patch:
diff --git a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
--- a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
+++ b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
@@ -32,6 +32,7 @@
     test_data_keys,
     generate_multiindex,
     eval_general,
+    eval_io,
 )
 
 
@@ -116,6 +117,7 @@ def run_modin(
         df_equals(ref_res, exp_res)
 
 
+@pytest.mark.usefixtures("TestReadCSVFixture")
 class TestCSV:
     root = os.path.abspath(__file__ + "/.." * 6)  # root of modin repo
 
@@ -302,6 +304,30 @@ def test_float32(self):
 
         df_equals(modin_df, pandas_df)
 
+    # Datetime Handling tests
+    @pytest.mark.parametrize("engine", [None, "arrow"])
+    @pytest.mark.parametrize(
+        "parse_dates",
+        [
+            True,
+            False,
+            ["col2"],
+        ],
+    )
+    def test_read_csv_datetime(
+        self,
+        engine,
+        parse_dates,
+    ):
+
+        eval_io(
+            fn_name="read_csv",
+            md_extra_kwargs={"engine": engine},
+            # read_csv kwargs
+            filepath_or_buffer=pytest.csvs_names["test_read_csv_regular"],
+            parse_dates=parse_dates,
+        )
+
 
 class TestMasks:
     data = {
diff --git a/modin/pandas/test/utils.py b/modin/pandas/test/utils.py
--- a/modin/pandas/test/utils.py
+++ b/modin/pandas/test/utils.py
@@ -648,6 +648,7 @@ def eval_general(
     check_exception_type=True,
     raising_exceptions=None,
     check_kwargs_callable=True,
+    md_extra_kwargs=None,
     **kwargs,
 ):
     if raising_exceptions:
@@ -689,6 +690,10 @@ def execute_callable(fn, inplace=False, md_kwargs={}, pd_kwargs={}):
         md_kwargs[key] = md_value
         pd_kwargs[key] = pd_value
 
+        if md_extra_kwargs:
+            assert isinstance(md_extra_kwargs, dict)
+            md_kwargs.update(md_extra_kwargs)
+
     values = execute_callable(
         operation, md_kwargs=md_kwargs, pd_kwargs=pd_kwargs, inplace=__inplace__
     )
@@ -704,6 +709,7 @@ def eval_io(
     raising_exceptions=io_ops_bad_exc,
     check_kwargs_callable=True,
     modin_warning=None,
+    md_extra_kwargs=None,
     *args,
     **kwargs,
 ):
@@ -726,7 +732,10 @@ def eval_io(
         Exceptions that should be raised even if they are raised
         both by Pandas and Modin (check evaluated only if
         `check_exception_type` passed as `True`).
-    modin_warning: Warning that should be raised by Modin.
+    modin_warning: obj
+        Warning that should be raised by Modin.
+    md_extra_kwargs: dict
+        Modin operation specific kwargs.
     """
 
     def applyier(module, *args, **kwargs):
@@ -743,6 +752,7 @@ def call_eval_general():
             check_exception_type=check_exception_type,
             raising_exceptions=raising_exceptions,
             check_kwargs_callable=check_kwargs_callable,
+            md_extra_kwargs=md_extra_kwargs,
             *args,
             **kwargs,
         )