Skip to content

Commit 77ec319

Browse files
authored
Feat: Make current_date aware of execution timezone. (#18034)
* Feat: Make current_date aware of execution timezone. * CI Fixes: Rectify assertion error of slt and update scalar_functions.md * CI Fixes: Comment out flaky test involving now(). * CI Fixes: Resolve slt error. * Feat: Add helper function to calculate current_date and fix tests. * Chore: Refactor timezone conversion logic.
1 parent 2b2113a commit 77ec319

File tree

3 files changed

+106
-10
lines changed

3 files changed

+106
-10
lines changed

datafusion/functions/src/datetime/current_date.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717

1818
use std::any::Any;
1919

20+
use arrow::array::timezone::Tz;
2021
use arrow::datatypes::DataType;
2122
use arrow::datatypes::DataType::Date32;
22-
use chrono::{Datelike, NaiveDate};
23+
use chrono::{Datelike, NaiveDate, TimeZone};
2324

2425
use datafusion_common::{internal_err, Result, ScalarValue};
2526
use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
@@ -31,7 +32,7 @@ use datafusion_macros::user_doc;
3132
#[user_doc(
3233
doc_section(label = "Time and Date Functions"),
3334
description = r#"
34-
Returns the current UTC date.
35+
Returns the current date in the session time zone.
3536
3637
The `current_date()` return value is determined at query time and will return the same date, no matter when in the query plan the function executes.
3738
"#,
@@ -100,14 +101,21 @@ impl ScalarUDFImpl for CurrentDateFunc {
100101
info: &dyn SimplifyInfo,
101102
) -> Result<ExprSimplifyResult> {
102103
let now_ts = info.execution_props().query_execution_start_time;
103-
let days = Some(
104-
now_ts.num_days_from_ce()
105-
- NaiveDate::from_ymd_opt(1970, 1, 1)
106-
.unwrap()
107-
.num_days_from_ce(),
108-
);
104+
105+
// Get timezone from config and convert to local time
106+
let days = info
107+
.execution_props()
108+
.config_options()
109+
.and_then(|config| config.execution.time_zone.parse::<Tz>().ok())
110+
.map_or_else(
111+
|| datetime_to_days(&now_ts),
112+
|tz| {
113+
let local_now = tz.from_utc_datetime(&now_ts.naive_utc());
114+
datetime_to_days(&local_now)
115+
},
116+
);
109117
Ok(ExprSimplifyResult::Simplified(Expr::Literal(
110-
ScalarValue::Date32(days),
118+
ScalarValue::Date32(Some(days)),
111119
None,
112120
)))
113121
}
@@ -116,3 +124,11 @@ impl ScalarUDFImpl for CurrentDateFunc {
116124
self.doc()
117125
}
118126
}
127+
128+
/// Converts a DateTime to the number of days since Unix epoch (1970-01-01)
129+
fn datetime_to_days<T: Datelike>(dt: &T) -> i32 {
130+
dt.num_days_from_ce()
131+
- NaiveDate::from_ymd_opt(1970, 1, 1)
132+
.unwrap()
133+
.num_days_from_ce()
134+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
##########
19+
## current_date with timezone tests
20+
##########
21+
22+
# Test 1: Verify current_date is consistent within the same query (default UTC)
23+
query B
24+
SELECT current_date() = current_date();
25+
----
26+
true
27+
28+
# Test 2: Verify alias 'today' works the same as current_date
29+
query B
30+
SELECT current_date() = today();
31+
----
32+
true
33+
34+
# Test 3: Set timezone to +05:00 and verify current_date is still stable
35+
statement ok
36+
SET datafusion.execution.time_zone = '+05:00';
37+
38+
query B
39+
SELECT current_date() = current_date();
40+
----
41+
true
42+
43+
#Test 4: Verify current_date matches cast(now() as date) in the same timezone
44+
query B
45+
SELECT current_date() = cast(now() as date);
46+
----
47+
true
48+
49+
# Test 5: Test with negative offset timezone
50+
statement ok
51+
SET datafusion.execution.time_zone = '-08:00';
52+
53+
query B
54+
SELECT current_date() = today();
55+
----
56+
true
57+
58+
# Test 6: Test with named timezone (America/New_York)
59+
statement ok
60+
SET datafusion.execution.time_zone = 'America/New_York';
61+
62+
query B
63+
SELECT current_date() = current_date();
64+
----
65+
true
66+
67+
# Test 7: Verify date type is preserved
68+
query T
69+
SELECT arrow_typeof(current_date());
70+
----
71+
Date32
72+
73+
# Test 8: Reset to UTC
74+
statement ok
75+
SET datafusion.execution.time_zone = '+00:00';
76+
77+
query B
78+
SELECT current_date() = today();
79+
----
80+
true

docs/source/user-guide/sql/scalar_functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2403,7 +2403,7 @@ Additional examples can be found [here](https://github.com/apache/datafusion/blo
24032403

24042404
### `current_date`
24052405

2406-
Returns the current UTC date.
2406+
Returns the current date in the session time zone.
24072407

24082408
The `current_date()` return value is determined at query time and will return the same date, no matter when in the query plan the function executes.
24092409

0 commit comments

Comments
 (0)