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							// Extract time nanos since midnight
59							let nanos1 = dt1.time().to_nanos_since_midnight() as i64;
60							let nanos2 = dt2.time().to_nanos_since_midnight() as i64;
61							let mut nanos_diff = nanos1 - nanos2;
62							let mut days_borrow: i32 = 0;
63
64							if nanos_diff < 0 {
65								days_borrow = 1;
66								nanos_diff += 86_400_000_000_000;
67							}
68
69							// Extract date parts
70							let date1 = dt1.date();
71							let date2 = dt2.date();
72
73							let y1 = date1.year();
74							let m1 = date1.month() as i32;
75							let day1 = date1.day() as i32;
76
77							let y2 = date2.year();
78							let m2 = date2.month() as i32;
79							let day2 = date2.day() as i32;
80
81							let mut years = y1 - y2;
82							let mut months = m1 - m2;
83							let mut days = day1 - day2 - days_borrow;
84
85							if days < 0 {
86								months -= 1;
87								let borrow_month = if m1 - 1 < 1 {
88									12
89								} else {
90									m1 - 1
91								};
92								let borrow_year = if m1 - 1 < 1 {
93									y1 - 1
94								} else {
95									y1
96								};
97								days += Date::days_in_month(
98									borrow_year,
99									borrow_month as u32,
100								) as i32;
101							}
102
103							if months < 0 {
104								years -= 1;
105								months += 12;
106							}
107
108							let total_months = years * 12 + months;
109							container.push(Duration::new(total_months, days, nanos_diff)?);
110						}
111						_ => container.push_default(),
112					}
113				}
114
115				ColumnBuffer::Duration(container)
116			}
117			(ColumnBuffer::DateTime(_), other) => {
118				return Err(RoutineError::FunctionInvalidArgumentType {
119					function: ctx.fragment.clone(),
120					argument_index: 1,
121					expected: vec![Type::DateTime],
122					actual: other.get_type(),
123				});
124			}
125			(other, _) => {
126				return Err(RoutineError::FunctionInvalidArgumentType {
127					function: ctx.fragment.clone(),
128					argument_index: 0,
129					expected: vec![Type::DateTime],
130					actual: other.get_type(),
131				});
132			}
133		};
134
135		let final_data = match (bitvec1, bitvec2) {
136			(Some(bv), _) | (_, Some(bv)) => ColumnBuffer::Option {
137				inner: Box::new(result_data),
138				bitvec: bv.clone(),
139			},
140			_ => result_data,
141		};
142
143		Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), final_data)]))
144	}
145}
146
147impl Function for DateTimeAge {
148	fn kinds(&self) -> &[FunctionKind] {
149		&[FunctionKind::Scalar]
150	}
151}