Repository: vaexio/vaex
Pull request #194 (2019-03-29 13:12:21)

Adding additional datetime operations, and improving test coverage.
Adding additional `datetime` operations. Each operation is covered by a unit-test.

maartenbreddels: Actually, you can remove all these!
```
    @property
    def day_name(self):
        return self.expression.ds.func.dt_day_name(self.expression)
```

`@register_function(scope='dt', as_property=True)` will add these automatically (look at the stringoperations class!)

I almost forgot.
JovanVeljanoski: I removed the redundant code. Tests pass.

Patch:
diff --git a/packages/vaex-core/vaex/expression.py b/packages/vaex-core/vaex/expression.py
index 56b5ac5a..c89e3699 100644
--- a/packages/vaex-core/vaex/expression.py
+++ b/packages/vaex-core/vaex/expression.py
@@ -115,26 +115,6 @@ class DateTime(object):
     def __init__(self, expression):
         self.expression = expression
 
-    @property
-    def year(self):
-        return self.expression.ds.func.dt_year(self.expression)
-
-    @property
-    def dayofweek(self):
-        return self.expression.ds.func.dt_dayofweek(self.expression)
-
-    @property
-    def dayofyear(self):
-        return self.expression.ds.func.dt_dayofyear(self.expression)
-
-    @property
-    def hour(self):
-        return self.expression.ds.func.dt_hour(self.expression)
-
-    @property
-    def weekofyear(self):
-        return self.expression.ds.func.dt_weekofyear(self.expression)
-
 
 class StringOperations(object):
     """String operations"""
@@ -503,7 +483,7 @@ def f({0}):
                 return new
         expr = expresso.translate(self.expression, translate)
         return Expression(self.ds, expr)
- 
+
     def astype(self, dtype):
         return self.ds.func.astype(self, str(dtype))
 
diff --git a/packages/vaex-core/vaex/functions.py b/packages/vaex-core/vaex/functions.py
index ee4d92f7..588fa08f 100644
--- a/packages/vaex-core/vaex/functions.py
+++ b/packages/vaex-core/vaex/functions.py
@@ -149,33 +149,63 @@ def fillna(ar, value, fill_nan=True, fill_masked=True):
 @register_function(scope='dt', as_property=True)
 def dt_dayofweek(x):
     import pandas as pd
-    # x = x.astype("<M8[ns]")
     return pd.Series(x).dt.dayofweek.values
 
 @register_function(scope='dt', as_property=True)
 def dt_dayofyear(x):
     import pandas as pd
-    # x = x.astype("<M8[ns]")
     return pd.Series(x).dt.dayofyear.values
 
+@register_function(scope='dt', as_property=True)
+def dt_is_leap_year(x):
+    import pandas as pd
+    return pd.Series(x).dt.is_leap_year.values
+
 @register_function(scope='dt', as_property=True)
 def dt_year(x):
     import pandas as pd
-    # x = x.astype("<M8[ns]")
     return pd.Series(x).dt.year.values
 
+@register_function(scope='dt', as_property=True)
+def dt_month(x):
+    import pandas as pd
+    return pd.Series(x).dt.month.values
+
+@register_function(scope='dt', as_property=True)
+def dt_month_name(x):
+    import pandas as pd
+    return pd.Series(x).dt.month_name().values.astype(str)
+
+@register_function(scope='dt', as_property=True)
+def dt_day(x):
+    import pandas as pd
+    return pd.Series(x).dt.day.values
+
+@register_function(scope='dt', as_property=True)
+def dt_day_name(x):
+    import pandas as pd
+    return pd.Series(x).dt.day_name().values.astype(str)
+
 @register_function(scope='dt', as_property=True)
 def dt_weekofyear(x):
     import pandas as pd
-    # x = x.astype("<M8[ns]")
     return pd.Series(x).dt.weekofyear.values
 
 @register_function(scope='dt', as_property=True)
 def dt_hour(x):
     import pandas as pd
-    # x = x.astype("<M8[ns]")
     return pd.Series(x).dt.hour.values
 
+@register_function(scope='dt', as_property=True)
+def dt_minute(x):
+    import pandas as pd
+    return pd.Series(x).dt.minute.values
+
+@register_function(scope='dt', as_property=True)
+def dt_second(x):
+    import pandas as pd
+    return pd.Series(x).dt.second.values
+
 
 ########## string operations ##########
 
@@ -239,7 +269,7 @@ def str_rstrip(x, to_strip=None):
     # in c++ we give empty string the same meaning as None
     return _to_string_sequence(x).rstrip('' if to_strip is None else to_strip) if to_strip != '' else x
 
-# TODO: slice, slice_replace, 
+# TODO: slice, slice_replace,
 
 @register_function(scope='str')
 def str_split(x, pattern):  # TODO: support n


Test patch:
diff --git a/tests/datetime_test.py b/tests/datetime_test.py
index 5d41e4b4..f440c119 100644
--- a/tests/datetime_test.py
+++ b/tests/datetime_test.py
@@ -1,10 +1,28 @@
 from common import *
 import numpy as np
 
-def test_dt():
-	t = np.array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='datetime64')
-	ds = vaex.from_arrays(t=t)
-	df = ds.to_pandas_df()
-	assert ds.t.dt.year.values.tolist() == df.t.dt.year.values.tolist()
-	assert ds.t.dt.dayofweek.values.tolist() == df.t.dt.dayofweek.values.tolist()
-	assert ds.t.dt.hour.values.tolist() == df.t.dt.hour.values.tolist()
+
+def test_datetime_operations():
+    date = [np.datetime64('2009-10-12T03:31:00'),
+            np.datetime64('2016-02-11T10:17:34'),
+            np.datetime64('2015-11-12T11:34:22'),
+            np.datetime64('2003-03-03T00:33:15'),
+            np.datetime64('2014-07-23T15:08:05'),
+            np.datetime64('2011-01-01T07:02:01')]
+
+    df = vaex.from_arrays(date=date)
+    pandas_df = df.to_pandas_df()
+
+    assert df.date.dt.hour.values.tolist() == pandas_df.date.dt.hour.values.tolist()
+    assert df.date.dt.minute.values.tolist() == pandas_df.date.dt.minute.values.tolist()
+    assert df.date.dt.second.values.tolist() == pandas_df.date.dt.second.values.tolist()
+    assert df.date.dt.day.values.tolist() == pandas_df.date.dt.day.values.tolist()
+    assert df.date.dt.day_name.values.tolist() == pandas_df.date.dt.day_name().values.tolist()
+    assert df.date.dt.month.values.tolist() == pandas_df.date.dt.month.values.tolist()
+    assert df.date.dt.month_name.values.tolist() == pandas_df.date.dt.month_name().values.tolist()
+    assert df.date.dt.year.values.tolist() == pandas_df.date.dt.year.values.tolist()
+    assert df.date.dt.is_leap_year.values.tolist() == pandas_df.date.dt.is_leap_year.values.tolist()
+    assert any(df.date.dt.is_leap_year.values.tolist())
+    assert df.date.dt.weekofyear.values.tolist() == pandas_df.date.dt.weekofyear.values.tolist()
+    assert df.date.dt.dayofyear.values.tolist() == pandas_df.date.dt.dayofyear.values.tolist()
+    assert df.date.dt.dayofweek.values.tolist() == pandas_df.date.dt.dayofweek.values.tolist()