feat: add timediff data and time function

This commit is contained in:
Sachin Singh
2025-04-11 04:30:57 +05:30
parent 17b206297e
commit b7acfa490c
4 changed files with 168 additions and 0 deletions

View File

@@ -293,6 +293,7 @@ pub enum ScalarFunc {
StrfTime, StrfTime,
Printf, Printf,
Likely, Likely,
TimeDiff,
} }
impl Display for ScalarFunc { impl Display for ScalarFunc {
@@ -348,6 +349,7 @@ impl Display for ScalarFunc {
Self::StrfTime => "strftime".to_string(), Self::StrfTime => "strftime".to_string(),
Self::Printf => "printf".to_string(), Self::Printf => "printf".to_string(),
Self::Likely => "likely".to_string(), Self::Likely => "likely".to_string(),
Self::TimeDiff => "timediff".to_string(),
}; };
write!(f, "{}", str) write!(f, "{}", str)
} }
@@ -555,6 +557,12 @@ impl Func {
} }
Ok(Self::Agg(AggFunc::Total)) Ok(Self::Agg(AggFunc::Total))
} }
"timediff" => {
if arg_count != 2 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Scalar(ScalarFunc::TimeDiff))
}
#[cfg(feature = "json")] #[cfg(feature = "json")]
"jsonb_group_array" => Ok(Self::Agg(AggFunc::JsonbGroupArray)), "jsonb_group_array" => Ok(Self::Agg(AggFunc::JsonbGroupArray)),
#[cfg(feature = "json")] #[cfg(feature = "json")]

View File

@@ -656,6 +656,61 @@ fn parse_modifier(modifier: &str) -> Result<Modifier> {
} }
} }
pub fn exec_timediff(values: &[Register]) -> OwnedValue {
if values.len() < 2 {
return OwnedValue::Null;
}
let start = parse_naive_date_time(values[0].get_owned_value());
let end = parse_naive_date_time(values[1].get_owned_value());
match (start, end) {
(Some(start), Some(end)) => {
let duration = start.signed_duration_since(end);
format_time_duration(&duration)
}
_ => OwnedValue::Null,
}
}
fn format_time_duration(duration: &chrono::Duration) -> OwnedValue {
let is_negative = duration.num_seconds() < 0;
let abs_duration = if is_negative {
-duration.clone()
} else {
duration.clone()
};
let total_seconds = abs_duration.num_seconds();
let hours = total_seconds / 3600;
let minutes = (total_seconds % 3600) / 60;
let seconds = total_seconds % 60;
let total_millis = abs_duration.num_milliseconds();
let millis = total_millis % 1000;
let result = if millis > 0 {
format!(
"{}{:02}:{:02}:{:02}.{:03}",
if is_negative { "-" } else { "" },
hours,
minutes,
seconds,
millis
)
} else {
format!(
"{}{:02}:{:02}:{:02}",
if is_negative { "-" } else { "" },
hours,
minutes,
seconds
)
};
OwnedValue::build_text(&result)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -1642,4 +1697,67 @@ mod tests {
#[test] #[test]
fn test_strftime() {} fn test_strftime() {}
#[test]
fn test_exec_timediff() {
let start = OwnedValue::build_text("12:00:00");
let end = OwnedValue::build_text("14:30:45");
let expected = OwnedValue::build_text("-02:30:45");
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("14:30:45");
let end = OwnedValue::build_text("12:00:00");
let expected = OwnedValue::build_text("02:30:45");
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("12:00:01.300");
let end = OwnedValue::build_text("12:00:00.500");
let expected = OwnedValue::build_text("00:00:00.800");
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("13:30:00");
let end = OwnedValue::build_text("16:45:30");
let expected = OwnedValue::build_text("-03:15:30");
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("2023-05-10 23:30:00");
let end = OwnedValue::build_text("2023-05-11 01:15:00");
let expected = OwnedValue::build_text("-01:45:00");
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::Null;
let end = OwnedValue::build_text("12:00:00");
let expected = OwnedValue::Null;
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("not a time");
let end = OwnedValue::build_text("12:00:00");
let expected = OwnedValue::Null;
assert_eq!(
exec_timediff(&[Register::OwnedValue(start), Register::OwnedValue(end)]),
expected
);
let start = OwnedValue::build_text("12:00:00");
let expected = OwnedValue::Null;
assert_eq!(exec_timediff(&[Register::OwnedValue(start)]), expected);
}
} }

View File

@@ -1309,6 +1309,33 @@ pub fn translate_expr(
}); });
Ok(target_register) Ok(target_register)
} }
ScalarFunc::TimeDiff => {
let args = expect_arguments_exact!(args, 2, srf);
let start_reg = program.alloc_registers(2);
translate_expr(
program,
referenced_tables,
&args[0],
start_reg,
resolver,
)?;
translate_expr(
program,
referenced_tables,
&args[1],
start_reg + 1,
resolver,
)?;
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg,
dest: target_register,
func: func_ctx,
});
Ok(target_register)
}
ScalarFunc::TotalChanges => { ScalarFunc::TotalChanges => {
if args.is_some() { if args.is_some() {
crate::bail_parse_error!( crate::bail_parse_error!(

View File

@@ -3406,6 +3406,21 @@ pub fn op_function(
let result = exec_time(values); let result = exec_time(values);
state.registers[*dest] = Register::OwnedValue(result); state.registers[*dest] = Register::OwnedValue(result);
} }
ScalarFunc::TimeDiff => {
if arg_count != 2 {
state.registers[*dest] = Register::OwnedValue(OwnedValue::Null);
} else {
let start = state.registers[*start_reg].get_owned_value().clone();
let end = state.registers[*start_reg + 1].get_owned_value().clone();
let result = crate::functions::datetime::exec_timediff(&[
Register::OwnedValue(start),
Register::OwnedValue(end),
]);
state.registers[*dest] = Register::OwnedValue(result);
}
}
ScalarFunc::TotalChanges => { ScalarFunc::TotalChanges => {
let res = &program.connection.upgrade().unwrap().total_changes; let res = &program.connection.upgrade().unwrap().total_changes;
let total_changes = res.get(); let total_changes = res.get();