Skip to content

Commit

Permalink
fix: fix bug when date_diif get negetive
Browse files Browse the repository at this point in the history
  • Loading branch information
chagelo committed Oct 3, 2024
1 parent 37d7f7f commit fcea4bc
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 35 deletions.
26 changes: 13 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 48 additions & 19 deletions src/query/expression/src/utils/date_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ where T: AsPrimitive<i64>
}
}

pub const SECONDS_PER_DAY: i64 = 86_400;
pub const MICROSECS_PER_DAY: i64 = 86_400_000_000;

// Timestamp arithmetic factors.
pub const FACTOR_HOUR: i64 = 3600;
Expand Down Expand Up @@ -328,6 +328,17 @@ fn last_day_of_year_month(year: i32, month: u32) -> u32 {
LAST_DAY_LUT[month as usize] as u32
}

// Check if the date difference is a multiple of months, 31 Jan and 28 Feb differ one month, but 30 Mar and 30 Apr not
fn is_same_day_of_month(date_start: &NaiveDate, date_end: &NaiveDate) -> bool {
let start_is_last_day_of_month =
date_start.day() == last_day_of_year_month(date_start.year(), date_start.month());
let end_is_last_day_of_month =
date_end.day() == last_day_of_year_month(date_end.year(), date_end.month());

start_is_last_day_of_month == end_is_last_day_of_month
&& (start_is_last_day_of_month || date_start.day() == date_end.day())
}

macro_rules! impl_interval_year_month {
($name: ident, $op: expr) => {
#[derive(Clone)]
Expand Down Expand Up @@ -392,33 +403,52 @@ impl EvalQuartersImpl {

impl EvalMonthsImpl {
pub fn eval_date_diff(date_start: i32, date_end: i32, tz: TzLUT) -> i32 {
let (mut date_start, mut date_end) = (date_start, date_end);
let sign = if date_start > date_end {
(date_start, date_end) = (date_end, date_start);
-1
} else {
1
};

let date_start = date_start.to_date(tz.tz);
let date_end = date_end.to_date(tz.tz);
let mut diff_months = (date_end.year() - date_start.year()) * 12
+ (date_end.month() as i32 - date_start.month() as i32);

let start_is_last_day_of_month =
date_start.day() == last_day_of_year_month(date_start.year(), date_start.month());
let end_is_last_day_of_month =
date_end.day() == last_day_of_year_month(date_end.year(), date_end.month());

if !end_is_last_day_of_month
&& (start_is_last_day_of_month || date_start.day() > date_end.day())
{
let is_same_day = is_same_day_of_month(&date_start, &date_end);
if !is_same_day && date_start.day() > date_end.day() {
diff_months -= 1
}
diff_months
diff_months * sign
}

pub fn eval_timestamp_diff(date_start: i64, date_end: i64, tz: TzLUT) -> i64 {
let (date_start_days, date_start_micros) =
(date_start / SECONDS_PER_DAY, date_start % SECONDS_PER_DAY);
let (mut date_start, mut date_end) = (date_start, date_end);
let sign = if date_start > date_end {
(date_start, date_end) = (date_end, date_start);
-1
} else {
1
};

let (date_start_days, date_start_micros) = (
date_start / MICROSECS_PER_DAY,
date_start % MICROSECS_PER_DAY,
);
let (date_end_days, date_end_micros) =
(date_end / SECONDS_PER_DAY, date_end % SECONDS_PER_DAY);
(date_end / MICROSECS_PER_DAY, date_end % MICROSECS_PER_DAY);
let mut diff_months =
EvalMonthsImpl::eval_date_diff(date_start_days as i32, date_end_days as i32, tz) as i64;
diff_months -= (date_start_micros > date_end_micros) as i64;
diff_months

let date_start = date_start_days.to_date(tz.tz);
let date_end = date_end_days.to_date(tz.tz);

let is_same_day = is_same_day_of_month(&date_start, &date_end);
if is_same_day && date_start_micros > date_end_micros {
diff_months -= 1;
}
diff_months * sign
}

// current we don't consider tz here
Expand Down Expand Up @@ -495,11 +525,11 @@ impl EvalDaysImpl {
date: i64,
delta: impl AsPrimitive<i64>,
) -> std::result::Result<i64, String> {
check_timestamp(date.wrapping_add(delta.as_() * SECONDS_PER_DAY * MICROS_PER_SEC))
check_timestamp(date.wrapping_add(delta.as_() * MICROSECS_PER_DAY))
}

pub fn eval_timestamp_diff(date_start: i64, date_end: i64) -> i64 {
(date_end - date_start) / SECONDS_PER_DAY
(date_end - date_start) / MICROSECS_PER_DAY
}
}

Expand All @@ -512,8 +542,7 @@ impl EvalTimesImpl {
factor: i64,
) -> std::result::Result<i32, String> {
check_date(
(date as i64 * SECONDS_PER_DAY * MICROS_PER_SEC)
.wrapping_add(delta.as_() * factor * MICROS_PER_SEC),
(date as i64 * MICROSECS_PER_DAY).wrapping_add(delta.as_() * factor * MICROS_PER_SEC),
)
}

Expand Down
80 changes: 80 additions & 0 deletions src/query/functions/tests/it/scalars/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,32 +607,112 @@ fn test_date_date_diff(file: &mut impl Write) {
"date_diff(year, to_date('2000-01-01'), to_date('2024-12-31'))",
&[],
);
run_ast(
file,
"date_diff(year, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(year, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(file, "date_diff(quarter, to_date(0), to_date(10000))", &[]);
run_ast(file, "date_diff(quarter, to_date(10000), to_date(0))", &[]);
run_ast(
file,
"date_diff(quarter, to_date('2000-01-01'), to_date('2024-12-31'))",
&[],
);
run_ast(
file,
"date_diff(quarter, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(quarter, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(file, "date_diff(month, to_date(0), to_date(10000))", &[]);
run_ast(file, "date_diff(month, to_date(10000), to_date(0))", &[]);
run_ast(
file,
"date_diff(month, to_date('2000-01-01'), to_date('2024-12-31'))",
&[],
);
run_ast(
file,
"date_diff(month, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(month, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(file, "date_diff(week, to_date(0), to_date(10000))", &[]);
run_ast(file, "date_diff(week, to_date(10000), to_date(0))", &[]);
run_ast(
file,
"date_diff(week, to_date('2000-01-01'), to_date('2024-12-31'))",
&[],
);
run_ast(
file,
"date_diff(week, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(week, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(file, "date_diff(day, to_date(0), to_date(10000))", &[]);
run_ast(file, "date_diff(day, to_date(10000), to_date(0))", &[]);
run_ast(
file,
"date_diff(day, to_date('2000-01-01'), to_date('2024-12-31'))",
&[],
);
run_ast(
file,
"date_diff(day, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(day, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(
file,
"date_diff(hour, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(hour, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(
file,
"date_diff(minute, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(minute, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
run_ast(
file,
"date_diff(second, to_timestamp('2023-11-12 09:38:18.165575'), to_timestamp('2025-03-27 21:01:35.423179'))",
&[],
);
run_ast(
file,
"date_diff(second, to_timestamp('2020-02-29 23:59:59.165575'), to_timestamp('2019-02-28 23:59:59.423179'))",
&[],
);
}
Loading

0 comments on commit fcea4bc

Please sign in to comment.