From 20eb658bfb63abb53416e92d7e5b2b6b713bd900 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 29 Oct 2025 16:28:33 +0800 Subject: [PATCH 1/3] Improve NowFunc::new() implementation and docs Delegate to new_with_config and enhance documentation. Add regression coverage for timestamp metadata consistency. Update the scalar functions guide to clarify the restored behavior of the deprecated helper in relation to the default timezone offset. --- datafusion/functions/src/datetime/now.rs | 53 +++++++++++++++++-- .../source/user-guide/sql/scalar_functions.md | 7 +++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/datafusion/functions/src/datetime/now.rs b/datafusion/functions/src/datetime/now.rs index 96a35c241ff0..dbc5428cce42 100644 --- a/datafusion/functions/src/datetime/now.rs +++ b/datafusion/functions/src/datetime/now.rs @@ -54,12 +54,14 @@ impl Default for NowFunc { impl NowFunc { #[deprecated(since = "50.2.0", note = "use `new_with_config` instead")] + /// Deprecated constructor retained for backwards compatibility. + /// + /// Prefer [`NowFunc::new_with_config`] which allows specifying the + /// timezone via [`ConfigOptions`]. This helper now mirrors the + /// canonical default offset (`"+00:00"`) provided by + /// `ConfigOptions::default()`. pub fn new() -> Self { - Self { - signature: Signature::nullary(Volatility::Stable), - aliases: vec!["current_timestamp".to_string()], - timezone: Some(Arc::from("+00")), - } + Self::new_with_config(&ConfigOptions::default()) } pub fn new_with_config(config: &ConfigOptions) -> Self { @@ -71,6 +73,47 @@ impl NowFunc { } } +#[cfg(test)] +mod tests { + use super::*; + + #[allow(deprecated)] + #[test] + fn now_func_default_matches_config() { + let default_config = ConfigOptions::default(); + + let legacy_now = NowFunc::new(); + let configured_now = NowFunc::new_with_config(&default_config); + + let empty_fields: [FieldRef; 0] = []; + let empty_scalars: [Option<&ScalarValue>; 0] = []; + + let legacy_field = legacy_now + .return_field_from_args(ReturnFieldArgs { + arg_fields: &empty_fields, + scalar_arguments: &empty_scalars, + }) + .expect("legacy now() return field"); + + let configured_field = configured_now + .return_field_from_args(ReturnFieldArgs { + arg_fields: &empty_fields, + scalar_arguments: &empty_scalars, + }) + .expect("configured now() return field"); + + assert_eq!(legacy_field.as_ref(), configured_field.as_ref()); + + let legacy_scalar = + ScalarValue::TimestampNanosecond(None, legacy_now.timezone.clone()); + let configured_scalar = + ScalarValue::TimestampNanosecond(None, configured_now.timezone.clone()); + + assert_eq!(legacy_scalar, configured_scalar); + assert_eq!(Some("+00:00"), legacy_now.timezone.as_deref()); + } +} + /// Create an implementation of `now()` that always returns the /// specified timestamp. /// diff --git a/docs/source/user-guide/sql/scalar_functions.md b/docs/source/user-guide/sql/scalar_functions.md index d090b5b70cda..98cacc6903ed 100644 --- a/docs/source/user-guide/sql/scalar_functions.md +++ b/docs/source/user-guide/sql/scalar_functions.md @@ -2637,6 +2637,13 @@ The `now()` return value is determined at query time and will return the same ti now() ``` +> **Note:** When constructing DataFusion directly from Rust, prefer +> [`NowFunc::new_with_config`](https://docs.rs/datafusion/latest/datafusion/functions/datetime/struct.NowFunc.html#method.new_with_config) +> to supply a configuration with the desired timezone. The deprecated +> [`NowFunc::new`](https://docs.rs/datafusion/latest/datafusion/functions/datetime/struct.NowFunc.html#method.new) +> helper remains available for backwards compatibility and now mirrors +> the canonical default offset (`+00:00`). + #### Aliases - current_timestamp From a03264246532c5d61b38c114295b8b822769205e Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 29 Oct 2025 17:18:01 +0800 Subject: [PATCH 2/3] Prettier md --- docs/source/user-guide/sql/scalar_functions.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/source/user-guide/sql/scalar_functions.md b/docs/source/user-guide/sql/scalar_functions.md index 98cacc6903ed..d090b5b70cda 100644 --- a/docs/source/user-guide/sql/scalar_functions.md +++ b/docs/source/user-guide/sql/scalar_functions.md @@ -2637,13 +2637,6 @@ The `now()` return value is determined at query time and will return the same ti now() ``` -> **Note:** When constructing DataFusion directly from Rust, prefer -> [`NowFunc::new_with_config`](https://docs.rs/datafusion/latest/datafusion/functions/datetime/struct.NowFunc.html#method.new_with_config) -> to supply a configuration with the desired timezone. The deprecated -> [`NowFunc::new`](https://docs.rs/datafusion/latest/datafusion/functions/datetime/struct.NowFunc.html#method.new) -> helper remains available for backwards compatibility and now mirrors -> the canonical default offset (`+00:00`). - #### Aliases - current_timestamp From 92c7f9c20dde9828cae69850e111869b22ad97f1 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 29 Oct 2025 17:17:38 +0800 Subject: [PATCH 3/3] move tests to the end --- datafusion/functions/src/datetime/now.rs | 82 ++++++++++++------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/datafusion/functions/src/datetime/now.rs b/datafusion/functions/src/datetime/now.rs index dbc5428cce42..fe317d0a16f9 100644 --- a/datafusion/functions/src/datetime/now.rs +++ b/datafusion/functions/src/datetime/now.rs @@ -73,47 +73,6 @@ impl NowFunc { } } -#[cfg(test)] -mod tests { - use super::*; - - #[allow(deprecated)] - #[test] - fn now_func_default_matches_config() { - let default_config = ConfigOptions::default(); - - let legacy_now = NowFunc::new(); - let configured_now = NowFunc::new_with_config(&default_config); - - let empty_fields: [FieldRef; 0] = []; - let empty_scalars: [Option<&ScalarValue>; 0] = []; - - let legacy_field = legacy_now - .return_field_from_args(ReturnFieldArgs { - arg_fields: &empty_fields, - scalar_arguments: &empty_scalars, - }) - .expect("legacy now() return field"); - - let configured_field = configured_now - .return_field_from_args(ReturnFieldArgs { - arg_fields: &empty_fields, - scalar_arguments: &empty_scalars, - }) - .expect("configured now() return field"); - - assert_eq!(legacy_field.as_ref(), configured_field.as_ref()); - - let legacy_scalar = - ScalarValue::TimestampNanosecond(None, legacy_now.timezone.clone()); - let configured_scalar = - ScalarValue::TimestampNanosecond(None, configured_now.timezone.clone()); - - assert_eq!(legacy_scalar, configured_scalar); - assert_eq!(Some("+00:00"), legacy_now.timezone.as_deref()); - } -} - /// Create an implementation of `now()` that always returns the /// specified timestamp. /// @@ -181,3 +140,44 @@ impl ScalarUDFImpl for NowFunc { self.doc() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(deprecated)] + #[test] + fn now_func_default_matches_config() { + let default_config = ConfigOptions::default(); + + let legacy_now = NowFunc::new(); + let configured_now = NowFunc::new_with_config(&default_config); + + let empty_fields: [FieldRef; 0] = []; + let empty_scalars: [Option<&ScalarValue>; 0] = []; + + let legacy_field = legacy_now + .return_field_from_args(ReturnFieldArgs { + arg_fields: &empty_fields, + scalar_arguments: &empty_scalars, + }) + .expect("legacy now() return field"); + + let configured_field = configured_now + .return_field_from_args(ReturnFieldArgs { + arg_fields: &empty_fields, + scalar_arguments: &empty_scalars, + }) + .expect("configured now() return field"); + + assert_eq!(legacy_field.as_ref(), configured_field.as_ref()); + + let legacy_scalar = + ScalarValue::TimestampNanosecond(None, legacy_now.timezone.clone()); + let configured_scalar = + ScalarValue::TimestampNanosecond(None, configured_now.timezone.clone()); + + assert_eq!(legacy_scalar, configured_scalar); + assert_eq!(Some("+00:00"), legacy_now.timezone.as_deref()); + } +}