reifydb_routine/function/datetime/
trunc.rs1use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns};
5use reifydb_type::value::{container::temporal::TemporalContainer, datetime::DateTime, r#type::Type};
6
7use crate::routine::{Function, FunctionKind, Routine, RoutineInfo, context::FunctionContext, error::RoutineError};
8
9pub struct DateTimeTrunc {
10 info: RoutineInfo,
11}
12
13impl Default for DateTimeTrunc {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl DateTimeTrunc {
20 pub fn new() -> Self {
21 Self {
22 info: RoutineInfo::new("datetime::trunc"),
23 }
24 }
25}
26
27impl<'a> Routine<FunctionContext<'a>> for DateTimeTrunc {
28 fn info(&self) -> &RoutineInfo {
29 &self.info
30 }
31
32 fn return_type(&self, _input_types: &[Type]) -> Type {
33 Type::DateTime
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 dt_col = &args[0];
46 let prec_col = &args[1];
47 let (dt_data, dt_bitvec) = dt_col.unwrap_option();
48 let (prec_data, prec_bitvec) = prec_col.unwrap_option();
49 let row_count = dt_data.len();
50
51 let result_data = match (dt_data, prec_data) {
52 (
53 ColumnBuffer::DateTime(dt_container),
54 ColumnBuffer::Utf8 {
55 container: prec_container,
56 ..
57 },
58 ) => {
59 let mut container = TemporalContainer::with_capacity(row_count);
60
61 for i in 0..row_count {
62 match (dt_container.get(i), prec_container.is_defined(i)) {
63 (Some(dt), true) => {
64 let precision = prec_container.get(i).unwrap();
65 let truncated = match precision {
66 "year" => DateTime::new(dt.year(), 1, 1, 0, 0, 0, 0),
67 "month" => DateTime::new(
68 dt.year(),
69 dt.month(),
70 1,
71 0,
72 0,
73 0,
74 0,
75 ),
76 "day" => DateTime::new(
77 dt.year(),
78 dt.month(),
79 dt.day(),
80 0,
81 0,
82 0,
83 0,
84 ),
85 "hour" => DateTime::new(
86 dt.year(),
87 dt.month(),
88 dt.day(),
89 dt.hour(),
90 0,
91 0,
92 0,
93 ),
94 "minute" => DateTime::new(
95 dt.year(),
96 dt.month(),
97 dt.day(),
98 dt.hour(),
99 dt.minute(),
100 0,
101 0,
102 ),
103 "second" => DateTime::new(
104 dt.year(),
105 dt.month(),
106 dt.day(),
107 dt.hour(),
108 dt.minute(),
109 dt.second(),
110 0,
111 ),
112 other => {
113 return Err(
114 RoutineError::FunctionExecutionFailed {
115 function: ctx.fragment.clone(),
116 reason: format!(
117 "invalid precision: '{}'",
118 other
119 ),
120 },
121 );
122 }
123 };
124 match truncated {
125 Some(val) => container.push(val),
126 None => container.push_default(),
127 }
128 }
129 _ => container.push_default(),
130 }
131 }
132
133 ColumnBuffer::DateTime(container)
134 }
135 (ColumnBuffer::DateTime(_), other) => {
136 return Err(RoutineError::FunctionInvalidArgumentType {
137 function: ctx.fragment.clone(),
138 argument_index: 1,
139 expected: vec![Type::Utf8],
140 actual: other.get_type(),
141 });
142 }
143 (other, _) => {
144 return Err(RoutineError::FunctionInvalidArgumentType {
145 function: ctx.fragment.clone(),
146 argument_index: 0,
147 expected: vec![Type::DateTime],
148 actual: other.get_type(),
149 });
150 }
151 };
152
153 let final_data = match (dt_bitvec, prec_bitvec) {
154 (Some(bv), _) | (_, Some(bv)) => ColumnBuffer::Option {
155 inner: Box::new(result_data),
156 bitvec: bv.clone(),
157 },
158 _ => result_data,
159 };
160
161 Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), final_data)]))
162 }
163}
164
165impl Function for DateTimeTrunc {
166 fn kinds(&self) -> &[FunctionKind] {
167 &[FunctionKind::Scalar]
168 }
169}