reifydb_routine/function/date/
age.rs1use reifydb_core::value::column::data::ColumnData;
5use reifydb_type::{
6 error::TypeError,
7 value::{container::temporal::TemporalContainer, date::Date, duration::Duration, r#type::Type},
8};
9
10use crate::function::{
11 ScalarFunction, ScalarFunctionContext,
12 error::{ScalarFunctionError, ScalarFunctionResult},
13 propagate_options,
14};
15
16pub struct DateAge;
17
18impl DateAge {
19 pub fn new() -> Self {
20 Self
21 }
22}
23
24pub fn date_age(d1: &Date, d2: &Date) -> Result<Duration, TypeError> {
27 let y1 = d1.year();
28 let m1 = d1.month() as i32;
29 let day1 = d1.day() as i32;
30
31 let y2 = d2.year();
32 let m2 = d2.month() as i32;
33 let day2 = d2.day() as i32;
34
35 let mut years = y1 - y2;
36 let mut months = m1 - m2;
37 let mut days = day1 - day2;
38
39 if days < 0 {
40 months -= 1;
41 let borrow_month = if m1 - 1 < 1 {
43 12
44 } else {
45 m1 - 1
46 };
47 let borrow_year = if m1 - 1 < 1 {
48 y1 - 1
49 } else {
50 y1
51 };
52 days += Date::days_in_month(borrow_year, borrow_month as u32) as i32;
53 }
54
55 if months < 0 {
56 years -= 1;
57 months += 12;
58 }
59
60 let total_months = years * 12 + months;
61 Duration::new(total_months, days, 0)
62}
63
64impl ScalarFunction for DateAge {
65 fn scalar(&self, ctx: ScalarFunctionContext) -> ScalarFunctionResult<ColumnData> {
66 if let Some(result) = propagate_options(self, &ctx) {
67 return result;
68 }
69
70 let columns = ctx.columns;
71 let row_count = ctx.row_count;
72
73 if columns.len() != 2 {
74 return Err(ScalarFunctionError::ArityMismatch {
75 function: ctx.fragment.clone(),
76 expected: 2,
77 actual: columns.len(),
78 });
79 }
80
81 let col1 = columns.get(0).unwrap();
82 let col2 = columns.get(1).unwrap();
83
84 match (col1.data(), col2.data()) {
85 (ColumnData::Date(container1), ColumnData::Date(container2)) => {
86 let mut container = TemporalContainer::with_capacity(row_count);
87
88 for i in 0..row_count {
89 match (container1.get(i), container2.get(i)) {
90 (Some(d1), Some(d2)) => {
91 container.push(date_age(&d1, &d2)?);
92 }
93 _ => container.push_default(),
94 }
95 }
96
97 Ok(ColumnData::Duration(container))
98 }
99 (ColumnData::Date(_), other) => Err(ScalarFunctionError::InvalidArgumentType {
100 function: ctx.fragment.clone(),
101 argument_index: 1,
102 expected: vec![Type::Date],
103 actual: other.get_type(),
104 }),
105 (other, _) => Err(ScalarFunctionError::InvalidArgumentType {
106 function: ctx.fragment.clone(),
107 argument_index: 0,
108 expected: vec![Type::Date],
109 actual: other.get_type(),
110 }),
111 }
112 }
113
114 fn return_type(&self, _input_types: &[Type]) -> Type {
115 Type::Duration
116 }
117}