From 18f3c4352159deb201e64a1844e015fffaa80024 Mon Sep 17 00:00:00 2001 From: Daniel Mesejo Date: Tue, 11 Nov 2025 21:36:22 +0100 Subject: [PATCH] feat(datafusion): add compilation rule for Strftime --- .../datafusion/tests/test_temporal.py | 31 +++++++++++++++++++ ibis/backends/sql/compilers/datafusion.py | 4 ++- ibis/backends/tests/test_temporal.py | 4 +-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ibis/backends/datafusion/tests/test_temporal.py b/ibis/backends/datafusion/tests/test_temporal.py index 009b6041c022..dbbdab90b4a6 100644 --- a/ibis/backends/datafusion/tests/test_temporal.py +++ b/ibis/backends/datafusion/tests/test_temporal.py @@ -6,6 +6,10 @@ from pytest import param import ibis +from ibis.backends.datafusion.tests.conftest import BackendTest + +pd = pytest.importorskip("pandas") +pa = pytest.importorskip("pyarrow") @pytest.mark.parametrize( @@ -36,3 +40,30 @@ def test_time_extract_literal(con, func, expected): value = ibis.time("14:48:05.359") assert con.execute(func(value).name("tmp")) == expected + + +@pytest.mark.parametrize( + "pattern", + [ + "%Y-%m-%d", + "%Y:%m:%d", + "%Y%m%d", + "%d-%m-%Y", + "%Y-%b-%d", + "%Y-%B-%d", + "%Y-%B-%d-%a", + "%F", + "%Y:%m:%d:%H:%M:%S", + ], +) +def test_strftime(con, pattern): + df = pd.DataFrame({"time_col": pa.array([18506, 18507, 18508, 18509], pa.date32())}) + + t = ibis.memtable(df) + + name = "formatted" + expr = t.time_col.strftime(pattern).name(name) + expected = df.time_col.dt.strftime(pattern).rename(name) + + result = con.execute(expr) + BackendTest.assert_series_equal(result, expected) diff --git a/ibis/backends/sql/compilers/datafusion.py b/ibis/backends/sql/compilers/datafusion.py index b357b4c859ea..032b8c6b2a72 100644 --- a/ibis/backends/sql/compilers/datafusion.py +++ b/ibis/backends/sql/compilers/datafusion.py @@ -38,7 +38,6 @@ class DataFusionCompiler(SQLGlotCompiler): ops.CountDistinctStar, ops.DateDelta, ops.RowID, - ops.Strftime, ops.TimeDelta, ops.TimestampBucket, ops.TimestampDelta, @@ -629,5 +628,8 @@ def visit_ApproxQuantile(self, op, *, arg, quantile, where): def visit_ApproxQuantile(self, op, *, arg, quantile, where): return self.agg.approx_percentile_cont(arg, quantile, where=where) + def visit_Strftime(self, op, *, arg, format_str): + return self.f.date_format(arg, format_str) + compiler = DataFusionCompiler() diff --git a/ibis/backends/tests/test_temporal.py b/ibis/backends/tests/test_temporal.py index 0ef9b309ad3f..1bc6d3993f9d 100644 --- a/ibis/backends/tests/test_temporal.py +++ b/ibis/backends/tests/test_temporal.py @@ -1041,9 +1041,7 @@ def test_interval_add_cast_column(backend, alltypes, df): ), ], ) -@pytest.mark.notimpl( - ["datafusion", "druid", "exasol"], raises=com.OperationNotDefinedError -) +@pytest.mark.notimpl(["druid", "exasol"], raises=com.OperationNotDefinedError) def test_strftime(backend, alltypes, df, expr_fn, pandas_pattern): expr = expr_fn(alltypes) expected = df.timestamp_col.dt.strftime(pandas_pattern).rename("formatted")