Skip to content

Commit

Permalink
feat(query): function about convert_timezone #16177 (#16181)
Browse files Browse the repository at this point in the history
* Feature: function about convert_timezone #16177

* Correction trait type

* Formatting the function

* Solving compiling issue

* Correcting file format

* adding tests

* Correction unti test

* Correction unit tests 2

* Correction unit tests 3

* Correction unit tests 4

* Correction unit tests 5

* Correction unit tests 6

* Correction unit tests 6

* Implementation of convert_timezone

* Correction registry and error handling

* Correction eval function

* Correction eval function 2

* Correction eval function 3

* Correction eval function 4

* Correction eval function 5

* Correction eval function 6

* Correction eval function 7

* Correction eval function 8

* Correction eval function 9

* Correction eval function 11

* Correction eval function 12

* Correction eval function 13 + add of the 3 arguments function version

* Code formatting

* Correction third parameter convert_timezone()

* Correction type

* Correction 1

* Correction 2

* Adding unit test

* Adding unit test 2

* Correciton unit test 4

* Correciton unit test 5

* Correciton unit test 6

* Correciton unit test 7

* Correction unit test 8

* Correction checked expr

* Correction convert_timezone

* Correction convert_timezone 2

* Correction convert_timezone 3

* Correction convert_timezone 4

* Correction convert_timezone 5

* Correction convert_timezone 6

* Correction convert_timezone 7

* Correction convert_timezone 8

* Correction convert_timezone 9

* Correction convert_timezone 10

* Correction convert_timezone 11

* Correction convert_timezone 12

* Correction convert_timezone 12

* Correction convert_timezone 13

* Unit test

* Unit test 2

* Unit test 3

* Unit test 4

* Unit test 5

* Unit test 6

* Unit test 7

* feat: convert_timezone(target_timezone, ts)

* add some check

* if error output push default

---------

Co-authored-by: TCeason <[email protected]>
  • Loading branch information
florann and TCeason authored Nov 5, 2024
1 parent db32c8d commit 31b7ceb
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 1 deletion.
56 changes: 56 additions & 0 deletions src/query/functions/src/scalars/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ pub fn register(registry: &mut FunctionRegistry) {

// [date | timestamp] +/- number
register_timestamp_add_sub(registry);

// convert_timezone( target_timezone, 'timestamp')
register_convert_timezone(registry);
}

/// Check if timestamp is within range, and return the timestamp in micros.
Expand All @@ -134,6 +137,59 @@ 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,
vectorize_with_builder_2_arg::<StringType, TimestampType, TimestampType>(
|target_tz, src_timestamp, output, ctx| {
if let Some(validity) = &ctx.validity {
if !validity.get_bit(output.len()) {
output.push(0);
return;
}
}
// Convert source timestamp from source timezone to target timezone
let p_src_timestamp = src_timestamp.to_timestamp(ctx.func_ctx.tz.tz);
let src_dst_from_utc = p_src_timestamp.offset().fix().local_minus_utc();
let t_tz: Tz = match target_tz.parse() {
Ok(tz) => tz,
Err(e) => {
ctx.set_error(
output.len(),
format!("cannot parse target `timezone`. {}", e),
);
output.push(0);
return;
}
};

let result_timestamp = p_src_timestamp.with_timezone(&t_tz).timestamp_micros();
let target_dst_from_utc = p_src_timestamp
.with_timezone(&t_tz)
.offset()
.fix()
.local_minus_utc();
let offset_as_micros_sec = (target_dst_from_utc - src_dst_from_utc) as i64;
match offset_as_micros_sec.checked_mul(MICROS_PER_SEC) {
Some(offset) => match result_timestamp.checked_add(offset) {
Some(res) => output.push(res),
None => {
ctx.set_error(output.len(), "calc final time error".to_string());
output.push(0);
}
},
None => {
ctx.set_error(output.len(), "calc time offset error".to_string());
output.push(0);
}
}
},
),
);
}

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"]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,8 @@ Functions overloads:
26 contains(Array(Boolean), Boolean) :: Boolean
27 contains(Array(Boolean) NULL, Boolean NULL) :: Boolean NULL
28 contains(Array(T0) NULL, T0) :: Boolean
0 convert_timezone(String, Timestamp) :: Timestamp
1 convert_timezone(String NULL, Timestamp NULL) :: Timestamp NULL
0 cos(Float64) :: Float64
1 cos(Float64 NULL) :: Float64 NULL
0 cosine_distance(Array(Float32), Array(Float32)) :: Float32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,6 @@ select to_datetime('1', '%s')
statement error 1006
select to_timestamp('200,2000', '%s,%Y');


statement ok
set timezone='UTC';

Expand Down Expand Up @@ -769,3 +768,59 @@ query T
SELECT substr(DATE_ADD(month, 1, now())::String, 1,4)=substr(now()::String, 1,4);
----
1


statement ok
set timezone='Asia/Shanghai';

query T
SELECT convert_timezone('America/Los_Angeles', '2024-11-01 11:36:10')
----
2024-10-31 20:36:10.000000

statement ok
set timezone='UTC';

statement ok
create or replace table t(a string null, c timestamp null);

statement ok
insert into t values('America/Los_Angeles','1970-01-01 00:00:00'), ('America/Los_Angeles','2024-10-31 22:21:15'), (null, '1970-01-01 00:00:00'), ('Asia/Shanghai', '1970-01-01 00:00:00'), ('Asia/Shanghai', '2024-10-31 22:21:15'),('Asia/Shanghai', null), (null, null);

# UTC to America/Los_Angeles, 20240-03-10 ~ 2024-11-03 is UTC-7(dst), other is UTC-8
query T
select a, c, CONVERT_TIMEZONE(a, c) from t order by a,c;
----
America/Los_Angeles 1970-01-01 00:00:00.000000 1969-12-31 16:00:00.000000
America/Los_Angeles 2024-10-31 22:21:15.000000 2024-10-31 15:21:15.000000
Asia/Shanghai 1970-01-01 00:00:00.000000 1970-01-01 08:00:00.000000
Asia/Shanghai 2024-10-31 22:21:15.000000 2024-11-01 06:21:15.000000
Asia/Shanghai NULL NULL
NULL 1970-01-01 00:00:00.000000 NULL
NULL NULL NULL

statement ok
set timezone='Asia/Shanghai';


statement error 1006
select convert_timezone('Asia/Shanghai', '1947-04-15 00:00:00');

statement ok
set enable_dst_hour_fix=1;

# 1947-04-15 00:00:00 is not exists in Asia/Shanghai. Such timings cannot be guaranteed to meet completely
# consider use date_add/sub calc the offset.
query T
select convert_timezone('UTC', '1947-04-15 00:00:00');
----
1947-04-14 15:00:00.000000

statement ok
unset enable_dst_hour_fix;

statement ok
drop table if exists t;

statement ok
unset timezone;

0 comments on commit 31b7ceb

Please sign in to comment.