Skip to main content

reifydb_core/encoded/
time.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use std::ptr;
5
6use reifydb_type::value::{time::Time, r#type::Type};
7
8use crate::encoded::{encoded::EncodedValues, schema::Schema};
9
10impl Schema {
11	pub fn set_time(&self, row: &mut EncodedValues, index: usize, value: Time) {
12		let field = &self.fields()[index];
13		debug_assert!(row.len() >= self.total_static_size());
14		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Time);
15		row.set_valid(index, true);
16		unsafe {
17			ptr::write_unaligned(
18				row.make_mut().as_mut_ptr().add(field.offset as usize) as *mut u64,
19				value.to_nanos_since_midnight(),
20			)
21		}
22	}
23
24	pub fn get_time(&self, row: &EncodedValues, index: usize) -> Time {
25		let field = &self.fields()[index];
26		debug_assert!(row.len() >= self.total_static_size());
27		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Time);
28		unsafe {
29			Time::from_nanos_since_midnight(
30				(row.as_ptr().add(field.offset as usize) as *const u64).read_unaligned(),
31			)
32			.unwrap()
33		}
34	}
35
36	pub fn try_get_time(&self, row: &EncodedValues, index: usize) -> Option<Time> {
37		if row.is_defined(index) && self.fields()[index].constraint.get_type() == Type::Time {
38			Some(self.get_time(row, index))
39		} else {
40			None
41		}
42	}
43}
44
45#[cfg(test)]
46pub mod tests {
47	use reifydb_type::value::{time::Time, r#type::Type};
48
49	use crate::encoded::schema::Schema;
50
51	#[test]
52	fn test_set_get_time() {
53		let schema = Schema::testing(&[Type::Time]);
54		let mut row = schema.allocate();
55
56		let value = Time::new(20, 50, 0, 0).unwrap();
57		schema.set_time(&mut row, 0, value.clone());
58		assert_eq!(schema.get_time(&row, 0), value);
59	}
60
61	#[test]
62	fn test_try_get_time() {
63		let schema = Schema::testing(&[Type::Time]);
64		let mut row = schema.allocate();
65
66		assert_eq!(schema.try_get_time(&row, 0), None);
67
68		let test_time = Time::from_hms(14, 30, 45).unwrap();
69		schema.set_time(&mut row, 0, test_time.clone());
70		assert_eq!(schema.try_get_time(&row, 0), Some(test_time));
71	}
72
73	#[test]
74	fn test_time_midnight() {
75		let schema = Schema::testing(&[Type::Time]);
76		let mut row = schema.allocate();
77
78		let midnight = Time::default(); // 00:00:00
79		schema.set_time(&mut row, 0, midnight.clone());
80		assert_eq!(schema.get_time(&row, 0), midnight);
81	}
82
83	#[test]
84	fn test_time_with_nanoseconds() {
85		let schema = Schema::testing(&[Type::Time]);
86		let mut row = schema.allocate();
87
88		// Test with high precision nanoseconds
89		let precise_time = Time::new(15, 30, 45, 123456789).unwrap();
90		schema.set_time(&mut row, 0, precise_time.clone());
91		assert_eq!(schema.get_time(&row, 0), precise_time);
92	}
93
94	#[test]
95	fn test_time_various_times() {
96		let schema = Schema::testing(&[Type::Time]);
97
98		let test_times = [
99			Time::new(0, 0, 0, 0).unwrap(),            // Midnight
100			Time::new(12, 0, 0, 0).unwrap(),           // Noon
101			Time::new(23, 59, 59, 999999999).unwrap(), // Just before midnight
102			Time::new(6, 30, 15, 500000000).unwrap(),  // Morning time
103			Time::new(18, 45, 30, 750000000).unwrap(), // Evening time
104		];
105
106		for time in test_times {
107			let mut row = schema.allocate();
108			schema.set_time(&mut row, 0, time.clone());
109			assert_eq!(schema.get_time(&row, 0), time);
110		}
111	}
112
113	#[test]
114	fn test_time_boundary_cases() {
115		let schema = Schema::testing(&[Type::Time]);
116
117		let boundary_times = [
118			Time::new(0, 0, 0, 0).unwrap(), // Start of day
119			Time::new(0, 0, 0, 1).unwrap(), /* One nanosecond
120			                                 * after midnight */
121			Time::new(23, 59, 59, 999999998).unwrap(), // One nanosecond before midnight
122			Time::new(23, 59, 59, 999999999).unwrap(), // Last nanosecond of day
123		];
124
125		for time in boundary_times {
126			let mut row = schema.allocate();
127			schema.set_time(&mut row, 0, time.clone());
128			assert_eq!(schema.get_time(&row, 0), time);
129		}
130	}
131
132	#[test]
133	fn test_time_mixed_with_other_types() {
134		let schema = Schema::testing(&[Type::Time, Type::Boolean, Type::Time, Type::Int4]);
135		let mut row = schema.allocate();
136
137		let time1 = Time::new(9, 15, 30, 0).unwrap();
138		let time2 = Time::new(21, 45, 0, 250000000).unwrap();
139
140		schema.set_time(&mut row, 0, time1.clone());
141		schema.set_bool(&mut row, 1, false);
142		schema.set_time(&mut row, 2, time2.clone());
143		schema.set_i32(&mut row, 3, -999);
144
145		assert_eq!(schema.get_time(&row, 0), time1);
146		assert_eq!(schema.get_bool(&row, 1), false);
147		assert_eq!(schema.get_time(&row, 2), time2);
148		assert_eq!(schema.get_i32(&row, 3), -999);
149	}
150
151	#[test]
152	fn test_time_undefined_handling() {
153		let schema = Schema::testing(&[Type::Time, Type::Time]);
154		let mut row = schema.allocate();
155
156		let time = Time::new(16, 20, 45, 333000000).unwrap();
157		schema.set_time(&mut row, 0, time.clone());
158
159		assert_eq!(schema.try_get_time(&row, 0), Some(time));
160		assert_eq!(schema.try_get_time(&row, 1), None);
161
162		schema.set_none(&mut row, 0);
163		assert_eq!(schema.try_get_time(&row, 0), None);
164	}
165
166	#[test]
167	fn test_time_precision_preservation() {
168		let schema = Schema::testing(&[Type::Time]);
169		let mut row = schema.allocate();
170
171		// Test that nanosecond precision is preserved
172		let high_precision = Time::new(12, 34, 56, 987654321).unwrap();
173		schema.set_time(&mut row, 0, high_precision.clone());
174
175		let retrieved = schema.get_time(&row, 0);
176		assert_eq!(retrieved, high_precision);
177		assert_eq!(retrieved.to_nanos_since_midnight(), high_precision.to_nanos_since_midnight());
178	}
179
180	#[test]
181	fn test_time_microsecond_precision() {
182		let schema = Schema::testing(&[Type::Time]);
183		let mut row = schema.allocate();
184
185		// Test microsecond precision (common in databases)
186		let microsecond_precision = Time::new(14, 25, 30, 123456000).unwrap();
187		schema.set_time(&mut row, 0, microsecond_precision.clone());
188		assert_eq!(schema.get_time(&row, 0), microsecond_precision);
189	}
190
191	#[test]
192	fn test_time_millisecond_precision() {
193		let schema = Schema::testing(&[Type::Time]);
194		let mut row = schema.allocate();
195
196		// Test millisecond precision
197		let millisecond_precision = Time::new(8, 15, 42, 123000000).unwrap();
198		schema.set_time(&mut row, 0, millisecond_precision.clone());
199		assert_eq!(schema.get_time(&row, 0), millisecond_precision);
200	}
201
202	#[test]
203	fn test_time_common_times() {
204		let schema = Schema::testing(&[Type::Time]);
205
206		// Test common business/system times
207		let common_times = [
208			Time::new(9, 0, 0, 0).unwrap(),   // 9 AM start of work
209			Time::new(12, 0, 0, 0).unwrap(),  // Noon
210			Time::new(17, 0, 0, 0).unwrap(),  // 5 PM end of work
211			Time::new(0, 0, 1, 0).unwrap(),   // 1 second after midnight
212			Time::new(23, 59, 0, 0).unwrap(), // 1 minute before midnight
213		];
214
215		for time in common_times {
216			let mut row = schema.allocate();
217			schema.set_time(&mut row, 0, time.clone());
218			assert_eq!(schema.get_time(&row, 0), time);
219		}
220	}
221
222	#[test]
223	fn test_try_get_time_wrong_type() {
224		let schema = Schema::testing(&[Type::Boolean]);
225		let mut row = schema.allocate();
226
227		schema.set_bool(&mut row, 0, true);
228
229		assert_eq!(schema.try_get_time(&row, 0), None);
230	}
231}