datafusion_functions/datetime/
from_unixtime.rs1use std::any::Any;
19use std::sync::Arc;
20
21use arrow::datatypes::DataType::{Int64, Timestamp, Utf8};
22use arrow::datatypes::TimeUnit::Second;
23use arrow::datatypes::{DataType, Field, FieldRef};
24use datafusion_common::{exec_err, internal_err, Result, ScalarValue};
25use datafusion_expr::TypeSignature::Exact;
26use datafusion_expr::{
27 ColumnarValue, Documentation, ReturnFieldArgs, ScalarUDFImpl, Signature, Volatility,
28};
29use datafusion_macros::user_doc;
30
31#[user_doc(
32 doc_section(label = "Time and Date Functions"),
33 description = "Converts an integer to RFC3339 timestamp format (`YYYY-MM-DDT00:00:00.000000000Z`). Integers and unsigned integers are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`) return the corresponding timestamp.",
34 syntax_example = "from_unixtime(expression[, timezone])",
35 sql_example = r#"```sql
36> select from_unixtime(1599572549, 'America/New_York');
37+-----------------------------------------------------------+
38| from_unixtime(Int64(1599572549),Utf8("America/New_York")) |
39+-----------------------------------------------------------+
40| 2020-09-08T09:42:29-04:00 |
41+-----------------------------------------------------------+
42```"#,
43 standard_argument(name = "expression",),
44 argument(
45 name = "timezone",
46 description = "Optional timezone to use when converting the integer to a timestamp. If not provided, the default timezone is UTC."
47 )
48)]
49#[derive(Debug)]
50pub struct FromUnixtimeFunc {
51 signature: Signature,
52}
53
54impl Default for FromUnixtimeFunc {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60impl FromUnixtimeFunc {
61 pub fn new() -> Self {
62 Self {
63 signature: Signature::one_of(
64 vec![Exact(vec![Int64, Utf8]), Exact(vec![Int64])],
65 Volatility::Immutable,
66 ),
67 }
68 }
69}
70
71impl ScalarUDFImpl for FromUnixtimeFunc {
72 fn as_any(&self) -> &dyn Any {
73 self
74 }
75
76 fn name(&self) -> &str {
77 "from_unixtime"
78 }
79
80 fn signature(&self) -> &Signature {
81 &self.signature
82 }
83
84 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
85 debug_assert!(matches!(args.scalar_arguments.len(), 1 | 2));
87
88 if args.scalar_arguments.len() == 1 {
89 Ok(Field::new(self.name(), Timestamp(Second, None), true).into())
90 } else {
91 args.scalar_arguments[1]
92 .and_then(|sv| {
93 sv.try_as_str()
94 .flatten()
95 .filter(|s| !s.is_empty())
96 .map(|tz| {
97 Field::new(
98 self.name(),
99 Timestamp(Second, Some(Arc::from(tz.to_string()))),
100 true,
101 )
102 })
103 })
104 .map(Arc::new)
105 .map_or_else(
106 || {
107 exec_err!(
108 "{} requires its second argument to be a constant string",
109 self.name()
110 )
111 },
112 Ok,
113 )
114 }
115 }
116
117 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
118 internal_err!("call return_field_from_args instead")
119 }
120
121 fn invoke_with_args(
122 &self,
123 args: datafusion_expr::ScalarFunctionArgs,
124 ) -> Result<ColumnarValue> {
125 let args = args.args;
126 let len = args.len();
127 if len != 1 && len != 2 {
128 return exec_err!(
129 "from_unixtime function requires 1 or 2 argument, got {}",
130 args.len()
131 );
132 }
133
134 if args[0].data_type() != Int64 {
135 return exec_err!(
136 "Unsupported data type {:?} for function from_unixtime",
137 args[0].data_type()
138 );
139 }
140
141 match len {
142 1 => args[0].cast_to(&Timestamp(Second, None), None),
143 2 => match &args[1] {
144 ColumnarValue::Scalar(ScalarValue::Utf8(Some(tz))) => args[0]
145 .cast_to(&Timestamp(Second, Some(Arc::from(tz.to_string()))), None),
146 _ => {
147 exec_err!(
148 "Unsupported data type {:?} for function from_unixtime",
149 args[1].data_type()
150 )
151 }
152 },
153 _ => unreachable!(),
154 }
155 }
156
157 fn documentation(&self) -> Option<&Documentation> {
158 self.doc()
159 }
160}
161
162#[cfg(test)]
163mod test {
164 use crate::datetime::from_unixtime::FromUnixtimeFunc;
165 use arrow::datatypes::TimeUnit::Second;
166 use arrow::datatypes::{DataType, Field};
167 use datafusion_common::ScalarValue;
168 use datafusion_common::ScalarValue::Int64;
169 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
170 use std::sync::Arc;
171
172 #[test]
173 fn test_without_timezone() {
174 let arg_field = Arc::new(Field::new("a", DataType::Int64, true));
175 let args = datafusion_expr::ScalarFunctionArgs {
176 args: vec![ColumnarValue::Scalar(Int64(Some(1729900800)))],
177 arg_fields: vec![arg_field],
178 number_rows: 1,
179 return_field: Field::new("f", DataType::Timestamp(Second, None), true).into(),
180 };
181 let result = FromUnixtimeFunc::new().invoke_with_args(args).unwrap();
182
183 match result {
184 ColumnarValue::Scalar(ScalarValue::TimestampSecond(Some(sec), None)) => {
185 assert_eq!(sec, 1729900800);
186 }
187 _ => panic!("Expected scalar value"),
188 }
189 }
190
191 #[test]
192 fn test_with_timezone() {
193 let arg_fields = vec![
194 Field::new("a", DataType::Int64, true).into(),
195 Field::new("a", DataType::Utf8, true).into(),
196 ];
197 let args = datafusion_expr::ScalarFunctionArgs {
198 args: vec![
199 ColumnarValue::Scalar(Int64(Some(1729900800))),
200 ColumnarValue::Scalar(ScalarValue::Utf8(Some(
201 "America/New_York".to_string(),
202 ))),
203 ],
204 arg_fields,
205 number_rows: 2,
206 return_field: Field::new(
207 "f",
208 DataType::Timestamp(Second, Some(Arc::from("America/New_York"))),
209 true,
210 )
211 .into(),
212 };
213 let result = FromUnixtimeFunc::new().invoke_with_args(args).unwrap();
214
215 match result {
216 ColumnarValue::Scalar(ScalarValue::TimestampSecond(Some(sec), Some(tz))) => {
217 assert_eq!(sec, 1729900800);
218 assert_eq!(tz.to_string(), "America/New_York");
219 }
220 _ => panic!("Expected scalar value"),
221 }
222 }
223}