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, ScalarFunctionArgs, ScalarUDFImpl, Signature,
28 Volatility,
29};
30use datafusion_macros::user_doc;
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 sql_example = r#"```sql
45> SELECT current_time();
46+--------------------+
47| current_time() |
48+--------------------+
49| 06:30:00.123456789 |
50+--------------------+
51
52-- The current time is based on the session time zone (UTC by default)
53> SET datafusion.execution.time_zone = 'Asia/Tokyo';
54> SELECT current_time();
55+--------------------+
56| current_time() |
57+--------------------+
58| 15:30:00.123456789 |
59+--------------------+
60```"#
61)]
62#[derive(Debug, PartialEq, Eq, Hash)]
63pub struct CurrentTimeFunc {
64 signature: Signature,
65}
66
67impl Default for CurrentTimeFunc {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl CurrentTimeFunc {
74 pub fn new() -> Self {
75 Self {
76 signature: Signature::nullary(Volatility::Stable),
77 }
78 }
79}
80
81impl ScalarUDFImpl for CurrentTimeFunc {
88 fn name(&self) -> &str {
89 "current_time"
90 }
91
92 fn signature(&self) -> &Signature {
93 &self.signature
94 }
95
96 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
97 Ok(Time64(Nanosecond))
98 }
99
100 fn invoke_with_args(&self, _args: ScalarFunctionArgs) -> Result<ColumnarValue> {
101 internal_err!(
102 "invoke should not be called on a simplified current_time() function"
103 )
104 }
105
106 fn simplify(
107 &self,
108 args: Vec<Expr>,
109 info: &SimplifyContext,
110 ) -> Result<ExprSimplifyResult> {
111 let Some(now_ts) = info.query_execution_start_time() else {
112 return Ok(ExprSimplifyResult::Original(args));
113 };
114
115 let nano = info
117 .config_options()
118 .execution
119 .time_zone
120 .as_ref()
121 .and_then(|tz| tz.parse::<Tz>().ok())
122 .map_or_else(
123 || datetime_to_time_nanos(&now_ts),
124 |tz| {
125 let local_now = tz.from_utc_datetime(&now_ts.naive_utc());
126 datetime_to_time_nanos(&local_now)
127 },
128 );
129
130 Ok(ExprSimplifyResult::Simplified(Expr::Literal(
131 ScalarValue::Time64Nanosecond(nano),
132 None,
133 )))
134 }
135
136 fn documentation(&self) -> Option<&Documentation> {
137 self.doc()
138 }
139}
140
141fn datetime_to_time_nanos<Tz: TimeZone>(dt: &chrono::DateTime<Tz>) -> Option<i64> {
143 let hour = dt.hour() as i64;
144 let minute = dt.minute() as i64;
145 let second = dt.second() as i64;
146 let nanosecond = dt.nanosecond() as i64;
147 Some((hour * 3600 + minute * 60 + second) * 1_000_000_000 + nanosecond)
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use chrono::{DateTime, Utc};
154 use datafusion_common::DFSchema;
155 use datafusion_common::config::ConfigOptions;
156 use std::sync::Arc;
157
158 fn set_session_timezone_env(tz: &str, start_time: DateTime<Utc>) -> SimplifyContext {
159 let mut config = ConfigOptions::default();
160 config.execution.time_zone = if tz.is_empty() {
161 None
162 } else {
163 Some(tz.to_string())
164 };
165 let schema = Arc::new(DFSchema::empty());
166 SimplifyContext::builder()
167 .with_schema(schema)
168 .with_config_options(Arc::new(config))
169 .with_query_execution_start_time(Some(start_time))
170 .build()
171 }
172
173 #[test]
174 fn test_current_time_timezone_offset() {
175 let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 12, 0, 0).unwrap();
177
178 let info_plus_5 = set_session_timezone_env("+05:00", start_time);
180 let result_plus_5 = CurrentTimeFunc::new()
181 .simplify(vec![], &info_plus_5)
182 .unwrap();
183
184 let info_minus_5 = set_session_timezone_env("-05:00", start_time);
186 let result_minus_5 = CurrentTimeFunc::new()
187 .simplify(vec![], &info_minus_5)
188 .unwrap();
189
190 let nanos_plus_5 = match result_plus_5 {
192 ExprSimplifyResult::Simplified(Expr::Literal(
193 ScalarValue::Time64Nanosecond(Some(n)),
194 _,
195 )) => n,
196 _ => panic!("Expected Time64Nanosecond literal"),
197 };
198
199 let nanos_minus_5 = match result_minus_5 {
200 ExprSimplifyResult::Simplified(Expr::Literal(
201 ScalarValue::Time64Nanosecond(Some(n)),
202 _,
203 )) => n,
204 _ => panic!("Expected Time64Nanosecond literal"),
205 };
206
207 let difference = nanos_plus_5 - nanos_minus_5;
209
210 let expected_offset = 10i64 * 3600 * 1_000_000_000;
212
213 assert_eq!(
214 difference, expected_offset,
215 "Expected 10-hour offset difference in nanoseconds between UTC+05:00 and UTC-05:00"
216 );
217 }
218}