Skip to content

Commit aa58cd8

Browse files
committed
fix(polars): correctly cast strings to time values
## Description of changes Parse strings with `str.strptime(pl.Time, "%H:%M:%S%.f")` when casting to time. Polars 2.0 removed string-to-time casts, and on Polars 1.x the plain cast raised on valid input while `try_cast` returned all nulls. The explicit format keeps `try_cast` from raising when no value parses on Polars 1.x. Add `string_to_time` cases to `test_cast`, `test_try_cast`, and `test_try_cast_null`. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent d715662 commit aa58cd8

2 files changed

Lines changed: 43 additions & 2 deletions

File tree

‎ibis/backends/polars/compiler.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ def _cast(op, strict=True, **kw):
167167
if time_unit == "s":
168168
return arg.dt.truncate("1s")
169169
return arg
170+
elif to.is_time() and dtype.is_string():
171+
return arg.str.strptime(pl.Time, "%H:%M:%S%.f", strict=strict)
170172

171173
typ = PolarsType.from_ibis(to)
172174
return arg.cast(typ, strict=strict)

‎ibis/backends/tests/test_generic.py‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,29 @@ def hash_256(col):
18541854
param("float", "int", 0.0, 0, id="float_to_int"),
18551855
param("string", "int", "0", 0, id="string_to_int"),
18561856
param("string", "float", "0", 0.0, id="string_to_float"),
1857+
param(
1858+
"string",
1859+
"time",
1860+
"01:02:03",
1861+
datetime.time(1, 2, 3),
1862+
marks=[
1863+
pytest.mark.notimpl(
1864+
["sqlite"],
1865+
raises=AssertionError,
1866+
reason="SQLite returns Timedelta from execution",
1867+
),
1868+
pytest.mark.notyet(
1869+
["clickhouse", "impala"],
1870+
raises=AssertionError,
1871+
reason="backend doesn't have a time datatype",
1872+
),
1873+
pytest.mark.notyet(["druid"], raises=PyDruidProgrammingError),
1874+
pytest.mark.notyet(["exasol"], raises=ExaQueryError),
1875+
pytest.mark.notimpl(["pyspark"], raises=AttributeError),
1876+
pytest.mark.notyet(["oracle"], raises=OracleDatabaseError),
1877+
],
1878+
id="string_to_time",
1879+
),
18571880
param(
18581881
"array<int>",
18591882
"array<string>",
@@ -1915,6 +1938,21 @@ def test_cast(con, from_type, to_type, from_val, expected):
19151938
param(0.0, "int", 0),
19161939
param("0", "int", 0),
19171940
param("0.0", "float", 0.0),
1941+
param(
1942+
"01:02:03",
1943+
"time",
1944+
datetime.time(1, 2, 3),
1945+
marks=[
1946+
pytest.mark.notyet(
1947+
["clickhouse", "impala"],
1948+
raises=AssertionError,
1949+
reason="backend doesn't have a time datatype",
1950+
),
1951+
pytest.mark.notyet(["druid"], raises=PyDruidProgrammingError),
1952+
pytest.mark.notyet(["exasol"], raises=ExaQueryError),
1953+
pytest.mark.notimpl(["pyspark"], raises=AttributeError),
1954+
],
1955+
),
19181956
param(
19191957
datetime.datetime(2023, 1, 1),
19201958
"int",
@@ -1955,7 +1993,6 @@ def test_try_cast(con, from_val, to_type, expected):
19551993
"datafusion",
19561994
"druid",
19571995
"exasol",
1958-
"mysql",
19591996
"singlestoredb",
19601997
"oracle",
19611998
"postgres",
@@ -1971,11 +2008,13 @@ def test_try_cast(con, from_val, to_type, expected):
19712008
@pytest.mark.parametrize(
19722009
("from_val", "to_type"),
19732010
[
1974-
param("a", "int"),
2011+
param("a", "int", marks=pytest.mark.notimpl(["mysql"])),
2012+
param("a", "time"),
19752013
param(
19762014
datetime.datetime(2023, 1, 1),
19772015
"int",
19782016
marks=[
2017+
pytest.mark.notimpl(["mysql"]),
19792018
pytest.mark.never(
19802019
["clickhouse", "pyspark", "flink", "databricks"],
19812020
reason="casts to 1672531200",

0 commit comments

Comments
 (0)