Skip to content

Commit

Permalink
feat: add total duration methods for DuckDB (#1831)
Browse files Browse the repository at this point in the history
  • Loading branch information
raisadz authored Jan 19, 2025
1 parent c39bd45 commit b4ce9d1
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
52 changes: 52 additions & 0 deletions narwhals/_duckdb/expr_dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,55 @@ def date(self) -> DuckDBExpr:
"date",
returns_scalar=self._compliant_expr._returns_scalar,
)

def total_minutes(self) -> DuckDBExpr:
from duckdb import ConstantExpression
from duckdb import FunctionExpression

return self._compliant_expr._from_call(
lambda _input: FunctionExpression(
"datepart", ConstantExpression("minute"), _input
),
"total_minutes",
returns_scalar=self._compliant_expr._returns_scalar,
)

def total_seconds(self) -> DuckDBExpr:
from duckdb import ConstantExpression
from duckdb import FunctionExpression

return self._compliant_expr._from_call(
lambda _input: 60
* FunctionExpression("datepart", ConstantExpression("minute"), _input)
+ FunctionExpression("datepart", ConstantExpression("second"), _input),
"total_seconds",
returns_scalar=self._compliant_expr._returns_scalar,
)

def total_milliseconds(self) -> DuckDBExpr:
from duckdb import ConstantExpression
from duckdb import FunctionExpression

return self._compliant_expr._from_call(
lambda _input: 60_000
* FunctionExpression("datepart", ConstantExpression("minute"), _input)
+ FunctionExpression("datepart", ConstantExpression("millisecond"), _input),
"total_milliseconds",
returns_scalar=self._compliant_expr._returns_scalar,
)

def total_microseconds(self) -> DuckDBExpr:
from duckdb import ConstantExpression
from duckdb import FunctionExpression

return self._compliant_expr._from_call(
lambda _input: 60_000_000
* FunctionExpression("datepart", ConstantExpression("minute"), _input)
+ FunctionExpression("datepart", ConstantExpression("microsecond"), _input),
"total_microseconds",
returns_scalar=self._compliant_expr._returns_scalar,
)

def total_nanoseconds(self) -> DuckDBExpr:
msg = "`total_nanoseconds` is not implemented for DuckDB"
raise NotImplementedError(msg)
4 changes: 3 additions & 1 deletion tests/expr_and_series/dt/datetime_duration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ def test_duration_attributes(
) -> None:
if PANDAS_VERSION < (2, 2) and "pandas_pyarrow" in str(constructor):
request.applymarker(pytest.mark.xfail)
if ("pyspark" in str(constructor)) or "duckdb" in str(constructor):
if "pyspark" in str(constructor):
request.applymarker(pytest.mark.xfail)
if "duckdb" in str(constructor) and attribute == "total_nanoseconds":
request.applymarker(pytest.mark.xfail)

df = nw.from_native(constructor(data))
Expand Down

0 comments on commit b4ce9d1

Please sign in to comment.