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