From 76a1d91b1154818806ce16f963498b4d5902877a Mon Sep 17 00:00:00 2001 From: Stefan Date: Thu, 22 May 2025 23:44:23 +0200 Subject: [PATCH] feat: dates min/max --- core/expression/src/functions/internal.rs | 77 +++++++++++++++++++---- core/expression/tests/data/date.csv | 28 ++++++++- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/core/expression/src/functions/internal.rs b/core/expression/src/functions/internal.rs index abce92ad..cd429aa8 100644 --- a/core/expression/src/functions/internal.rs +++ b/core/expression/src/functions/internal.rs @@ -212,14 +212,20 @@ impl From<&InternalFunction> for Rc { signature: FunctionSignature::single(VT::Number.array(), VT::Number), }), - IF::Min => Rc::new(StaticFunction { + IF::Min => Rc::new(CompositeFunction { implementation: Rc::new(imp::min), - signature: FunctionSignature::single(VT::Number.array(), VT::Number), + signatures: vec![ + FunctionSignature::single(VT::Number.array(), VT::Number), + FunctionSignature::single(VT::Date.array(), VT::Date), + ], }), - IF::Max => Rc::new(StaticFunction { + IF::Max => Rc::new(CompositeFunction { implementation: Rc::new(imp::max), - signature: FunctionSignature::single(VT::Number.array(), VT::Number), + signatures: vec![ + FunctionSignature::single(VT::Number.array(), VT::Number), + FunctionSignature::single(VT::Date.array(), VT::Date), + ], }), IF::Median => Rc::new(StaticFunction { @@ -299,7 +305,7 @@ impl From<&InternalFunction> for Rc { pub(crate) mod imp { use crate::functions::arguments::Arguments; use crate::vm::VmDate; - use crate::Variable as V; + use crate::{Variable as V, Variable}; use anyhow::{anyhow, Context}; use chrono_tz::Tz; #[cfg(not(feature = "regex-lite"))] @@ -323,6 +329,39 @@ pub(crate) mod imp { .context("Expected a number array") } + enum Either { + Left(A), + Right(B), + } + + fn __internal_number_or_date_array( + args: &Arguments, + pos: usize, + ) -> anyhow::Result, Vec>> { + let a = args.array(pos)?; + let arr = a.borrow(); + + let is_number = arr.first().map(|v| v.as_number()).flatten().is_some(); + if is_number { + Ok(Either::Left( + arr.iter() + .map(|v| v.as_number()) + .collect::>>() + .context("Expected a number array")?, + )) + } else { + Ok(Either::Right( + arr.iter() + .map(|v| match v { + Variable::Dynamic(d) => d.as_date().cloned(), + _ => None, + }) + .collect::>>() + .context("Expected a number array")?, + )) + } + } + pub fn starts_with(args: Arguments) -> anyhow::Result { let a = args.str(0)?; let b = args.str(1)?; @@ -457,17 +496,33 @@ pub(crate) mod imp { } pub fn min(args: Arguments) -> anyhow::Result { - let a = __internal_number_array(&args, 0)?; - let min = a.iter().min().context("Empty array")?; + let a = __internal_number_or_date_array(&args, 0)?; - Ok(V::Number(Decimal::from(*min))) + match a { + Either::Left(arr) => { + let max = arr.into_iter().min().context("Empty array")?; + Ok(V::Number(Decimal::from(max))) + } + Either::Right(arr) => { + let max = arr.into_iter().min().context("Empty array")?; + Ok(V::Dynamic(Rc::new(max))) + } + } } pub fn max(args: Arguments) -> anyhow::Result { - let a = __internal_number_array(&args, 0)?; - let max = a.iter().max().context("Empty array")?; + let a = __internal_number_or_date_array(&args, 0)?; - Ok(V::Number(Decimal::from(*max))) + match a { + Either::Left(arr) => { + let max = arr.into_iter().max().context("Empty array")?; + Ok(V::Number(Decimal::from(max))) + } + Either::Right(arr) => { + let max = arr.into_iter().max().context("Empty array")?; + Ok(V::Dynamic(Rc::new(max))) + } + } } pub fn avg(args: Arguments) -> anyhow::Result { diff --git a/core/expression/tests/data/date.csv b/core/expression/tests/data/date.csv index 6feed217..4699dccc 100644 --- a/core/expression/tests/data/date.csv +++ b/core/expression/tests/data/date.csv @@ -188,4 +188,30 @@ d('2023-10-31') not in (d('2023-10-01')..d('2023-10-31'));;true d('2023-10-15') not in (d('2023-10-01')..d('2023-10-31'));;false d('2023-10-31') not in [d('2023-10-01')..d('2023-10-31'));;true -d('2023-10-01') not in (d('2023-10-01')..d('2023-10-31')];;true \ No newline at end of file +d('2023-10-01') not in (d('2023-10-01')..d('2023-10-31')];;true + + +min([d('2023-01-15'), d('2023-03-20'), d('2023-02-10')]);;'2023-01-15T00:00:00Z' +max([d('2023-01-15'), d('2023-03-20'), d('2023-02-10')]);;'2023-03-20T00:00:00Z' +min([d('2023-10-15'), d('2023-10-01'), d('2023-10-31')]);;'2023-10-01T00:00:00Z' +max([d('2023-10-15'), d('2023-10-01'), d('2023-10-31')]);;'2023-10-31T00:00:00Z' +min([d('2023-10-15T10:30:00Z'), d('2023-10-15T08:15:00Z'), d('2023-10-15T14:45:00Z')]);;'2023-10-15T08:15:00Z' +max([d('2023-10-15T10:30:00Z'), d('2023-10-15T08:15:00Z'), d('2023-10-15T14:45:00Z')]);;'2023-10-15T14:45:00Z' +min([d('2022-12-31'), d('2023-01-01'), d('2024-01-01')]);;'2022-12-31T00:00:00Z' +max([d('2022-12-31'), d('2023-01-01'), d('2024-01-01')]);;'2024-01-01T00:00:00Z' +min([d('2023-10-15'), d('2023-10-15'), d('2023-10-15')]);;'2023-10-15T00:00:00Z' +max([d('2023-10-15'), d('2023-10-15'), d('2023-10-15')]);;'2023-10-15T00:00:00Z' +min([d('2023-10-15')]);;'2023-10-15T00:00:00Z' +max([d('2023-10-15')]);;'2023-10-15T00:00:00Z' +min([d('2023-10-15T10:00:00Z'), d('2023-10-15T12:00:00Z')]);;'2023-10-15T10:00:00Z' +max([d('2023-10-15T10:00:00Z'), d('2023-10-15T12:00:00Z')]);;'2023-10-15T12:00:00Z' +min([d('1900-01-01'), d('2000-01-01'), d('1950-06-15')]);;'1900-01-01T00:00:00Z' +max([d('1900-01-01'), d('2000-01-01'), d('1950-06-15')]);;'2000-01-01T00:00:00Z' +min([d('2024-02-29'), d('2023-02-28'), d('2024-03-01')]);;'2023-02-28T00:00:00Z' +max([d('2024-02-29'), d('2023-02-28'), d('2024-03-01')]);;'2024-03-01T00:00:00Z' +min([d('2023-01-31'), d('2023-02-28'), d('2023-03-31')]);;'2023-01-31T00:00:00Z' +max([d('2023-01-31'), d('2023-02-28'), d('2023-03-31')]);;'2023-03-31T00:00:00Z' +min([d('2023-10-15').add(1, 'd'), d('2023-10-15').sub(1, 'd'), d('2023-10-15')]);;'2023-10-14T00:00:00Z' +max([d('2023-10-15').add(1, 'd'), d('2023-10-15').sub(1, 'd'), d('2023-10-15')]);;'2023-10-16T00:00:00Z' +min([d('2023-01-01'), d('2023-12-31')]).isBefore(d('2023-06-01'));;true +max([d('2023-01-01'), d('2023-12-31')]).isAfter(d('2023-06-01'));;true \ No newline at end of file