datafusion_functions/datetime/
current_time.rs1use arrow::array::timezone::Tz;
19use arrow::datatypes::DataType;
20use arrow::datatypes::DataType::Time64;
21use arrow::datatypes::TimeUnit::Nanosecond;
22use chrono::TimeZone;
23use chrono::Timelike;
24use datafusion_common::{internal_err, Result, ScalarValue};
25use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
26use datafusion_expr::{
27 ColumnarValue, Documentation, Expr, ScalarUDFImpl, Signature, Volatility,
28};
29use datafusion_macros::user_doc;
30use std::any::Any;
31
32#[user_doc(
33 doc_section(label = "Time and Date Functions"),
34 description = r#"
35Returns the current time in the session time zone.
36
37The `current_time()` return value is determined at query time and will return the same time, no matter when in the query plan the function executes.
38
39The session time zone can be set using the statement 'SET datafusion.execution.time_zone = desired time zone'. The time zone can be a value like +00:00, 'Europe/London' etc.
40"#,
41 syntax_example = r#"current_time()
42 (optional) SET datafusion.execution.time_zone = '+00:00';
43 SELECT current_time();"#
44)]
45#[derive(Debug, PartialEq, Eq, Hash)]
46pub struct CurrentTimeFunc {
47 signature: Signature,
48}
49
50impl Default for CurrentTimeFunc {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl CurrentTimeFunc {
57 pub fn new() -> Self {
58 Self {
59 signature: Signature::nullary(Volatility::Stable),
60 }
61 }
62}
63
64impl ScalarUDFImpl for CurrentTimeFunc {
71 fn as_any(&self) -> &dyn Any {
72 self
73 }
74
75 fn name(&self) -> &str {
76 "current_time"
77 }
78
79 fn signature(&self) -> &Signature {
80 &self.signature
81 }
82
83 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
84 Ok(Time64(Nanosecond))
85 }
86
87 fn invoke_with_args(
88 &self,
89 _args: datafusion_expr::ScalarFunctionArgs,
90 ) -> Result<ColumnarValue> {
91 internal_err!(
92 "invoke should not be called on a simplified current_time() function"
93 )
94 }
95
96 fn simplify(
97 &self,
98 _args: Vec<Expr>,
99 info: &dyn SimplifyInfo,
100 ) -> Result<ExprSimplifyResult> {
101 let now_ts = info.execution_props().query_execution_start_time;
102
103 let nano = info
105 .execution_props()
106 .config_options()
107 .and_then(|config| {
108 config
109 .execution
110 .time_zone
111 .as_ref()
112 .map(|tz| tz.parse::<Tz>().ok())
113 })
114 .flatten()
115 .map_or_else(
116 || datetime_to_time_nanos(&now_ts),
117 |tz| {
118 let local_now = tz.from_utc_datetime(&now_ts.naive_utc());
119 datetime_to_time_nanos(&local_now)
120 },
121 );
122
123 Ok(ExprSimplifyResult::Simplified(Expr::Literal(
124 ScalarValue::Time64Nanosecond(nano),
125 None,
126 )))
127 }
128
129 fn documentation(&self) -> Option<&Documentation> {
130 self.doc()
131 }
132}
133
134fn datetime_to_time_nanos<Tz: TimeZone>(dt: &chrono::DateTime<Tz>) -> Option<i64> {
136 let hour = dt.hour() as i64;
137 let minute = dt.minute() as i64;
138 let second = dt.second() as i64;
139 let nanosecond = dt.nanosecond() as i64;
140 Some((hour * 3600 + minute * 60 + second) * 1_000_000_000 + nanosecond)
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use arrow::datatypes::{DataType, TimeUnit::Nanosecond};
147 use chrono::{DateTime, Utc};
148 use datafusion_common::{Result, ScalarValue};
149 use datafusion_expr::execution_props::ExecutionProps;
150 use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
151 use std::sync::Arc;
152
153 struct MockSimplifyInfo {
154 execution_props: ExecutionProps,
155 }
156
157 impl SimplifyInfo for MockSimplifyInfo {
158 fn is_boolean_type(&self, _expr: &Expr) -> Result<bool> {
159 Ok(false)
160 }
161
162 fn nullable(&self, _expr: &Expr) -> Result<bool> {
163 Ok(true)
164 }
165
166 fn execution_props(&self) -> &ExecutionProps {
167 &self.execution_props
168 }
169
170 fn get_data_type(&self, _expr: &Expr) -> Result<DataType> {
171 Ok(Time64(Nanosecond))
172 }
173 }
174
175 fn set_session_timezone_env(tz: &str, start_time: DateTime<Utc>) -> MockSimplifyInfo {
176 let mut config = datafusion_common::config::ConfigOptions::default();
177 config.execution.time_zone = if tz.is_empty() {
178 None
179 } else {
180 Some(tz.to_string())
181 };
182 let mut execution_props =
183 ExecutionProps::new().with_query_execution_start_time(start_time);
184 execution_props.config_options = Some(Arc::new(config));
185 MockSimplifyInfo { execution_props }
186 }
187
188 #[test]
189 fn test_current_time_timezone_offset() {
190 let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 12, 0, 0).unwrap();
192
193 let info_plus_5 = set_session_timezone_env("+05:00", start_time);
195 let result_plus_5 = CurrentTimeFunc::new()
196 .simplify(vec![], &info_plus_5)
197 .unwrap();
198
199 let info_minus_5 = set_session_timezone_env("-05:00", start_time);
201 let result_minus_5 = CurrentTimeFunc::new()
202 .simplify(vec![], &info_minus_5)
203 .unwrap();
204
205 let nanos_plus_5 = match result_plus_5 {
207 ExprSimplifyResult::Simplified(Expr::Literal(
208 ScalarValue::Time64Nanosecond(Some(n)),
209 _,
210 )) => n,
211 _ => panic!("Expected Time64Nanosecond literal"),
212 };
213
214 let nanos_minus_5 = match result_minus_5 {
215 ExprSimplifyResult::Simplified(Expr::Literal(
216 ScalarValue::Time64Nanosecond(Some(n)),
217 _,
218 )) => n,
219 _ => panic!("Expected Time64Nanosecond literal"),
220 };
221
222 let difference = nanos_plus_5 - nanos_minus_5;
224
225 let expected_offset = 10i64 * 3600 * 1_000_000_000;
227
228 assert_eq!(difference, expected_offset, "Expected 10-hour offset difference in nanoseconds between UTC+05:00 and UTC-05:00");
229 }
230}