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