From 9997961db6e8790d995183782f0cc33722d799cf Mon Sep 17 00:00:00 2001 From: TCeason <33082201+TCeason@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:24:22 +0800 Subject: [PATCH] fix(query): fix months_between overflow error (#17403) --- src/query/expression/src/utils/date_helper.rs | 16 ++++------------ .../src/scalars/timestamp/src/datetime.rs | 6 ++++-- .../functions/02_0012_function_datetimes.test | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/query/expression/src/utils/date_helper.rs b/src/query/expression/src/utils/date_helper.rs index d14eaee4a5186..b339e56293c10 100644 --- a/src/query/expression/src/utils/date_helper.rs +++ b/src/query/expression/src/utils/date_helper.rs @@ -230,25 +230,17 @@ impl EvalMonthsImpl { .checked_add(SignedDuration::from_hours(date_b as i64 * 24)) .unwrap(); - let year_diff = date_a.year() - date_b.year(); - let month_diff = date_a.month() as i16 - date_b.month() as i16; + let year_diff = (date_a.year() - date_b.year()) as i64; + let month_diff = date_a.month() as i64 - date_b.month() as i64; // Calculate total months difference let total_months_diff = year_diff * 12 + month_diff; // Determine if special case for fractional part applies let is_same_day_of_month = date_a.day() == date_b.day(); - let are_both_end_of_month = date_a - .checked_add(SignedDuration::from_hours(24)) - .unwrap() - .month() - != date_a.month() - && date_b - .checked_add(SignedDuration::from_hours(24)) - .unwrap() - .month() - != date_b.month(); + let are_both_end_of_month = + date_a.last_of_month() == date_a && date_b.last_of_month() == date_b; let day_fraction = if is_same_day_of_month || are_both_end_of_month { 0.0 } else { diff --git a/src/query/functions/src/scalars/timestamp/src/datetime.rs b/src/query/functions/src/scalars/timestamp/src/datetime.rs index de0b8e94252cd..90609a2368b8f 100644 --- a/src/query/functions/src/scalars/timestamp/src/datetime.rs +++ b/src/query/functions/src/scalars/timestamp/src/datetime.rs @@ -1227,9 +1227,11 @@ fn register_diff_functions(registry: &mut FunctionRegistry) { let rm = rhs.max; let rn = rhs.min; + let min = EvalMonthsImpl::months_between(ln, rm); + let max = EvalMonthsImpl::months_between(lm, rn); FunctionDomain::Domain(SimpleDomain:: { - min: EvalMonthsImpl::months_between(ln, rm).into(), - max: EvalMonthsImpl::months_between(lm, rn).into(), + min: min.into(), + max: max.into(), }) }, vectorize_2_arg::(|a, b, _ctx| { diff --git a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test index 7268e9e7029ba..2964c4f9a5b74 100644 --- a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test +++ b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test @@ -257,6 +257,21 @@ select to_date('2023-01-01') - 100 = to_date('2022-09-23') ---- 1 +statement ok +create or replace table t(mine_date date, birth_date date); + +statement ok +insert into t values('2022-01-01', '2022-01-01'),('2022-01-01', '2022-02-01'),('2022-02-01', '2022-01-01'); + +query T +SELECT max(MONTHS_BETWEEN(mine_date, birth_date))/12 as demo_age FROM t GROUP BY mine_date order by demo_age; +---- +0.0 +0.08333333333333333 + +statement ok +drop table if exists t; + query FF SELECT MONTHS_BETWEEN('2019-03-15'::DATE,