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::{Result, ScalarValue, internal_err};
25use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyContext};
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: &SimplifyContext,
100 ) -> Result<ExprSimplifyResult> {
101 let Some(now_ts) = info.query_execution_start_time() else {
102 return Ok(ExprSimplifyResult::Original(args));
103 };
104
105 let nano = info
107 .config_options()
108 .execution
109 .time_zone
110 .as_ref()
111 .and_then(|tz| tz.parse::<Tz>().ok())
112 .map_or_else(
113 || datetime_to_time_nanos(&now_ts),
114 |tz| {
115 let local_now = tz.from_utc_datetime(&now_ts.naive_utc());
116 datetime_to_time_nanos(&local_now)
117 },
118 );
119
120 Ok(ExprSimplifyResult::Simplified(Expr::Literal(
121 ScalarValue::Time64Nanosecond(nano),
122 None,
123 )))
124 }
125
126 fn documentation(&self) -> Option<&Documentation> {
127 self.doc()
128 }
129}
130
131fn datetime_to_time_nanos<Tz: TimeZone>(dt: &chrono::DateTime<Tz>) -> Option<i64> {
133 let hour = dt.hour() as i64;
134 let minute = dt.minute() as i64;
135 let second = dt.second() as i64;
136 let nanosecond = dt.nanosecond() as i64;
137 Some((hour * 3600 + minute * 60 + second) * 1_000_000_000 + nanosecond)
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use chrono::{DateTime, Utc};
144 use datafusion_common::config::ConfigOptions;
145 use datafusion_common::{DFSchema, ScalarValue};
146 use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyContext};
147 use std::sync::Arc;
148
149 fn set_session_timezone_env(tz: &str, start_time: DateTime<Utc>) -> SimplifyContext {
150 let mut config = ConfigOptions::default();
151 config.execution.time_zone = if tz.is_empty() {
152 None
153 } else {
154 Some(tz.to_string())
155 };
156 let schema = Arc::new(DFSchema::empty());
157 SimplifyContext::default()
158 .with_schema(schema)
159 .with_config_options(Arc::new(config))
160 .with_query_execution_start_time(Some(start_time))
161 }
162
163 #[test]
164 fn test_current_time_timezone_offset() {
165 let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 12, 0, 0).unwrap();
167
168 let info_plus_5 = set_session_timezone_env("+05:00", start_time);
170 let result_plus_5 = CurrentTimeFunc::new()
171 .simplify(vec![], &info_plus_5)
172 .unwrap();
173
174 let info_minus_5 = set_session_timezone_env("-05:00", start_time);
176 let result_minus_5 = CurrentTimeFunc::new()
177 .simplify(vec![], &info_minus_5)
178 .unwrap();
179
180 let nanos_plus_5 = match result_plus_5 {
182 ExprSimplifyResult::Simplified(Expr::Literal(
183 ScalarValue::Time64Nanosecond(Some(n)),
184 _,
185 )) => n,
186 _ => panic!("Expected Time64Nanosecond literal"),
187 };
188
189 let nanos_minus_5 = match result_minus_5 {
190 ExprSimplifyResult::Simplified(Expr::Literal(
191 ScalarValue::Time64Nanosecond(Some(n)),
192 _,
193 )) => n,
194 _ => panic!("Expected Time64Nanosecond literal"),
195 };
196
197 let difference = nanos_plus_5 - nanos_minus_5;
199
200 let expected_offset = 10i64 * 3600 * 1_000_000_000;
202
203 assert_eq!(
204 difference, expected_offset,
205 "Expected 10-hour offset difference in nanoseconds between UTC+05:00 and UTC-05:00"
206 );
207 }
208}