datafusion_functions/datetime/
make_date.rs1use std::sync::Arc;
19
20use arrow::array::builder::PrimitiveBuilder;
21use arrow::array::cast::AsArray;
22use arrow::array::types::{Date32Type, Int32Type};
23use arrow::array::{Array, PrimitiveArray};
24use arrow::datatypes::DataType;
25use arrow::datatypes::DataType::Date32;
26use chrono::prelude::*;
27
28use datafusion_common::types::{NativeType, logical_int32, logical_string};
29use datafusion_common::{Result, ScalarValue, exec_err, utils::take_function_args};
30use datafusion_expr::{
31 Coercion, ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
32 TypeSignatureClass, Volatility,
33};
34use datafusion_macros::user_doc;
35
36#[user_doc(
37 doc_section(label = "Time and Date Functions"),
38 description = "Make a date from year/month/day component parts.",
39 syntax_example = "make_date(year, month, day)",
40 sql_example = r#"```sql
41> select make_date(2023, 1, 31);
42+-------------------------------------------+
43| make_date(Int64(2023),Int64(1),Int64(31)) |
44+-------------------------------------------+
45| 2023-01-31 |
46+-------------------------------------------+
47> select make_date('2023', '01', '31');
48+-----------------------------------------------+
49| make_date(Utf8("2023"),Utf8("01"),Utf8("31")) |
50+-----------------------------------------------+
51| 2023-01-31 |
52+-----------------------------------------------+
53```
54
55Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs)
56"#,
57 argument(
58 name = "year",
59 description = "Year to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators."
60 ),
61 argument(
62 name = "month",
63 description = "Month to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators."
64 ),
65 argument(
66 name = "day",
67 description = "Day to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators."
68 )
69)]
70#[derive(Debug, PartialEq, Eq, Hash)]
71pub struct MakeDateFunc {
72 signature: Signature,
73}
74
75impl Default for MakeDateFunc {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl MakeDateFunc {
82 pub fn new() -> Self {
83 let int = Coercion::new_implicit(
84 TypeSignatureClass::Native(logical_int32()),
85 vec![
86 TypeSignatureClass::Integer,
87 TypeSignatureClass::Native(logical_string()),
88 ],
89 NativeType::Int32,
90 );
91 Self {
92 signature: Signature::coercible(vec![int; 3], Volatility::Immutable),
93 }
94 }
95}
96
97impl ScalarUDFImpl for MakeDateFunc {
98 fn name(&self) -> &str {
99 "make_date"
100 }
101
102 fn signature(&self) -> &Signature {
103 &self.signature
104 }
105
106 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
107 Ok(Date32)
108 }
109
110 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
111 let [years, months, days] = take_function_args(self.name(), args.args)?;
112
113 match (years, months, days) {
114 (ColumnarValue::Scalar(y), _, _) if y.is_null() => {
115 Ok(ColumnarValue::Scalar(ScalarValue::Date32(None)))
116 }
117 (_, ColumnarValue::Scalar(m), _) if m.is_null() => {
118 Ok(ColumnarValue::Scalar(ScalarValue::Date32(None)))
119 }
120 (_, _, ColumnarValue::Scalar(d)) if d.is_null() => {
121 Ok(ColumnarValue::Scalar(ScalarValue::Date32(None)))
122 }
123 (
124 ColumnarValue::Scalar(ScalarValue::Int32(Some(years))),
125 ColumnarValue::Scalar(ScalarValue::Int32(Some(months))),
126 ColumnarValue::Scalar(ScalarValue::Int32(Some(days))),
127 ) => {
128 let mut value = 0;
129 make_date_inner(years, months, days, |days: i32| value = days)?;
130 Ok(ColumnarValue::Scalar(ScalarValue::Date32(Some(value))))
131 }
132 (years, months, days) => {
133 let len = args.number_rows;
134 let years = years.into_array(len)?;
135 let months = months.into_array(len)?;
136 let days = days.into_array(len)?;
137
138 let years = years.as_primitive::<Int32Type>();
139 let months = months.as_primitive::<Int32Type>();
140 let days = days.as_primitive::<Int32Type>();
141
142 let mut builder: PrimitiveBuilder<Date32Type> =
143 PrimitiveArray::builder(len);
144
145 for i in 0..len {
146 if years.is_null(i) || months.is_null(i) || days.is_null(i) {
148 builder.append_null();
149 } else {
150 make_date_inner(
151 years.value(i),
152 months.value(i),
153 days.value(i),
154 |days: i32| builder.append_value(days),
155 )?;
156 }
157 }
158
159 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
160 }
161 }
162 }
163
164 fn documentation(&self) -> Option<&Documentation> {
165 self.doc()
166 }
167}
168
169fn make_date_inner<F: FnMut(i32)>(
172 year: i32,
173 month: i32,
174 day: i32,
175 mut date_consumer_fn: F,
176) -> Result<()> {
177 let m = match month {
178 1..=12 => month as u32,
179 _ => return exec_err!("Month value '{month:?}' is out of range"),
180 };
181 let d = match day {
182 1..=31 => day as u32,
183 _ => return exec_err!("Day value '{day:?}' is out of range"),
184 };
185
186 if let Some(date) = NaiveDate::from_ymd_opt(year, m, d) {
187 const UNIX_DAYS_FROM_CE: i32 = 719_163;
190
191 date_consumer_fn(date.num_days_from_ce() - UNIX_DAYS_FROM_CE);
195 Ok(())
196 } else {
197 exec_err!("Unable to parse date from {year}, {month}, {day}")
198 }
199}