reifydb_core/value/encoded/
date.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use std::ptr;
5
6use reifydb_type::{Date, Type};
7
8use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
9
10impl EncodedValuesLayout {
11	pub fn set_date(&self, row: &mut EncodedValues, index: usize, value: Date) {
12		let field = &self.fields[index];
13		debug_assert!(row.len() >= self.total_static_size());
14		debug_assert_eq!(field.r#type, Type::Date);
15		row.set_valid(index, true);
16		unsafe {
17			ptr::write_unaligned(
18				row.make_mut().as_mut_ptr().add(field.offset) as *mut i32,
19				value.to_days_since_epoch(),
20			)
21		}
22	}
23
24	pub fn get_date(&self, row: &EncodedValues, index: usize) -> Date {
25		let field = &self.fields[index];
26		debug_assert!(row.len() >= self.total_static_size());
27		debug_assert_eq!(field.r#type, Type::Date);
28		unsafe {
29			Date::from_days_since_epoch((row.as_ptr().add(field.offset) as *const i32).read_unaligned())
30				.unwrap()
31		}
32	}
33
34	pub fn try_get_date(&self, row: &EncodedValues, index: usize) -> Option<Date> {
35		if row.is_defined(index) && self.fields[index].r#type == Type::Date {
36			Some(self.get_date(row, index))
37		} else {
38			None
39		}
40	}
41}
42
43#[cfg(test)]
44mod tests {
45	use reifydb_type::{Date, Type};
46
47	use crate::value::encoded::EncodedValuesLayout;
48
49	#[test]
50	fn test_set_get_date() {
51		let layout = EncodedValuesLayout::new(&[Type::Date]);
52		let mut row = layout.allocate();
53
54		let value = Date::new(2021, 1, 1).unwrap();
55		layout.set_date(&mut row, 0, value.clone());
56		assert_eq!(layout.get_date(&row, 0), value);
57	}
58
59	#[test]
60	fn test_try_get_date() {
61		let layout = EncodedValuesLayout::new(&[Type::Date]);
62		let mut row = layout.allocate();
63
64		assert_eq!(layout.try_get_date(&row, 0), None);
65
66		let test_date = Date::from_ymd(2025, 1, 15).unwrap();
67		layout.set_date(&mut row, 0, test_date.clone());
68		assert_eq!(layout.try_get_date(&row, 0), Some(test_date));
69	}
70
71	#[test]
72	fn test_epoch() {
73		let layout = EncodedValuesLayout::new(&[Type::Date]);
74		let mut row = layout.allocate();
75
76		let epoch = Date::default(); // Unix epoch
77		layout.set_date(&mut row, 0, epoch.clone());
78		assert_eq!(layout.get_date(&row, 0), epoch);
79	}
80
81	#[test]
82	fn test_various_dates() {
83		let layout = EncodedValuesLayout::new(&[Type::Date]);
84
85		let test_dates = [
86			Date::new(1970, 1, 1).unwrap(),   // Unix epoch
87			Date::new(2000, 1, 1).unwrap(),   // Y2K
88			Date::new(2024, 2, 29).unwrap(),  // Leap year
89			Date::new(2025, 12, 31).unwrap(), // Future date
90		];
91
92		for date in test_dates {
93			let mut row = layout.allocate();
94			layout.set_date(&mut row, 0, date.clone());
95			assert_eq!(layout.get_date(&row, 0), date);
96		}
97	}
98
99	#[test]
100	fn test_boundaries() {
101		let layout = EncodedValuesLayout::new(&[Type::Date]);
102
103		// Test various boundary dates that should work
104		let boundary_dates = [
105			Date::new(1900, 1, 1).unwrap(),
106			Date::new(1999, 12, 31).unwrap(),
107			Date::new(2000, 1, 1).unwrap(),
108			Date::new(2100, 12, 31).unwrap(),
109		];
110
111		for date in boundary_dates {
112			let mut row = layout.allocate();
113			layout.set_date(&mut row, 0, date.clone());
114			assert_eq!(layout.get_date(&row, 0), date);
115		}
116	}
117
118	#[test]
119	fn test_mixed_with_other_types() {
120		let layout = EncodedValuesLayout::new(&[Type::Date, Type::Boolean, Type::Date, Type::Int4]);
121		let mut row = layout.allocate();
122
123		let date1 = Date::new(2025, 6, 15).unwrap();
124		let date2 = Date::new(1995, 3, 22).unwrap();
125
126		layout.set_date(&mut row, 0, date1.clone());
127		layout.set_bool(&mut row, 1, true);
128		layout.set_date(&mut row, 2, date2.clone());
129		layout.set_i32(&mut row, 3, 42);
130
131		assert_eq!(layout.get_date(&row, 0), date1);
132		assert_eq!(layout.get_bool(&row, 1), true);
133		assert_eq!(layout.get_date(&row, 2), date2);
134		assert_eq!(layout.get_i32(&row, 3), 42);
135	}
136
137	#[test]
138	fn test_undefined_handling() {
139		let layout = EncodedValuesLayout::new(&[Type::Date, Type::Date]);
140		let mut row = layout.allocate();
141
142		let date = Date::new(2025, 7, 4).unwrap();
143		layout.set_date(&mut row, 0, date.clone());
144
145		assert_eq!(layout.try_get_date(&row, 0), Some(date));
146		assert_eq!(layout.try_get_date(&row, 1), None);
147
148		layout.set_undefined(&mut row, 0);
149		assert_eq!(layout.try_get_date(&row, 0), None);
150	}
151
152	#[test]
153	fn test_clone_consistency() {
154		let layout = EncodedValuesLayout::new(&[Type::Date]);
155		let mut row = layout.allocate();
156
157		let original_date = Date::new(2023, 9, 15).unwrap();
158		layout.set_date(&mut row, 0, original_date.clone());
159
160		let retrieved_date = layout.get_date(&row, 0);
161		assert_eq!(retrieved_date, original_date);
162
163		// Verify that the retrieved date is functionally equivalent
164		assert_eq!(retrieved_date.to_days_since_epoch(), original_date.to_days_since_epoch());
165	}
166
167	#[test]
168	fn test_special_years() {
169		let layout = EncodedValuesLayout::new(&[Type::Date]);
170
171		// Test leap years and century boundaries
172		let special_dates = [
173			Date::new(1600, 2, 29).unwrap(), // Leap year century
174			Date::new(1700, 2, 28).unwrap(), // Non-leap century
175			Date::new(1800, 2, 28).unwrap(), // Non-leap century
176			Date::new(1900, 2, 28).unwrap(), // Non-leap century
177			Date::new(2000, 2, 29).unwrap(), // Leap year century
178			Date::new(2024, 2, 29).unwrap(), // Recent leap year
179		];
180
181		for date in special_dates {
182			let mut row = layout.allocate();
183			layout.set_date(&mut row, 0, date.clone());
184			assert_eq!(layout.get_date(&row, 0), date);
185		}
186	}
187
188	#[test]
189	fn test_try_get_date_wrong_type() {
190		let layout = EncodedValuesLayout::new(&[Type::Boolean]);
191		let mut row = layout.allocate();
192
193		layout.set_bool(&mut row, 0, true);
194
195		assert_eq!(layout.try_get_date(&row, 0), None);
196	}
197}