diff --git a/common/functions/src/scalars/dates/date.rs b/common/functions/src/scalars/dates/date.rs index 2fbda31da6c2e..ddaf8551f6ed1 100644 --- a/common/functions/src/scalars/dates/date.rs +++ b/common/functions/src/scalars/dates/date.rs @@ -16,6 +16,8 @@ use common_exception::Result; use super::now::NowFunction; use super::RoundFunction; +use super::ToStartOfISOYearFunction; +use super::ToStartOfYearFunction; use super::ToYYYYMMDDFunction; use super::ToYYYYMMDDhhmmssFunction; use super::ToYYYYMMFunction; @@ -40,6 +42,11 @@ impl DateFunction { "toYYYYMMDDhhmmss".into(), ToYYYYMMDDhhmmssFunction::try_create, ); + map.insert("toStartOfYear".into(), ToStartOfYearFunction::try_create); + map.insert( + "toStartOfISOYear".into(), + ToStartOfISOYearFunction::try_create, + ); // rounders { diff --git a/common/functions/src/scalars/dates/mod.rs b/common/functions/src/scalars/dates/mod.rs index e9cb3100d6eef..76ba4adb142db 100644 --- a/common/functions/src/scalars/dates/mod.rs +++ b/common/functions/src/scalars/dates/mod.rs @@ -24,6 +24,8 @@ mod round_function; mod simple_date; pub use date::DateFunction; +pub use number_function::ToStartOfISOYearFunction; +pub use number_function::ToStartOfYearFunction; pub use number_function::ToYYYYMMDDFunction; pub use number_function::ToYYYYMMDDhhmmssFunction; pub use number_function::ToYYYYMMFunction; diff --git a/common/functions/src/scalars/dates/number_function.rs b/common/functions/src/scalars/dates/number_function.rs index 28d83872ce941..a1ab9f44f5dfd 100644 --- a/common/functions/src/scalars/dates/number_function.rs +++ b/common/functions/src/scalars/dates/number_function.rs @@ -93,6 +93,49 @@ impl NumberResultFunction for ToYYYYMMDDhhmmss { } } +#[derive(Clone)] +pub struct ToStartOfYear; + +impl NumberResultFunction for ToStartOfYear { + fn return_type() -> Result { + Ok(DataType::Date16) + } + fn to_number(value: DateTime) -> u32 { + let start: DateTime = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let end: DateTime = Utc.ymd(value.year(), 1, 1).and_hms(0, 0, 0); + let duration = end.signed_duration_since(start); + duration.num_days() as u32 + } + + fn to_constant_value(value: DateTime) -> DataValue { + DataValue::UInt16(Some(Self::to_number(value) as u16)) + } +} + +#[derive(Clone)] +pub struct ToStartOfISOYear; + +impl NumberResultFunction for ToStartOfISOYear { + fn return_type() -> Result { + Ok(DataType::Date16) + } + fn to_number(value: DateTime) -> u32 { + let start: DateTime = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let week_day = value.weekday().num_days_from_monday(); + let iso_week = value.iso_week(); + let iso_week_num = iso_week.week(); + let sub_days = (iso_week_num - 1) * 7 + week_day; + let result = value.timestamp_millis() - sub_days as i64 * 24 * 3600 * 1000; + let end: DateTime = Utc.timestamp_millis(result); + let duration = end.signed_duration_since(start); + duration.num_days() as u32 + } + + fn to_constant_value(value: DateTime) -> DataValue { + DataValue::UInt16(Some(Self::to_number(value) as u16)) + } +} + impl NumberFunction where T: NumberResultFunction + Clone + Sync + Send + 'static, @@ -146,7 +189,7 @@ where let date_time = Utc.timestamp(v as i64 * 24 * 3600, 0_u32); T::to_number(date_time) } - ); + ); Ok(result.into()) } }, @@ -163,7 +206,7 @@ where let date_time = Utc.timestamp(v as i64 * 24 * 3600, 0_u32); T::to_number(date_time) } - ); + ); Ok(result.into()) } }, @@ -180,14 +223,14 @@ where let date_time = Utc.timestamp(v as i64, 0_u32); T::to_number(date_time) } - ); + ); Ok(result.into()) } }, other => Result::Err(ErrorCode::IllegalDataType(format!( - "Illegal type {:?} of argument of function {}.Should be a date16/data32 or a dateTime32", + "Illegal type {:?} of argument of function {}.Should be a date16/data32 or a dateTime32", other, - self.name()))), + self.name()))), }?; Ok(number_array) } @@ -202,3 +245,5 @@ impl fmt::Display for NumberFunction { pub type ToYYYYMMFunction = NumberFunction; pub type ToYYYYMMDDFunction = NumberFunction; pub type ToYYYYMMDDhhmmssFunction = NumberFunction; +pub type ToStartOfISOYearFunction = NumberFunction; +pub type ToStartOfYearFunction = NumberFunction; diff --git a/tests/suites/0_stateless/02_0012_function_datetimes.result b/tests/suites/0_stateless/02_0012_function_datetimes.result index 67339c9b18c39..d88b86dddf144 100644 --- a/tests/suites/0_stateless/02_0012_function_datetimes.result +++ b/tests/suites/0_stateless/02_0012_function_datetimes.result @@ -38,3 +38,9 @@ 1 1 ===toYYYYMMDD=== +===toStartOf=== +2021-01-01 +2021-01-04 +2021-01-01 +2021-01-04 +===toStartOf=== diff --git a/tests/suites/0_stateless/02_0012_function_datetimes.sql b/tests/suites/0_stateless/02_0012_function_datetimes.sql index c65d94ec05228..5a5472d23a556 100644 --- a/tests/suites/0_stateless/02_0012_function_datetimes.sql +++ b/tests/suites/0_stateless/02_0012_function_datetimes.sql @@ -46,4 +46,9 @@ select toYYYYMMDD(toDateTime(1630833797)) = 20210905; select toYYYYMMDD(toDate(18875)) = 20210905; select '===toYYYYMMDD==='; - +select '===toStartOf==='; +select toStartOfYear(toDateTime(1630812366)); +select toStartOfISOYear(toDateTime(1630812366)); +select toStartOfYear(toDate(18869)); +select toStartOfISOYear(toDate(18869)); +select '===toStartOf===';