Skip to content

Commit 2cd4e78

Browse files
authored
feat: support datetime objects in literal instantiation (#1542)
* feat: support datetime objects in literal instantiation * add integration test * proper tests * fix typing
1 parent 9d5638c commit 2cd4e78

File tree

4 files changed

+38
-1
lines changed

4 files changed

+38
-1
lines changed

pyiceberg/expressions/literals.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import struct
2525
from abc import ABC, abstractmethod
26+
from datetime import datetime
2627
from decimal import ROUND_HALF_UP, Decimal
2728
from functools import singledispatchmethod
2829
from math import isnan
@@ -49,6 +50,7 @@
4950
)
5051
from pyiceberg.utils.datetime import (
5152
date_str_to_days,
53+
datetime_to_micros,
5254
micros_to_days,
5355
time_str_to_micros,
5456
timestamp_to_micros,
@@ -145,6 +147,8 @@ def literal(value: L) -> Literal[L]:
145147
return BinaryLiteral(value)
146148
elif isinstance(value, Decimal):
147149
return DecimalLiteral(value)
150+
elif isinstance(value, datetime):
151+
return TimestampLiteral(datetime_to_micros(value)) # type: ignore
148152
else:
149153
raise TypeError(f"Invalid literal value: {repr(value)}")
150154

pyiceberg/typedef.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import annotations
1818

1919
from abc import abstractmethod
20+
from datetime import datetime
2021
from decimal import Decimal
2122
from functools import lru_cache
2223
from typing import (
@@ -78,7 +79,7 @@ def __missing__(self, key: K) -> V:
7879
RecursiveDict = Dict[str, Union[str, "RecursiveDict"]]
7980

8081
# Represents the literal value
81-
L = TypeVar("L", str, bool, int, float, bytes, UUID, Decimal, covariant=True)
82+
L = TypeVar("L", str, bool, int, float, bytes, UUID, Decimal, datetime, covariant=True)
8283

8384

8485
@runtime_checkable

tests/expressions/test_literals.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ def test_uuid_to_binary() -> None:
906906
assert isinstance(binary_literal, BinaryLiteral) # type: ignore
907907

908908

909+
def test_literal_from_datetime() -> None:
910+
assert isinstance(literal(datetime.datetime.now()), TimestampLiteral)
911+
912+
909913
# __ __ ___
910914
# | \/ |_ _| _ \_ _
911915
# | |\/| | || | _/ || |

tests/integration/test_reads.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import math
2020
import time
2121
import uuid
22+
from datetime import datetime, timedelta
2223
from pathlib import PosixPath
2324
from urllib.parse import urlparse
2425

@@ -950,3 +951,30 @@ def test_read_from_s3_and_local_fs(catalog: Catalog, tmp_path: PosixPath) -> Non
950951

951952
result_table = tbl.scan().to_arrow()
952953
assert result_table["colA"].to_pylist() == ["one", "one"]
954+
955+
956+
@pytest.mark.integration
957+
@pytest.mark.parametrize("catalog", [pytest.lazy_fixture("session_catalog_hive"), pytest.lazy_fixture("session_catalog")])
958+
def test_scan_with_datetime(catalog: Catalog) -> None:
959+
table = create_table(catalog)
960+
961+
yesterday = datetime.now() - timedelta(days=1)
962+
table.append(
963+
pa.Table.from_pylist(
964+
[
965+
{
966+
"str": "foo",
967+
"int": 1,
968+
"bool": True,
969+
"datetime": yesterday,
970+
}
971+
],
972+
schema=table.schema().as_arrow(),
973+
),
974+
)
975+
976+
df = table.scan(row_filter=GreaterThanOrEqual("datetime", yesterday)).to_pandas()
977+
assert len(df) == 1
978+
979+
df = table.scan(row_filter=LessThan("datetime", yesterday)).to_pandas()
980+
assert len(df) == 0

0 commit comments

Comments
 (0)