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 std::time::Duration;
288
289 use tokio::time::sleep;
290
291 use super::*;
292
293 #[tokio::test]
294 async fn test_system_time() {
295 mock_time_clear(); let t1 = now_millis();
298 sleep(Duration::from_millis(10)).await;
299 let t2 = now_millis();
300 assert!(t2 >= t1 + 10);
301 }
302
303 #[test]
304 fn test_mock_time_set() {
305 mock_time_set(1000);
306 assert_eq!(now_millis(), 1000);
307 assert!(mock_time_is_active());
308
309 mock_time_clear();
310 assert!(!mock_time_is_active());
311 }
312
313 #[test]
314 fn test_mock_time_advance() {
315 mock_time_set(1000);
316 assert_eq!(now_millis(), 1000);
317
318 mock_time_advance(500);
319 assert_eq!(now_millis(), 1500);
320
321 mock_time_advance(250);
322 assert_eq!(now_millis(), 1750);
323
324 mock_time_clear();
325 }
326
327 #[test]
328 fn test_nanosecond_precision() {
329 mock_time_set_nanos(1_234_567_890_123_456_789);
331
332 assert_eq!(now_nanos(), 1_234_567_890_123_456_789);
333 assert_eq!(now_micros(), 1_234_567_890_123_456);
334 assert_eq!(now_millis(), 1_234_567_890_123);
335
336 let dt = now();
337 assert_eq!(dt.timestamp(), 1_234_567_890);
338 assert_eq!(dt.timestamp_nanos() % 1_000_000_000, 123_456_789);
339
340 mock_time_clear();
341 }
342
343 #[test]
344 fn test_microsecond_precision() {
345 mock_time_set_micros(1_234_567_890_123);
346
347 assert_eq!(now_micros(), 1_234_567_890_123);
348 assert_eq!(now_millis(), 1_234_567_890);
349
350 mock_time_advance_micros(500);
351 assert_eq!(now_micros(), 1_234_567_890_623);
352
353 mock_time_clear();
354 }
355
356 #[test]
357 fn test_datetime_conversion() {
358 mock_time_set_nanos(1_700_000_000_987_654_321);
359
360 let dt = now();
361 assert_eq!(dt.timestamp(), 1_700_000_000);
362 assert_eq!(dt.timestamp_nanos() % 1_000_000_000, 987_654_321);
363 assert_eq!(dt.timestamp_millis(), 1_700_000_000_987);
364
365 mock_time_clear();
366 }
367
368 #[test]
369 fn test_mock_time_scoped() {
370 assert!(!mock_time_is_active());
371
372 {
373 let _guard = mock_time_scoped(2000);
374 assert_eq!(now_millis(), 2000);
375 assert!(mock_time_is_active());
376 }
377
378 assert!(!mock_time_is_active());
379 }
380
381 #[test]
382 fn test_mock_time_with() {
383 let result = mock_time_with(3000, || {
384 assert_eq!(now_millis(), 3000);
385 "test"
386 });
387
388 assert_eq!(result, "test");
389 assert!(!mock_time_is_active());
390 }
391
392 #[test]
393 fn test_mock_time_with_control() {
394 mock_time_with_control(1000, |control| {
395 assert_eq!(control.current(), 1000);
396
397 control.advance(500);
398 assert_eq!(now_millis(), 1500);
399
400 control.set(2000);
401 assert_eq!(now_millis(), 2000);
402
403 control.advance_micros(500_000);
404 assert_eq!(now_millis(), 2500);
405
406 control.advance_nanos(500_000_000);
407 assert_eq!(now_millis(), 3000);
408 });
409
410 assert!(!mock_time_is_active());
411 }
412
413 #[test]
414 fn test_parallel_tests_isolated() {
415 use std::thread;
416
417 let handle1 = thread::spawn(|| {
418 mock_time_with(1000, || {
419 assert_eq!(now_millis(), 1000);
420 });
421 });
422
423 let handle2 = thread::spawn(|| {
424 mock_time_with(2000, || {
425 assert_eq!(now_millis(), 2000);
426 });
427 });
428
429 handle1.join().unwrap();
430 handle2.join().unwrap();
431 }
432}