Skip to content

Commit

Permalink
feat(date): add ibis.date(y,m,d) functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Saul Pwanson committed Mar 9, 2022
1 parent 88e81a3 commit 2d9a128
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 1 deletion.
15 changes: 15 additions & 0 deletions ibis/backends/sqlite/registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sqlalchemy as sa
import toolz
from multipledispatch import Dispatcher
import functools

import ibis
import ibis.common.exceptions as com
Expand Down Expand Up @@ -252,9 +253,22 @@ def _rpad(t, expr):
return arg + _generic_pad(arg, length, pad)


def _date_from_ymd(t, expr):
y, m, d = map(t.translate, expr.op().args)
ymdstr = sa.func.printf('%04d-%02d-%02d', y, m, d)
return sa.func.date(ymdstr)


def _string_concat(t, expr):
return functools.reduce(
lambda x, y: x.concat(y), map(t.translate, expr.op().args[0])
)


operation_registry.update(
{
ops.Cast: _cast,
ops.DateFromYMD: _date_from_ymd,
ops.Substring: _substr,
ops.StrRight: _string_right,
ops.StringFind: _string_find,
Expand All @@ -265,6 +279,7 @@ def _rpad(t, expr):
ops.Date: unary(sa.func.date),
ops.TimestampTruncate: _truncate(sa.func.datetime),
ops.Strftime: _strftime,
ops.StringConcat: _string_concat,
ops.ExtractYear: _strftime_int('%Y'),
ops.ExtractMonth: _strftime_int('%m'),
ops.ExtractDay: _strftime_int('%d'),
Expand Down
46 changes: 46 additions & 0 deletions ibis/backends/tests/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,49 @@ def test_now_from_projection(backend, alltypes):
now = pd.Timestamp('now')
year_expected = pd.Series([now.year] * n, name='ts')
tm.assert_series_equal(ts.dt.year, year_expected)


def test_date_literal_ex(con):
expr = ibis.date(2022, 2, 4)
result = con.execute(expr)
assert result.strftime('%Y-%m-%d') == '2022-02-04'


def test_date_column_from_ymd(con, alltypes, df):
c = alltypes.timestamp_col
expr = ibis.date(c.year(), c.month(), c.day())
tbl = alltypes[
expr.name('timestamp_col'),
]
result = con.execute(tbl)

golden = df.timestamp_col.dt.date.astype('datetime64[ns]')
tm.assert_series_equal(golden, result.timestamp_col)


def test_date_scalar_from_iso(con):
expr = ibis.literal('2022-02-24')
expr2 = ibis.date(expr)

result = con.execute(expr2)
assert result.strftime('%Y-%m-%d') == '2022-02-24'


def test_date_column_from_iso(con, alltypes, df):
expr = (
alltypes.year.cast('string')
+ '-'
+ alltypes.month.cast('string').lpad(2, '0')
+ '-13'
)
expr = ibis.date(expr)

result = con.execute(expr)
golden = (
df.year.astype(str)
+ '-'
+ df.month.astype(str).str.rjust(2, '0')
+ '-13'
)
actual = result.dt.strftime('%Y-%m-%d')
tm.assert_series_equal(golden.rename('tmp'), actual)
19 changes: 18 additions & 1 deletion ibis/expr/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,13 @@ def timestamp(
return literal(value, type=dt.Timestamp(timezone=timezone))


def date(value: str) -> ir.DateScalar:
@functools.singledispatch
def date(value) -> DateValue:
raise Exception('notimpl')


@date.register(str)
def _(value: str) -> ir.DateScalar:
"""Return a date literal if `value` is coercible to a date.
Parameters
Expand All @@ -399,6 +405,17 @@ def date(value: str) -> ir.DateScalar:
return literal(value, type=dt.date)


@date.register(IntegerColumn)
@date.register(int)
def _(year, month, day) -> ir.DateScalar:
return ops.DateFromYMD(year, month, day).to_expr()


@date.register(StringValue)
def _(value: StringValue) -> DateValue:
return value.cast(dt.date)


def time(value: str) -> ir.TimeScalar:
"""Return a time literal if `value` is coercible to a time.
Expand Down
8 changes: 8 additions & 0 deletions ibis/expr/operations/temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ class Date(UnaryOp):
output_type = rlz.shape_like('arg', dt.date)


@public
class DateFromYMD(ValueOp):
year = rlz.integer
month = rlz.integer
day = rlz.integer
output_type = rlz.shape_like('args', dt.date)


@public
class TimestampFromUNIX(ValueOp):
arg = rlz.any
Expand Down
4 changes: 4 additions & 0 deletions ibis/tests/expr/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,3 +705,7 @@ def test_time_truncate(table, operand, unit):
expr = operand(table).truncate(unit)
assert isinstance(expr, ir.TimeValue)
assert isinstance(expr.op(), ops.TimeTruncate)


def test_date_literal():
ibis.date(2022, 2, 4)

0 comments on commit 2d9a128

Please sign in to comment.