-
Notifications
You must be signed in to change notification settings - Fork 742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(query): function about convert_timezone #16177 #16181
base: main
Are you sure you want to change the base?
Changes from all commits
499a4ac
947110c
04cf38e
9b93737
8a26246
2ba4ef6
72331aa
ef04fd0
86942fc
3e3cada
4a7a668
ab8d39e
286b259
cf248e3
be4161a
85e70b6
b33db2e
b2e33b0
490aa6e
f93d572
261fd1e
1a748fe
7c71cbe
05f019b
c08e5ee
53c64ce
a0b3327
1d9c360
bcdaf5e
1a514be
f244b23
819c225
1898ea5
90d3017
609c74e
311f4e0
ae55f91
15b8c3e
e20df6d
2d80243
dba9dbf
a1f1b9d
871cb8b
f931c90
9acde6d
07765bc
40eaa13
d0dc745
d5555c7
5b7e576
191c57d
6ebe9ca
637b9ea
5ac0b1e
98d6fe3
09e6ee4
a2a6b2a
70d1176
ccb349c
34fb1c2
65ae38b
3865762
9d7b425
00abbef
aec53b6
0c504aa
542daca
a596664
23b7a02
dcb449c
6283302
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,7 @@ use databend_common_expression::vectorize_1_arg; | |
use databend_common_expression::vectorize_2_arg; | ||
use databend_common_expression::vectorize_with_builder_1_arg; | ||
use databend_common_expression::vectorize_with_builder_2_arg; | ||
use databend_common_expression::vectorize_with_builder_3_arg; | ||
use databend_common_expression::EvalContext; | ||
use databend_common_expression::FunctionDomain; | ||
use databend_common_expression::FunctionProperty; | ||
|
@@ -108,6 +109,9 @@ pub fn register(registry: &mut FunctionRegistry) { | |
|
||
// [date | timestamp] +/- number | ||
register_timestamp_add_sub(registry); | ||
|
||
// convert_timezone( target_timezone, date, src_timezone) | ||
register_convert_timezone(registry); | ||
} | ||
|
||
/// Check if timestamp is within range, and return the timestamp in micros. | ||
|
@@ -131,6 +135,109 @@ fn int64_domain_to_timestamp_domain<T: AsPrimitive<i64>>( | |
}) | ||
} | ||
|
||
fn register_convert_timezone(registry: &mut FunctionRegistry) { | ||
// 2 arguments function [target_timezone, src_timestamp] | ||
registry.register_passthrough_nullable_2_arg::<StringType, TimestampType, TimestampType, _, _>( | ||
"convert_timezone", | ||
|_, _, _| FunctionDomain::MayThrow, | ||
eval_convert_timezone, | ||
); | ||
|
||
// 3 arguments function [src_timezone, target_timezone, src_timestamp] | ||
registry.register_passthrough_nullable_3_arg::<StringType, StringType, TimestampType, TimestampType, _, _>( | ||
"convert_timezone", | ||
|_, _, _, _| FunctionDomain::MayThrow, | ||
vectorize_with_builder_3_arg::<StringType, StringType, TimestampType, TimestampType>( | ||
|src_tz, target_tz, src_timestamp, output, ctx| { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. check validity here to handle if let Some(validity) = &ctx.validity {
if !validity.get_bit(output.len()) {
output.push_default();
return;
}
} |
||
// Parsing parameters | ||
let t_tz: Tz = match target_tz.parse() { | ||
Ok(tz) => tz, | ||
Err(e) => { | ||
return ctx.set_error( | ||
output.len(), | ||
format!("cannot parse target `timezone`. {}", e), | ||
); | ||
} | ||
}; | ||
let s_tz: Tz = match src_tz.parse() { | ||
Ok(tz) => tz, | ||
Err(e) => { | ||
return ctx.set_error( | ||
output.len(), | ||
format!("cannot parse src `timezone`. {}", e), | ||
); | ||
} | ||
}; | ||
|
||
let p_src_timestamp: i64 = match Some(src_timestamp){ | ||
Ok(timestamp) => { | ||
timestamp.Utc.timestamp_opt(src_timestamp, 0).unwrap(); | ||
}, | ||
Err(e) => { | ||
return ctx.set_error( | ||
output.len(), | ||
format!("cannot parse target `timezone`. {}", e), | ||
); | ||
} | ||
}; | ||
|
||
|
||
// Create dummy timezone | ||
let utc_now: DateTime<Utc> = Utc::now(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think no need to create dummy timezone. You can use this https://docs.rs/chrono/latest/chrono/offset/trait.TimeZone.html#method.timestamp_opt to convert ts to source timezone. Then convert the ts with src tz to target tz. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
let src_time = utc_now.with_timezone(&s_tz); | ||
let target_time = utc_now.with_timezone(&t_tz); | ||
|
||
// Calculate the difference in seconds | ||
let delta = target_time.signed_duration_since(src_time); | ||
let result_timestamp = p_src_timestamp + delta.num_seconds(); | ||
|
||
output.push(result_timestamp) | ||
}, | ||
), | ||
); | ||
|
||
fn eval_convert_timezone( | ||
target_tz: ValueRef<StringType>, | ||
src_timestamp: ValueRef<TimestampType>, | ||
ctx: &mut EvalContext, | ||
) -> Value<TimestampType> { | ||
vectorize_with_builder_2_arg::<StringType, TimestampType, TimestampType>( | ||
|target_tz, src_timestamp, output, ctx| { | ||
// Parse the target timezone | ||
let t_tz: Tz = match target_tz.parse() { | ||
Ok(tz) => tz, | ||
Err(e) => { | ||
return ctx.set_error( | ||
output.len(), | ||
format!("cannot parse target `timezone`. {}", e), | ||
); | ||
} | ||
}; | ||
|
||
// Assume the source timestamp is in UTC | ||
match Some(src_timestamp) { | ||
Ok(src_timestamp) => { | ||
let timestamp: i64 = src_timestamp; | ||
let datetime: DateTime<Utc> = Utc.timestamp_opt(src_timestamp, 0).unwrap(); | ||
|
||
// Convert the UTC time to the specified target timezone | ||
let target_datetime: DateTime<Tz> = datetime.with_timezone(&t_tz); | ||
let result_timestamp = target_datetime.timestamp(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. timestamp_micros() |
||
// Return the adjusted timestamp as a Unix timestamp in seconds | ||
output.push(result_timestamp) | ||
} | ||
Err(e) => { | ||
return ctx.set_error( | ||
output.len(), | ||
format!("cannot parse target `timezone`. {}", e), | ||
); | ||
} | ||
}; | ||
}, | ||
)(target_tz, src_timestamp, ctx) | ||
} | ||
} | ||
fn register_string_to_timestamp(registry: &mut FunctionRegistry) { | ||
registry.register_aliases("to_date", &["str_to_date", "date"]); | ||
registry.register_aliases("to_year", &["str_to_year", "year"]); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,42 @@ fn test_datetime() { | |
test_timestamp_arith(file); | ||
test_to_number(file); | ||
test_rounder_functions(file); | ||
test_convert_timezone(file); | ||
} | ||
|
||
fn test_convert_timezone(file: &mut impl Write) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool. SELECT CONVERT_TIMEZONE( SELECT CONVERT_TIMEZONE( SELECT CONVERT_TIMEZONE( SELECT Need to add these tests in logical test. |
||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', to_timestamp(100))", | ||
&[], | ||
); | ||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', to_timestamp(-100))", | ||
&[], | ||
); | ||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', to_timestamp(0))", | ||
&[], | ||
); | ||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', to_timestamp(315360000000))", | ||
&[], | ||
); | ||
|
||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', 'Europe/Simferopol', to_timestamp(100))", | ||
&[], | ||
); | ||
|
||
run_ast( | ||
file, | ||
"convert_timezone('America/New_York', 'Europe/Simferopol', to_timestamp(0))", | ||
&[], | ||
); | ||
} | ||
|
||
fn test_to_timestamp(file: &mut impl Write) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The arguments are in the wrong order. They should be
src_timezone, target_timezone, source_timestamp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
Need support
CONVERT_TIMEZONE( <source_tz> , <target_tz> , <source_timestamp_ntz> )
and
CONVERT_TIMEZONE( <target_tz> , <source_timestamp> )