Skip to main content

reifydb_routine/function/datetime/
age.rs

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