diff --git a/lib/src/fnc/mod.rs b/lib/src/fnc/mod.rs index 1c5fcd92..6c0a7f25 100644 --- a/lib/src/fnc/mod.rs +++ b/lib/src/fnc/mod.rs @@ -225,6 +225,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec) -> Result string::uppercase, "string::words" => string::words, // + "time::ceil" => time::ceil, "time::day" => time::day, "time::floor" => time::floor, "time::format" => time::format, diff --git a/lib/src/fnc/script/modules/surrealdb/functions/time.rs b/lib/src/fnc/script/modules/surrealdb/functions/time.rs index cd6a8440..63b61f08 100644 --- a/lib/src/fnc/script/modules/surrealdb/functions/time.rs +++ b/lib/src/fnc/script/modules/surrealdb/functions/time.rs @@ -8,6 +8,7 @@ pub struct Package; impl_module_def!( Package, "time", + "ceil" => run, "day" => run, "floor" => run, "format" => run, diff --git a/lib/src/fnc/time.rs b/lib/src/fnc/time.rs index 7b71cf14..17e8547e 100644 --- a/lib/src/fnc/time.rs +++ b/lib/src/fnc/time.rs @@ -9,6 +9,22 @@ use chrono::Local; use chrono::Timelike; use chrono::Utc; +pub fn ceil((val, duration): (Datetime, Duration)) -> Result { + match chrono::Duration::from_std(*duration) { + Ok(d) => match val.duration_trunc(d).ok().and_then(|floor| floor.checked_add_signed(d)) { + Some(v) => Ok(v.into()), + _ => Err(Error::InvalidArguments { + name: String::from("time::ceil"), + message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."), + }), + }, + _ => Err(Error::InvalidArguments { + name: String::from("time::ceil"), + message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."), + }), + } +} + pub fn day((val,): (Option,)) -> Result { Ok(match val { Some(v) => v.day().into(), @@ -18,7 +34,7 @@ pub fn day((val,): (Option,)) -> Result { pub fn floor((val, duration): (Datetime, Duration)) -> Result { match chrono::Duration::from_std(*duration) { - Ok(d) => match val.duration_trunc(d) { + Ok(d) => match val.duration_trunc(d){ Ok(v) => Ok(v.into()), _ => Err(Error::InvalidArguments { name: String::from("time::floor"), diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index 92a82abb..04398c7b 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -514,6 +514,7 @@ fn function_string(i: &str) -> IResult<&str, &str> { fn function_time(i: &str) -> IResult<&str, &str> { alt(( + tag("ceil"), tag("day"), tag("floor"), tag("format"), diff --git a/lib/tests/function.rs b/lib/tests/function.rs index 63c88225..a6e96cf8 100644 --- a/lib/tests/function.rs +++ b/lib/tests/function.rs @@ -3554,6 +3554,28 @@ async fn function_string_words() -> Result<(), Error> { // time // -------------------------------------------------- +#[tokio::test] +async fn function_time_ceil() -> Result<(), Error> { + let sql = r#" + RETURN time::ceil("1987-06-22T08:30:45Z", 1w); + RETURN time::ceil("1987-06-22T08:30:45Z", 1y); + "#; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result?; + let val = Value::parse("'1987-06-25T00:00:00Z'"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("'1987-12-28T00:00:00Z'"); + assert_eq!(tmp, val); + // + Ok(()) +} + #[tokio::test] async fn function_time_day() -> Result<(), Error> { let sql = r#"