common_function/scalars/date/
date_format.rs1use std::fmt;
16use std::sync::Arc;
17
18use common_error::ext::BoxedError;
19use common_query::error;
20use common_time::{Date, Timestamp};
21use datafusion_common::DataFusionError;
22use datafusion_common::arrow::array::{Array, AsArray, StringViewBuilder};
23use datafusion_common::arrow::datatypes::{ArrowTimestampType, DataType, Date32Type, TimeUnit};
24use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, Signature};
25use snafu::ResultExt;
26
27use crate::function::{Function, extract_args, find_function_context};
28use crate::helper;
29use crate::helper::with_match_timestamp_types;
30
31#[derive(Clone, Debug)]
33pub(crate) struct DateFormatFunction {
34 signature: Signature,
35}
36
37impl Default for DateFormatFunction {
38 fn default() -> Self {
39 Self {
40 signature: helper::one_of_sigs2(
41 vec![
42 DataType::Date32,
43 DataType::Timestamp(TimeUnit::Second, None),
44 DataType::Timestamp(TimeUnit::Millisecond, None),
45 DataType::Timestamp(TimeUnit::Microsecond, None),
46 DataType::Timestamp(TimeUnit::Nanosecond, None),
47 ],
48 vec![DataType::Utf8],
49 ),
50 }
51 }
52}
53
54impl Function for DateFormatFunction {
55 fn name(&self) -> &str {
56 "date_format"
57 }
58
59 fn return_type(&self, _: &[DataType]) -> datafusion_common::Result<DataType> {
60 Ok(DataType::Utf8View)
61 }
62
63 fn signature(&self) -> &Signature {
64 &self.signature
65 }
66
67 fn invoke_with_args(
68 &self,
69 args: ScalarFunctionArgs,
70 ) -> datafusion_common::Result<ColumnarValue> {
71 let ctx = find_function_context(&args)?;
72 let timezone = &ctx.query_ctx.timezone();
73
74 let [left, arg1] = extract_args(self.name(), &args)?;
75 let formats = arg1.as_string::<i32>();
76
77 let size = left.len();
78 let left_datatype = left.data_type();
79 let mut builder = StringViewBuilder::with_capacity(size);
80
81 match left_datatype {
82 DataType::Timestamp(_, _) => {
83 with_match_timestamp_types!(left_datatype, |$S| {
84 let array = left.as_primitive::<$S>();
85 for (date, format) in array.iter().zip(formats.iter()) {
86 let result = match (date, format) {
87 (Some(date), Some(format)) => {
88 let ts = Timestamp::new(date, $S::UNIT.into());
89 let x = ts.as_formatted_string(&format, Some(timezone))
90 .map_err(|e| DataFusionError::Execution(format!(
91 "cannot format {ts:?} as '{format}': {e}"
92 )))?;
93 Some(x)
94 }
95 _ => None
96 };
97 builder.append_option(result.as_deref());
98 }
99 })?;
100 }
101 DataType::Date32 => {
102 let left = left.as_primitive::<Date32Type>();
103 for i in 0..size {
104 let date = left.is_valid(i).then(|| Date::from(left.value(i)));
105 let format = formats.is_valid(i).then(|| formats.value(i));
106
107 let result = match (date, format) {
108 (Some(date), Some(fmt)) => date
109 .as_formatted_string(fmt, Some(timezone))
110 .map_err(BoxedError::new)
111 .context(error::ExecuteSnafu)?,
112 _ => None,
113 };
114
115 builder.append_option(result.as_deref());
116 }
117 }
118 x => {
119 return Err(DataFusionError::Execution(format!(
120 "unsupported input data type {x}"
121 )));
122 }
123 }
124
125 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
126 }
127}
128
129impl fmt::Display for DateFormatFunction {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 write!(f, "DATE_FORMAT")
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use std::sync::Arc;
138
139 use arrow_schema::Field;
140 use datafusion_common::arrow::array::{Date32Array, StringArray, TimestampSecondArray};
141 use datafusion_common::config::ConfigOptions;
142 use datafusion_expr::{TypeSignature, Volatility};
143
144 use super::{DateFormatFunction, *};
145 use crate::function::FunctionContext;
146
147 #[test]
148 fn test_date_format_misc() {
149 let f = DateFormatFunction::default();
150 assert_eq!("date_format", f.name());
151 assert_eq!(
152 DataType::Utf8View,
153 f.return_type(&[DataType::Timestamp(TimeUnit::Microsecond, None)])
154 .unwrap()
155 );
156 assert_eq!(
157 DataType::Utf8View,
158 f.return_type(&[DataType::Timestamp(TimeUnit::Second, None)])
159 .unwrap()
160 );
161 assert_eq!(
162 DataType::Utf8View,
163 f.return_type(&[DataType::Date32]).unwrap()
164 );
165 assert!(matches!(f.signature(),
166 Signature {
167 type_signature: TypeSignature::OneOf(sigs),
168 volatility: Volatility::Immutable
169 } if sigs.len() == 5));
170 }
171
172 #[test]
173 fn test_timestamp_date_format() {
174 let f = DateFormatFunction::default();
175
176 let times = vec![Some(123), None, Some(42), None];
177 let formats = vec![
178 "%Y-%m-%d %T.%3f",
179 "%Y-%m-%d %T.%3f",
180 "%Y-%m-%d %T.%3f",
181 "%Y-%m-%d %T.%3f",
182 ];
183 let results = [
184 Some("1970-01-01 00:02:03.000"),
185 None,
186 Some("1970-01-01 00:00:42.000"),
187 None,
188 ];
189
190 let mut config_options = ConfigOptions::default();
191 config_options.extensions.insert(FunctionContext::default());
192 let config_options = Arc::new(config_options);
193
194 let args = ScalarFunctionArgs {
195 args: vec![
196 ColumnarValue::Array(Arc::new(TimestampSecondArray::from(times))),
197 ColumnarValue::Array(Arc::new(StringArray::from_iter_values(formats))),
198 ],
199 arg_fields: vec![],
200 number_rows: 4,
201 return_field: Arc::new(Field::new("x", DataType::Utf8View, false)),
202 config_options,
203 };
204 let result = f
205 .invoke_with_args(args)
206 .and_then(|x| x.to_array(4))
207 .unwrap();
208 let vector = result.as_string_view();
209
210 assert_eq!(4, vector.len());
211 for (actual, expect) in vector.iter().zip(results) {
212 assert_eq!(actual, expect);
213 }
214 }
215
216 #[test]
217 fn test_date_date_format() {
218 let f = DateFormatFunction::default();
219
220 let dates = vec![Some(123), None, Some(42), None];
221 let formats = vec![
222 "%Y-%m-%d %T.%3f",
223 "%Y-%m-%d %T.%3f",
224 "%Y-%m-%d %T.%3f",
225 "%Y-%m-%d %T.%3f",
226 ];
227 let results = [
228 Some("1970-05-04 00:00:00.000"),
229 None,
230 Some("1970-02-12 00:00:00.000"),
231 None,
232 ];
233
234 let mut config_options = ConfigOptions::default();
235 config_options.extensions.insert(FunctionContext::default());
236 let config_options = Arc::new(config_options);
237
238 let args = ScalarFunctionArgs {
239 args: vec![
240 ColumnarValue::Array(Arc::new(Date32Array::from(dates))),
241 ColumnarValue::Array(Arc::new(StringArray::from_iter_values(formats))),
242 ],
243 arg_fields: vec![],
244 number_rows: 4,
245 return_field: Arc::new(Field::new("x", DataType::Utf8View, false)),
246 config_options,
247 };
248 let result = f
249 .invoke_with_args(args)
250 .and_then(|x| x.to_array(4))
251 .unwrap();
252 let vector = result.as_string_view();
253
254 assert_eq!(4, vector.len());
255 for (actual, expect) in vector.iter().zip(results) {
256 assert_eq!(actual, expect);
257 }
258 }
259}