Skip to content

Commit

Permalink
feat(api): isoyear method (#9034)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaijennissen authored May 8, 2024
1 parent c770fa1 commit 4707c44
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 0 deletions.
3 changes: 3 additions & 0 deletions ibis/backends/bigquery/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ def visit_ExtractEpochSeconds(self, op, *, arg):
def visit_ExtractWeekOfYear(self, op, *, arg):
return self.f.extract(self.v.isoweek, arg)

def visit_ExtractIsoYear(self, op, *, arg):
return self.f.extract(self.v.isoyear, arg)

def visit_ExtractMillisecond(self, op, *, arg):
return self.f.extract(self.v.millisecond, arg)

Expand Down
1 change: 1 addition & 0 deletions ibis/backends/clickhouse/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class ClickHouseCompiler(SQLGlotCompiler):
ops.ExtractSecond: "toSecond",
ops.ExtractWeekOfYear: "toISOWeek",
ops.ExtractYear: "toYear",
ops.ExtractIsoYear: "toISOYear",
ops.First: "any",
ops.IntegerRange: "range",
ops.IsInf: "isInfinite",
Expand Down
1 change: 1 addition & 0 deletions ibis/backends/duckdb/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class DuckDBCompiler(SQLGlotCompiler):
ops.BitOr: "bit_or",
ops.BitXor: "bit_xor",
ops.EndsWith: "suffix",
ops.ExtractIsoYear: "isoyear",
ops.Hash: "hash",
ops.IntegerRange: "range",
ops.TimestampRange: "range",
Expand Down
3 changes: 3 additions & 0 deletions ibis/backends/exasol/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ def visit_ExtractDayOfYear(self, op, *, arg):
def visit_ExtractWeekOfYear(self, op, *, arg):
return self.cast(self.f.to_char(arg, "IW"), op.dtype)

def visit_ExtractIsoYear(self, op, *, arg):
return self.cast(self.f.to_char(arg, "IYYY"), op.dtype)

def visit_DayOfWeekName(self, op, *, arg):
return self.f.concat(
self.f.substr(self.f.to_char(arg, "DAY"), 0, 1),
Expand Down
3 changes: 3 additions & 0 deletions ibis/backends/oracle/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,6 @@ def visit_WindowFunction(self, op, *, how, func, start, end, group_by, order_by)
def visit_StringConcat(self, op, *, arg):
any_args_null = (a.is_(NULL) for a in arg)
return self.if_(sg.or_(*any_args_null), NULL, self.f.concat(*arg))

def visit_ExtractIsoYear(self, op, *, arg):
return self.cast(self.f.to_char(arg, "IYYY"), op.dtype)
1 change: 1 addition & 0 deletions ibis/backends/pandas/kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ def wrapper(*args, **kwargs):
ops.ExtractSecond: lambda arg: arg.dt.second,
ops.ExtractWeekOfYear: lambda arg: arg.dt.isocalendar().week.astype("int32"),
ops.ExtractYear: lambda arg: arg.dt.year,
ops.ExtractIsoYear: lambda arg: arg.dt.isocalendar().year,
ops.IsNull: lambda arg: arg.isnull(),
ops.NotNull: lambda arg: arg.notnull(),
ops.Lowercase: lambda arg: arg.str.lower(),
Expand Down
1 change: 1 addition & 0 deletions ibis/backends/polars/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,7 @@ def array_flatten(op, **kw):
ops.ExtractDay: "day",
ops.ExtractMonth: "month",
ops.ExtractYear: "year",
ops.ExtractIsoYear: "iso_year",
ops.ExtractQuarter: "quarter",
ops.ExtractDayOfYear: "ordinal_day",
ops.ExtractWeekOfYear: "week",
Expand Down
3 changes: 3 additions & 0 deletions ibis/backends/postgres/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,9 @@ def visit_ExtractDayOfYear(self, op, *, arg):
def visit_ExtractWeekOfYear(self, op, *, arg):
return self.f.extract("week", arg)

def visit_ExtractIsoYear(self, op, *, arg):
return self.f.extract("isoyear", arg)

def visit_ExtractEpochSeconds(self, op, *, arg):
return self.f.extract("epoch", arg)

Expand Down
1 change: 1 addition & 0 deletions ibis/backends/snowflake/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class SnowflakeCompiler(SQLGlotCompiler):
ops.BitwiseRightShift: "bitshiftright",
ops.BitwiseXor: "bitxor",
ops.EndsWith: "endswith",
ops.ExtractIsoYear: "yearofweekiso",
ops.Hash: "hash",
ops.Median: "median",
ops.Mode: "mode",
Expand Down
40 changes: 40 additions & 0 deletions ibis/backends/tests/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pandas as pd
import pytest
import sqlglot as sg
import toolz
from pytest import param

import ibis
Expand Down Expand Up @@ -99,6 +100,45 @@ def test_timestamp_extract(backend, alltypes, df, attr):
backend.assert_series_equal(result, expected)


@pytest.mark.parametrize(
"transform", [toolz.identity, methodcaller("date")], ids=["timestamp", "date"]
)
@pytest.mark.notimpl(
["druid"],
raises=(AttributeError, com.OperationNotDefinedError),
reason="AttributeError: 'StringColumn' object has no attribute 'X'",
)
@pytest.mark.notyet(
["mysql", "sqlite", "mssql", "impala", "datafusion", "pyspark", "flink"],
raises=com.OperationNotDefinedError,
reason="backend doesn't appear to support this operation directly",
)
def test_extract_iso_year(backend, con, alltypes, df, transform):
value = transform(alltypes.timestamp_col)
name = "iso_year"
expr = value.iso_year().name(name)
result = expr.execute()
expected = backend.default_series_rename(
df.timestamp_col.dt.isocalendar().year.astype("int32")
).rename(name)
backend.assert_series_equal(result, expected)


@pytest.mark.notimpl(
["druid"],
raises=(AttributeError, com.OperationNotDefinedError),
reason="AttributeError: 'StringColumn' object has no attribute 'X'",
)
@pytest.mark.notyet(
["mysql", "sqlite", "mssql", "impala", "datafusion", "pyspark", "flink"],
raises=com.OperationNotDefinedError,
reason="backend doesn't appear to support this operation directly",
)
def test_iso_year_does_not_match_date_year(con):
expr = ibis.date("2022-01-01").iso_year()
assert con.execute(expr) == 2021


@pytest.mark.parametrize(
("func", "expected"),
[
Expand Down
1 change: 1 addition & 0 deletions ibis/backends/trino/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class TrinoCompiler(SQLGlotCompiler):
ops.ExtractPath: "url_extract_path",
ops.ExtractFragment: "url_extract_fragment",
ops.ArrayPosition: "array_position",
ops.ExtractIsoYear: "year_of_week",
}

def _aggregate(self, funcname: str, *args, where):
Expand Down
5 changes: 5 additions & 0 deletions ibis/expr/operations/temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ class ExtractYear(ExtractDateField):
pass


@public
class ExtractIsoYear(ExtractDateField):
pass


@public
class ExtractMonth(ExtractDateField):
pass
Expand Down
4 changes: 4 additions & 0 deletions ibis/expr/types/temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def year(self) -> ir.IntegerValue:
"""Extract the year component."""
return ops.ExtractYear(self).to_expr()

def iso_year(self) -> ir.IntegerValue:
"""Extract the ISO year component."""
return ops.ExtractIsoYear(self).to_expr()

def month(self) -> ir.IntegerValue:
"""Extract the month component."""
return ops.ExtractMonth(self).to_expr()
Expand Down

0 comments on commit 4707c44

Please sign in to comment.