reifydb_routine/function/date/
age.rs1use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns};
5use reifydb_type::{
6 error::TypeError,
7 value::{container::temporal::TemporalContainer, date::Date, duration::Duration, r#type::Type},
8};
9
10use crate::routine::{Function, FunctionKind, Routine, RoutineInfo, context::FunctionContext, error::RoutineError};
11
12pub struct DateAge {
13 info: RoutineInfo,
14}
15
16impl Default for DateAge {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl DateAge {
23 pub fn new() -> Self {
24 Self {
25 info: RoutineInfo::new("date::age"),
26 }
27 }
28}
29
30pub fn date_age(d1: &Date, d2: &Date) -> Result<Duration, Box<TypeError>> {
31 let y1 = d1.year();
32 let m1 = d1.month() as i32;
33 let day1 = d1.day() as i32;
34
35 let y2 = d2.year();
36 let m2 = d2.month() as i32;
37 let day2 = d2.day() as i32;
38
39 let mut years = y1 - y2;
40 let mut months = m1 - m2;
41 let mut days = day1 - day2;
42
43 if days < 0 {
44 months -= 1;
45
46 let borrow_month = if m1 - 1 < 1 {
47 12
48 } else {
49 m1 - 1
50 };
51 let borrow_year = if m1 - 1 < 1 {
52 y1 - 1
53 } else {
54 y1
55 };
56 days += Date::days_in_month(borrow_year, borrow_month as u32) as i32;
57 }
58
59 if months < 0 {
60 years -= 1;
61 months += 12;
62 }
63
64 let total_months = years * 12 + months;
65 Duration::new(total_months, days, 0)
66}
67
68impl<'a> Routine<FunctionContext<'a>> for DateAge {
69 fn info(&self) -> &RoutineInfo {
70 &self.info
71 }
72
73 fn return_type(&self, _input_types: &[Type]) -> Type {
74 Type::Duration
75 }
76
77 fn execute(&self, ctx: &mut FunctionContext<'a>, args: &Columns) -> Result<Columns, RoutineError> {
78 if args.len() != 2 {
79 return Err(RoutineError::FunctionArityMismatch {
80 function: ctx.fragment.clone(),
81 expected: 2,
82 actual: args.len(),
83 });
84 }
85
86 let col1 = &args[0];
87 let col2 = &args[1];
88 let (data1, bitvec1) = col1.unwrap_option();
89 let (data2, bitvec2) = col2.unwrap_option();
90 let row_count = data1.len();
91
92 let result_data = match (data1, data2) {
93 (ColumnBuffer::Date(container1), ColumnBuffer::Date(container2)) => {
94 let mut container = TemporalContainer::with_capacity(row_count);
95
96 for i in 0..row_count {
97 match (container1.get(i), container2.get(i)) {
98 (Some(d1), Some(d2)) => {
99 container.push(date_age(d1, d2)?);
100 }
101 _ => container.push_default(),
102 }
103 }
104
105 ColumnBuffer::Duration(container)
106 }
107 (ColumnBuffer::Date(_), other) => {
108 return Err(RoutineError::FunctionInvalidArgumentType {
109 function: ctx.fragment.clone(),
110 argument_index: 1,
111 expected: vec![Type::Date],
112 actual: other.get_type(),
113 });
114 }
115 (other, _) => {
116 return Err(RoutineError::FunctionInvalidArgumentType {
117 function: ctx.fragment.clone(),
118 argument_index: 0,
119 expected: vec![Type::Date],
120 actual: other.get_type(),
121 });
122 }
123 };
124
125 let final_data = match (bitvec1, bitvec2) {
126 (Some(bv), _) | (_, Some(bv)) => ColumnBuffer::Option {
127 inner: Box::new(result_data),
128 bitvec: bv.clone(),
129 },
130 _ => result_data,
131 };
132
133 Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), final_data)]))
134 }
135}
136
137impl Function for DateAge {
138 fn kinds(&self) -> &[FunctionKind] {
139 &[FunctionKind::Scalar]
140 }
141}