reifydb_core/util/
clock.rs1#[cfg(debug_assertions)]
5use std::cell::RefCell;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use reifydb_type::value::DateTime;
9
10#[cfg(debug_assertions)]
11thread_local! {
12 static MOCK_TIME_NANOS: RefCell<Option<u128>> = RefCell::new(None);
14}
15
16#[inline(always)]
20pub fn now_nanos() -> u128 {
21 #[cfg(debug_assertions)]
22 {
23 if let Some(nanos) = MOCK_TIME_NANOS.with(|c| *c.borrow()) {
24 return nanos;
25 }
26 }
27
28 SystemTime::now().duration_since(UNIX_EPOCH).expect("System time is before Unix epoch").as_nanos()
29}
30
31#[inline(always)]
33pub fn now_micros() -> u64 {
34 (now_nanos() / 1_000) as u64
35}
36
37#[inline(always)]
39pub fn now_millis() -> u64 {
40 (now_nanos() / 1_000_000) as u64
41}
42
43#[inline(always)]
46pub fn now() -> DateTime {
47 let nanos = now_nanos();
48 let secs = (nanos / 1_000_000_000) as i64;
49 let nanos_remainder = (nanos % 1_000_000_000) as u32;
50
51 DateTime::from_parts(secs, nanos_remainder).unwrap_or_else(|_| DateTime::now())
52}
53
54#[cfg(debug_assertions)]
60pub fn mock_time_set_nanos(nanos: u128) {
61 MOCK_TIME_NANOS.with(|c| {
62 *c.borrow_mut() = Some(nanos);
63 });
64}
65
66#[cfg(debug_assertions)]
68pub fn mock_time_set_micros(micros: u64) {
69 mock_time_set_nanos(micros as u128 * 1_000);
70}
71
72#[cfg(debug_assertions)]
74pub fn mock_time_set_millis(millis: u64) {
75 mock_time_set_nanos(millis as u128 * 1_000_000);
76}
77
78#[cfg(debug_assertions)]
80pub fn mock_time_set(millis: u64) {
81 mock_time_set_millis(millis);
82}
83
84#[cfg(debug_assertions)]
86pub fn mock_time_advance_nanos(nanos: u128) {
87 MOCK_TIME_NANOS.with(|c| {
88 let mut time = c.borrow_mut();
89 let current = time.unwrap_or_else(|| now_nanos());
90 *time = Some(current + nanos);
91 });
92}
93
94#[cfg(debug_assertions)]
96pub fn mock_time_advance_micros(micros: u64) {
97 mock_time_advance_nanos(micros as u128 * 1_000);
98}
99
100#[cfg(debug_assertions)]
102pub fn mock_time_advance_millis(millis: u64) {
103 mock_time_advance_nanos(millis as u128 * 1_000_000);
104}
105
106#[cfg(debug_assertions)]
108pub fn mock_time_advance(millis: u64) {
109 mock_time_advance_millis(millis);
110}
111
112#[cfg(debug_assertions)]
114pub fn mock_time_clear() {
115 MOCK_TIME_NANOS.with(|c| {
116 *c.borrow_mut() = None;
117 });
118}
119
120#[cfg(debug_assertions)]
122pub fn mock_time_get_nanos() -> Option<u128> {
123 MOCK_TIME_NANOS.with(|c| *c.borrow())
124}
125
126#[cfg(debug_assertions)]
128pub fn mock_time_get_micros() -> Option<u64> {
129 mock_time_get_nanos().map(|n| (n / 1_000) as u64)
130}
131
132#[cfg(debug_assertions)]
134pub fn mock_time_get_millis() -> Option<u64> {
135 mock_time_get_nanos().map(|n| (n / 1_000_000) as u64)
136}
137
138#[cfg(debug_assertions)]
140pub fn mock_time_get() -> Option<u64> {
141 mock_time_get_millis()
142}
143
144#[cfg(debug_assertions)]
146pub fn mock_time_is_active() -> bool {
147 MOCK_TIME_NANOS.with(|c| c.borrow().is_some())
148}
149
150#[cfg(debug_assertions)]
152pub struct MockTimeGuard {
153 prev: Option<u128>,
154}
155
156#[cfg(debug_assertions)]
157impl Drop for MockTimeGuard {
158 fn drop(&mut self) {
159 MOCK_TIME_NANOS.with(|c| {
160 *c.borrow_mut() = self.prev;
161 });
162 }
163}
164
165#[cfg(debug_assertions)]
167pub fn mock_time_scoped_nanos(nanos: u128) -> MockTimeGuard {
168 MOCK_TIME_NANOS.with(|c| {
169 let prev = *c.borrow();
170 *c.borrow_mut() = Some(nanos);
171 MockTimeGuard {
172 prev,
173 }
174 })
175}
176
177#[cfg(debug_assertions)]
179pub fn mock_time_scoped_micros(micros: u64) -> MockTimeGuard {
180 mock_time_scoped_nanos(micros as u128 * 1_000)
181}
182
183#[cfg(debug_assertions)]
185pub fn mock_time_scoped_millis(millis: u64) -> MockTimeGuard {
186 mock_time_scoped_nanos(millis as u128 * 1_000_000)
187}
188
189#[cfg(debug_assertions)]
191pub fn mock_time_scoped(millis: u64) -> MockTimeGuard {
192 mock_time_scoped_millis(millis)
193}
194
195#[cfg(debug_assertions)]
197pub fn mock_time_with_nanos<T>(nanos: u128, f: impl FnOnce() -> T) -> T {
198 let _guard = mock_time_scoped_nanos(nanos);
199 f()
200}
201
202#[cfg(debug_assertions)]
204pub fn mock_time_with_micros<T>(micros: u64, f: impl FnOnce() -> T) -> T {
205 let _guard = mock_time_scoped_micros(micros);
206 f()
207}
208
209#[cfg(debug_assertions)]
211pub fn mock_time_with_millis<T>(millis: u64, f: impl FnOnce() -> T) -> T {
212 let _guard = mock_time_scoped_millis(millis);
213 f()
214}
215
216#[cfg(debug_assertions)]
218pub fn mock_time_with<T>(millis: u64, f: impl FnOnce() -> T) -> T {
219 mock_time_with_millis(millis, f)
220}
221
222#[cfg(debug_assertions)]
224pub struct MockTimeControl;
225
226#[cfg(debug_assertions)]
227impl MockTimeControl {
228 pub fn advance_nanos(&self, nanos: u128) {
229 mock_time_advance_nanos(nanos);
230 }
231
232 pub fn advance_micros(&self, micros: u64) {
233 mock_time_advance_micros(micros);
234 }
235
236 pub fn advance_millis(&self, millis: u64) {
237 mock_time_advance_millis(millis);
238 }
239
240 pub fn advance(&self, millis: u64) {
241 mock_time_advance_millis(millis);
242 }
243
244 pub fn set_nanos(&self, nanos: u128) {
245 mock_time_set_nanos(nanos);
246 }
247
248 pub fn set_micros(&self, micros: u64) {
249 mock_time_set_micros(micros);
250 }
251
252 pub fn set_millis(&self, millis: u64) {
253 mock_time_set_millis(millis);
254 }
255
256 pub fn set(&self, millis: u64) {
257 mock_time_set_millis(millis);
258 }
259
260 pub fn current_nanos(&self) -> u128 {
261 mock_time_get_nanos().expect("Mock time should be active")
262 }
263
264 pub fn current_micros(&self) -> u64 {
265 mock_time_get_micros().expect("Mock time should be active")
266 }
267
268 pub fn current_millis(&self) -> u64 {
269 mock_time_get_millis().expect("Mock time should be active")
270 }
271
272 pub fn current(&self) -> u64 {
273 self.current_millis()
274 }
275}
276
277#[cfg(debug_assertions)]
279pub fn mock_time_with_control<T>(initial_millis: u64, f: impl FnOnce(&MockTimeControl) -> T) -> T {
280 let _guard = mock_time_scoped_millis(initial_millis);
281 let control = MockTimeControl;
282 f(&control)
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_system_time() {
291 mock_time_clear(); let t1 = now_millis();
294 std::thread::sleep(std::time::Duration::from_millis(10));
295 let t2 = now_millis();
296 assert!(t2 >= t1 + 10);
297 }
298
299 #[test]
300 fn test_mock_time_set() {
301 mock_time_set(1000);
302 assert_eq!(now_millis(), 1000);
303 assert!(mock_time_is_active());
304
305 mock_time_clear();
306 assert!(!mock_time_is_active());
307 }
308
309 #[test]
310 fn test_mock_time_advance() {
311 mock_time_set(1000);
312 assert_eq!(now_millis(), 1000);
313
314 mock_time_advance(500);
315 assert_eq!(now_millis(), 1500);
316
317 mock_time_advance(250);
318 assert_eq!(now_millis(), 1750);
319
320 mock_time_clear();
321 }
322
323 #[test]
324 fn test_nanosecond_precision() {
325 mock_time_set_nanos(1_234_567_890_123_456_789);
327
328 assert_eq!(now_nanos(), 1_234_567_890_123_456_789);
329 assert_eq!(now_micros(), 1_234_567_890_123_456);
330 assert_eq!(now_millis(), 1_234_567_890_123);
331
332 let dt = now();
333 assert_eq!(dt.timestamp(), 1_234_567_890);
334 assert_eq!(dt.timestamp_nanos() % 1_000_000_000, 123_456_789);
335
336 mock_time_clear();
337 }
338
339 #[test]
340 fn test_microsecond_precision() {
341 mock_time_set_micros(1_234_567_890_123);
342
343 assert_eq!(now_micros(), 1_234_567_890_123);
344 assert_eq!(now_millis(), 1_234_567_890);
345
346 mock_time_advance_micros(500);
347 assert_eq!(now_micros(), 1_234_567_890_623);
348
349 mock_time_clear();
350 }
351
352 #[test]
353 fn test_datetime_conversion() {
354 mock_time_set_nanos(1_700_000_000_987_654_321);
355
356 let dt = now();
357 assert_eq!(dt.timestamp(), 1_700_000_000);
358 assert_eq!(dt.timestamp_nanos() % 1_000_000_000, 987_654_321);
359 assert_eq!(dt.timestamp_millis(), 1_700_000_000_987);
360
361 mock_time_clear();
362 }
363
364 #[test]
365 fn test_mock_time_scoped() {
366 assert!(!mock_time_is_active());
367
368 {
369 let _guard = mock_time_scoped(2000);
370 assert_eq!(now_millis(), 2000);
371 assert!(mock_time_is_active());
372 }
373
374 assert!(!mock_time_is_active());
375 }
376
377 #[test]
378 fn test_mock_time_with() {
379 let result = mock_time_with(3000, || {
380 assert_eq!(now_millis(), 3000);
381 "test"
382 });
383
384 assert_eq!(result, "test");
385 assert!(!mock_time_is_active());
386 }
387
388 #[test]
389 fn test_mock_time_with_control() {
390 mock_time_with_control(1000, |control| {
391 assert_eq!(control.current(), 1000);
392
393 control.advance(500);
394 assert_eq!(now_millis(), 1500);
395
396 control.set(2000);
397 assert_eq!(now_millis(), 2000);
398
399 control.advance_micros(500_000);
400 assert_eq!(now_millis(), 2500);
401
402 control.advance_nanos(500_000_000);
403 assert_eq!(now_millis(), 3000);
404 });
405
406 assert!(!mock_time_is_active());
407 }
408
409 #[test]
410 fn test_parallel_tests_isolated() {
411 use std::thread;
412
413 let handle1 = thread::spawn(|| {
414 mock_time_with(1000, || {
415 assert_eq!(now_millis(), 1000);
416 });
417 });
418
419 let handle2 = thread::spawn(|| {
420 mock_time_with(2000, || {
421 assert_eq!(now_millis(), 2000);
422 });
423 });
424
425 handle1.join().unwrap();
426 handle2.join().unwrap();
427 }
428}