Skip to content

Commit 0113a6e

Browse files
timsaucerclaude
andauthored
Add missing datetime functions (#1467)
* Add missing datetime functions: make_time, current_timestamp, date_format Closes #1451. Adds make_time Rust binding and Python wrapper, and adds current_timestamp (alias for now) and date_format (alias for to_char) Python functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add unit tests for make_time, current_timestamp, and date_format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2499409 commit 0113a6e

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

crates/core/src/functions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@ expr_fn!(date_part, part date);
616616
expr_fn!(date_trunc, part date);
617617
expr_fn!(date_bin, stride source origin);
618618
expr_fn!(make_date, year month day);
619+
expr_fn!(make_time, hour minute second);
619620
expr_fn!(to_char, datetime format);
620621

621622
expr_fn!(translate, string from to, "Replaces each character in string that matches a character in the from set with the corresponding character in the to set. If from is longer than to, occurrences of the extra characters in from are deleted.");
@@ -974,6 +975,7 @@ pub(crate) fn init_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
974975
m.add_wrapped(wrap_pyfunction!(date_part))?;
975976
m.add_wrapped(wrap_pyfunction!(date_trunc))?;
976977
m.add_wrapped(wrap_pyfunction!(make_date))?;
978+
m.add_wrapped(wrap_pyfunction!(make_time))?;
977979
m.add_wrapped(wrap_pyfunction!(digest))?;
978980
m.add_wrapped(wrap_pyfunction!(ends_with))?;
979981
m.add_wrapped(wrap_pyfunction!(exp))?;

python/datafusion/functions.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@
128128
"cume_dist",
129129
"current_date",
130130
"current_time",
131+
"current_timestamp",
131132
"date_bin",
133+
"date_format",
132134
"date_part",
133135
"date_trunc",
134136
"datepart",
@@ -200,6 +202,7 @@
200202
"make_array",
201203
"make_date",
202204
"make_list",
205+
"make_time",
203206
"max",
204207
"md5",
205208
"mean",
@@ -1948,6 +1951,15 @@ def now() -> Expr:
19481951
return Expr(f.now())
19491952

19501953

1954+
def current_timestamp() -> Expr:
1955+
"""Returns the current timestamp in nanoseconds.
1956+
1957+
See Also:
1958+
This is an alias for :py:func:`now`.
1959+
"""
1960+
return now()
1961+
1962+
19511963
def to_char(arg: Expr, formatter: Expr) -> Expr:
19521964
"""Returns a string representation of a date, time, timestamp or duration.
19531965
@@ -1970,6 +1982,15 @@ def to_char(arg: Expr, formatter: Expr) -> Expr:
19701982
return Expr(f.to_char(arg.expr, formatter.expr))
19711983

19721984

1985+
def date_format(arg: Expr, formatter: Expr) -> Expr:
1986+
"""Returns a string representation of a date, time, timestamp or duration.
1987+
1988+
See Also:
1989+
This is an alias for :py:func:`to_char`.
1990+
"""
1991+
return to_char(arg, formatter)
1992+
1993+
19731994
def _unwrap_exprs(args: tuple[Expr, ...]) -> list:
19741995
return [arg.expr for arg in args]
19751996

@@ -2270,6 +2291,21 @@ def make_date(year: Expr, month: Expr, day: Expr) -> Expr:
22702291
return Expr(f.make_date(year.expr, month.expr, day.expr))
22712292

22722293

2294+
def make_time(hour: Expr, minute: Expr, second: Expr) -> Expr:
2295+
"""Make a time from hour, minute and second component parts.
2296+
2297+
Examples:
2298+
>>> ctx = dfn.SessionContext()
2299+
>>> df = ctx.from_pydict({"h": [12], "m": [30], "s": [0]})
2300+
>>> result = df.select(
2301+
... dfn.functions.make_time(dfn.col("h"), dfn.col("m"),
2302+
... dfn.col("s")).alias("t"))
2303+
>>> result.collect_column("t")[0].as_py()
2304+
datetime.time(12, 30)
2305+
"""
2306+
return Expr(f.make_time(hour.expr, minute.expr, second.expr))
2307+
2308+
22732309
def translate(string: Expr, from_val: Expr, to_val: Expr) -> Expr:
22742310
"""Replaces the characters in ``from_val`` with the counterpart in ``to_val``.
22752311

python/tests/test_functions.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,39 @@ def test_today_alias_matches_current_date(df):
11071107
assert result.column(0) == result.column(1)
11081108

11091109

1110+
def test_current_timestamp_alias_matches_now(df):
1111+
result = df.select(
1112+
f.now().alias("now"),
1113+
f.current_timestamp().alias("current_timestamp"),
1114+
).collect()[0]
1115+
1116+
assert result.column(0) == result.column(1)
1117+
1118+
1119+
def test_date_format_alias_matches_to_char(df):
1120+
result = df.select(
1121+
f.to_char(
1122+
f.to_timestamp(literal("2021-01-01T00:00:00")), literal("%Y/%m/%d")
1123+
).alias("to_char"),
1124+
f.date_format(
1125+
f.to_timestamp(literal("2021-01-01T00:00:00")), literal("%Y/%m/%d")
1126+
).alias("date_format"),
1127+
).collect()[0]
1128+
1129+
assert result.column(0) == result.column(1)
1130+
assert result.column(0)[0].as_py() == "2021/01/01"
1131+
1132+
1133+
def test_make_time(df):
1134+
ctx = SessionContext()
1135+
df_time = ctx.from_pydict({"h": [12], "m": [30], "s": [0]})
1136+
result = df_time.select(
1137+
f.make_time(column("h"), column("m"), column("s")).alias("t")
1138+
).collect()[0]
1139+
1140+
assert result.column(0)[0].as_py() == time(12, 30)
1141+
1142+
11101143
def test_arrow_cast(df):
11111144
df = df.select(
11121145
# we use `string_literal` to return utf8 instead of `literal` which returns

0 commit comments

Comments
 (0)