1use rand::RngExt;
2
3use crate::Error;
4use crate::coding::{Decode, DecodeError, Encode, EncodeError, VarInt};
5
6use std::sync::LazyLock;
7
8pub type Time = Timescale<1_000>;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
16#[error("time overflow")]
17pub struct TimeOverflow;
18
19#[derive(Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Timescale<const SCALE: u64>(VarInt);
28
29impl<const SCALE: u64> Timescale<SCALE> {
30 pub const MAX: Self = Self(VarInt::MAX);
32
33 pub const ZERO: Self = Self(VarInt::ZERO);
35
36 pub const fn new(value: u32) -> Self {
39 Self(VarInt::from_u32(value))
40 }
41
42 pub const fn new_u64(value: u64) -> Result<Self, TimeOverflow> {
45 match VarInt::from_u64(value) {
46 Some(varint) => Ok(Self(varint)),
47 None => Err(TimeOverflow),
48 }
49 }
50
51 pub const fn from_secs(seconds: u64) -> Result<Self, TimeOverflow> {
53 match seconds.checked_mul(SCALE) {
55 Some(value) => Self::new_u64(value),
56 None => Err(TimeOverflow),
57 }
58 }
59
60 pub const fn from_secs_unchecked(seconds: u64) -> Self {
63 match Self::from_secs(seconds) {
64 Ok(time) => time,
65 Err(_) => panic!("time overflow"),
66 }
67 }
68
69 pub const fn from_millis(millis: u64) -> Result<Self, TimeOverflow> {
71 Self::from_scale(millis, 1000)
72 }
73
74 pub const fn from_millis_unchecked(millis: u64) -> Self {
76 Self::from_scale_unchecked(millis, 1000)
77 }
78
79 pub const fn from_micros(micros: u64) -> Result<Self, TimeOverflow> {
81 Self::from_scale(micros, 1_000_000)
82 }
83
84 pub const fn from_micros_unchecked(micros: u64) -> Self {
86 Self::from_scale_unchecked(micros, 1_000_000)
87 }
88
89 pub const fn from_nanos(nanos: u64) -> Result<Self, TimeOverflow> {
91 Self::from_scale(nanos, 1_000_000_000)
92 }
93
94 pub const fn from_nanos_unchecked(nanos: u64) -> Self {
96 Self::from_scale_unchecked(nanos, 1_000_000_000)
97 }
98
99 pub const fn from_scale(value: u64, scale: u64) -> Result<Self, TimeOverflow> {
102 match VarInt::from_u128(value as u128 * SCALE as u128 / scale as u128) {
103 Some(varint) => Ok(Self(varint)),
104 None => Err(TimeOverflow),
105 }
106 }
107
108 pub const fn from_scale_u128(value: u128, scale: u64) -> Result<Self, TimeOverflow> {
110 match value.checked_mul(SCALE as u128) {
111 Some(value) => match VarInt::from_u128(value / scale as u128) {
112 Some(varint) => Ok(Self(varint)),
113 None => Err(TimeOverflow),
114 },
115 None => Err(TimeOverflow),
116 }
117 }
118
119 pub const fn from_scale_unchecked(value: u64, scale: u64) -> Self {
121 match Self::from_scale(value, scale) {
122 Ok(time) => time,
123 Err(_) => panic!("time overflow"),
124 }
125 }
126
127 pub const fn as_secs(self) -> u64 {
129 self.0.into_inner() / SCALE
130 }
131
132 pub const fn as_millis(self) -> u128 {
136 self.as_scale(1000)
137 }
138
139 pub const fn as_micros(self) -> u128 {
141 self.as_scale(1_000_000)
142 }
143
144 pub const fn as_nanos(self) -> u128 {
146 self.as_scale(1_000_000_000)
147 }
148
149 pub const fn as_scale(self, scale: u64) -> u128 {
151 self.0.into_inner() as u128 * scale as u128 / SCALE as u128
152 }
153
154 pub const fn max(self, other: Self) -> Self {
156 if self.0.into_inner() > other.0.into_inner() {
157 self
158 } else {
159 other
160 }
161 }
162
163 pub const fn checked_add(self, rhs: Self) -> Result<Self, TimeOverflow> {
165 let lhs = self.0.into_inner();
166 let rhs = rhs.0.into_inner();
167 match lhs.checked_add(rhs) {
168 Some(result) => Self::new_u64(result),
169 None => Err(TimeOverflow),
170 }
171 }
172
173 pub const fn checked_sub(self, rhs: Self) -> Result<Self, TimeOverflow> {
175 let lhs = self.0.into_inner();
176 let rhs = rhs.0.into_inner();
177 match lhs.checked_sub(rhs) {
178 Some(result) => Self::new_u64(result),
179 None => Err(TimeOverflow),
180 }
181 }
182
183 pub const fn is_zero(self) -> bool {
185 self.0.into_inner() == 0
186 }
187
188 pub fn now() -> Self {
190 web_async::time::Instant::now().into()
191 }
192
193 pub const fn convert<const NEW_SCALE: u64>(self) -> Result<Timescale<NEW_SCALE>, TimeOverflow> {
198 let value = self.0.into_inner();
199 match (value as u128).checked_mul(NEW_SCALE as u128) {
201 Some(v) => match v.checked_div(SCALE as u128) {
202 Some(v) => match VarInt::from_u128(v) {
203 Some(varint) => Ok(Timescale(varint)),
204 None => Err(TimeOverflow),
205 },
206 None => Err(TimeOverflow),
207 },
208 None => Err(TimeOverflow),
209 }
210 }
211
212 pub fn encode<W: bytes::BufMut>(&self, w: &mut W) -> Result<(), EncodeError> {
214 self.0.encode(w, crate::lite::Version::Lite01)?;
216 Ok(())
217 }
218
219 pub fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, Error> {
221 let v = VarInt::decode(r, crate::lite::Version::Lite01)?;
223 Ok(Self(v))
224 }
225}
226
227impl<const SCALE: u64> TryFrom<std::time::Duration> for Timescale<SCALE> {
228 type Error = TimeOverflow;
229
230 fn try_from(duration: std::time::Duration) -> Result<Self, Self::Error> {
231 Self::from_scale_u128(duration.as_nanos(), 1_000_000_000)
232 }
233}
234
235impl<const SCALE: u64> From<Timescale<SCALE>> for std::time::Duration {
236 fn from(time: Timescale<SCALE>) -> Self {
237 std::time::Duration::new(time.as_secs(), (time.as_nanos() % 1_000_000_000) as u32)
238 }
239}
240
241impl<const SCALE: u64> std::fmt::Debug for Timescale<SCALE> {
242 #[allow(clippy::manual_is_multiple_of)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 let nanos = self.as_nanos();
245
246 if nanos % 1_000_000_000 == 0 {
249 write!(f, "{}s", nanos / 1_000_000_000)
250 } else if nanos % 1_000_000 == 0 {
251 write!(f, "{}ms", nanos / 1_000_000)
252 } else if nanos % 1_000 == 0 {
253 write!(f, "{}µs", nanos / 1_000)
254 } else {
255 write!(f, "{}ns", nanos)
256 }
257 }
258}
259
260impl<const SCALE: u64> std::ops::Add for Timescale<SCALE> {
261 type Output = Self;
262
263 fn add(self, rhs: Self) -> Self {
264 self.checked_add(rhs).expect("time overflow")
265 }
266}
267
268impl<const SCALE: u64> std::ops::AddAssign for Timescale<SCALE> {
269 fn add_assign(&mut self, rhs: Self) {
270 *self = *self + rhs;
271 }
272}
273
274impl<const SCALE: u64> std::ops::Sub for Timescale<SCALE> {
275 type Output = Self;
276
277 fn sub(self, rhs: Self) -> Self {
278 self.checked_sub(rhs).expect("time overflow")
279 }
280}
281
282impl<const SCALE: u64> std::ops::SubAssign for Timescale<SCALE> {
283 fn sub_assign(&mut self, rhs: Self) {
284 *self = *self - rhs;
285 }
286}
287
288static TIME_ANCHOR: LazyLock<(web_async::time::Instant, web_async::time::SystemTime)> = LazyLock::new(|| {
290 let jitter = std::time::Duration::from_millis(rand::rng().random_range(0..69_420));
294 (
295 web_async::time::Instant::now(),
296 web_async::time::SystemTime::now() - jitter,
297 )
298});
299
300impl<const SCALE: u64> From<web_async::time::Instant> for Timescale<SCALE> {
302 fn from(instant: web_async::time::Instant) -> Self {
303 let (anchor_instant, anchor_system) = *TIME_ANCHOR;
304
305 let system = match instant.checked_duration_since(anchor_instant) {
307 Some(forward) => anchor_system + forward,
308 None => anchor_system - anchor_instant.duration_since(instant),
309 };
310
311 system
314 .duration_since(web_async::time::UNIX_EPOCH)
315 .expect("dude your clock is earlier than 1970")
316 .try_into()
317 .expect("dude your clock is later than 2116")
318 }
319}
320
321impl<const SCALE: u64> Decode<crate::Version> for Timescale<SCALE> {
322 fn decode<R: bytes::Buf>(r: &mut R, version: crate::Version) -> Result<Self, DecodeError> {
323 let v = VarInt::decode(r, version)?;
324 Ok(Self(v))
325 }
326}
327
328impl<const SCALE: u64> Encode<crate::Version> for Timescale<SCALE> {
329 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: crate::Version) -> Result<(), EncodeError> {
330 self.0.encode(w, version)?;
331 Ok(())
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_from_secs() {
341 let time = Time::from_secs(5).unwrap();
342 assert_eq!(time.as_secs(), 5);
343 assert_eq!(time.as_millis(), 5000);
344 assert_eq!(time.as_micros(), 5_000_000);
345 assert_eq!(time.as_nanos(), 5_000_000_000);
346 }
347
348 #[test]
349 fn test_from_millis() {
350 let time = Time::from_millis(5000).unwrap();
351 assert_eq!(time.as_secs(), 5);
352 assert_eq!(time.as_millis(), 5000);
353 }
354
355 #[test]
356 fn test_from_micros() {
357 let time = Time::from_micros(5_000_000).unwrap();
358 assert_eq!(time.as_secs(), 5);
359 assert_eq!(time.as_millis(), 5000);
360 assert_eq!(time.as_micros(), 5_000_000);
361 }
362
363 #[test]
364 fn test_from_nanos() {
365 let time = Time::from_nanos(5_000_000_000).unwrap();
366 assert_eq!(time.as_secs(), 5);
367 assert_eq!(time.as_millis(), 5000);
368 assert_eq!(time.as_micros(), 5_000_000);
369 assert_eq!(time.as_nanos(), 5_000_000_000);
370 }
371
372 #[test]
373 fn test_zero() {
374 let time = Time::ZERO;
375 assert_eq!(time.as_secs(), 0);
376 assert_eq!(time.as_millis(), 0);
377 assert_eq!(time.as_micros(), 0);
378 assert_eq!(time.as_nanos(), 0);
379 assert!(time.is_zero());
380 }
381
382 #[test]
383 fn test_roundtrip_millis() {
384 let values = [0, 1, 100, 1000, 999999, 1_000_000_000];
385 for &val in &values {
386 let time = Time::from_millis(val).unwrap();
387 assert_eq!(time.as_millis(), val as u128);
388 }
389 }
390
391 #[test]
392 fn test_roundtrip_micros() {
393 let values = [0, 1000, 1_000_000, 1_000_000_000];
395 for &val in &values {
396 let time = Time::from_micros(val).unwrap();
397 assert_eq!(time.as_micros(), val as u128);
398 }
399 }
400
401 #[test]
402 fn test_different_scale_seconds() {
403 type TimeInSeconds = Timescale<1>;
404 let time = TimeInSeconds::from_secs(5).unwrap();
405 assert_eq!(time.as_secs(), 5);
406 assert_eq!(time.as_millis(), 5000);
407 }
408
409 #[test]
410 fn test_different_scale_microseconds() {
411 type TimeInMicros = Timescale<1_000_000>;
412 let time = TimeInMicros::from_micros(5_000_000).unwrap();
413 assert_eq!(time.as_secs(), 5);
414 assert_eq!(time.as_micros(), 5_000_000);
415 }
416
417 #[test]
418 fn test_scale_conversion() {
419 let time = Time::from_scale(5000, 1000).unwrap();
421 assert_eq!(time.as_millis(), 5000);
422 assert_eq!(time.as_secs(), 5);
423
424 let time = Time::from_scale(5, 1).unwrap();
426 assert_eq!(time.as_millis(), 5000);
427 assert_eq!(time.as_secs(), 5);
428 }
429
430 #[test]
431 fn test_add() {
432 let a = Time::from_secs(3).unwrap();
433 let b = Time::from_secs(2).unwrap();
434 let c = a + b;
435 assert_eq!(c.as_secs(), 5);
436 assert_eq!(c.as_millis(), 5000);
437 }
438
439 #[test]
440 fn test_sub() {
441 let a = Time::from_secs(5).unwrap();
442 let b = Time::from_secs(2).unwrap();
443 let c = a - b;
444 assert_eq!(c.as_secs(), 3);
445 assert_eq!(c.as_millis(), 3000);
446 }
447
448 #[test]
449 fn test_checked_add() {
450 let a = Time::from_millis(1000).unwrap();
451 let b = Time::from_millis(2000).unwrap();
452 let c = a.checked_add(b).unwrap();
453 assert_eq!(c.as_millis(), 3000);
454 }
455
456 #[test]
457 fn test_checked_sub() {
458 let a = Time::from_millis(5000).unwrap();
459 let b = Time::from_millis(2000).unwrap();
460 let c = a.checked_sub(b).unwrap();
461 assert_eq!(c.as_millis(), 3000);
462 }
463
464 #[test]
465 fn test_checked_sub_underflow() {
466 let a = Time::from_millis(1000).unwrap();
467 let b = Time::from_millis(2000).unwrap();
468 assert!(a.checked_sub(b).is_err());
469 }
470
471 #[test]
472 fn test_max() {
473 let a = Time::from_secs(5).unwrap();
474 let b = Time::from_secs(10).unwrap();
475 assert_eq!(a.max(b), b);
476 assert_eq!(b.max(a), b);
477 }
478
479 #[test]
480 fn test_duration_conversion() {
481 let duration = std::time::Duration::from_secs(5);
482 let time: Time = duration.try_into().unwrap();
483 assert_eq!(time.as_secs(), 5);
484 assert_eq!(time.as_millis(), 5000);
485
486 let duration_back: std::time::Duration = time.into();
487 assert_eq!(duration_back.as_secs(), 5);
488 }
489
490 #[test]
491 fn test_duration_with_nanos() {
492 let duration = std::time::Duration::new(5, 500_000_000); let time: Time = duration.try_into().unwrap();
494 assert_eq!(time.as_millis(), 5500);
495
496 let duration_back: std::time::Duration = time.into();
497 assert_eq!(duration_back.as_millis(), 5500);
498 }
499
500 #[test]
501 fn test_fractional_conversion() {
502 let time = Time::from_millis(1500).unwrap();
504 assert_eq!(time.as_secs(), 1); assert_eq!(time.as_millis(), 1500);
506 assert_eq!(time.as_micros(), 1_500_000);
507 }
508
509 #[test]
510 fn test_precision_loss() {
511 let time = Time::from_micros(1234).unwrap();
515 assert_eq!(time.as_millis(), 1); assert_eq!(time.as_micros(), 1000); }
518
519 #[test]
520 fn test_scale_boundaries() {
521 let time = Time::from_millis(999).unwrap();
523 assert_eq!(time.as_secs(), 0);
524 assert_eq!(time.as_millis(), 999);
525
526 let time = Time::from_millis(1000).unwrap();
527 assert_eq!(time.as_secs(), 1);
528 assert_eq!(time.as_millis(), 1000);
529
530 let time = Time::from_millis(1001).unwrap();
531 assert_eq!(time.as_secs(), 1);
532 assert_eq!(time.as_millis(), 1001);
533 }
534
535 #[test]
536 fn test_large_values() {
537 let large_secs = 1_000_000_000u64; let time = Time::from_secs(large_secs).unwrap();
540 assert_eq!(time.as_secs(), large_secs);
541 }
542
543 #[test]
544 fn test_new() {
545 let time = Time::new(5000); assert_eq!(time.as_millis(), 5000);
547 assert_eq!(time.as_secs(), 5);
548 }
549
550 #[test]
551 fn test_new_u64() {
552 let time = Time::new_u64(5000).unwrap();
553 assert_eq!(time.as_millis(), 5000);
554 }
555
556 #[test]
557 fn test_ordering() {
558 let a = Time::from_secs(1).unwrap();
559 let b = Time::from_secs(2).unwrap();
560 assert!(a < b);
561 assert!(b > a);
562 assert_eq!(a, a);
563 }
564
565 #[test]
566 fn test_unchecked_variants() {
567 let time = Time::from_secs_unchecked(5);
568 assert_eq!(time.as_secs(), 5);
569
570 let time = Time::from_millis_unchecked(5000);
571 assert_eq!(time.as_millis(), 5000);
572
573 let time = Time::from_micros_unchecked(5_000_000);
574 assert_eq!(time.as_micros(), 5_000_000);
575
576 let time = Time::from_nanos_unchecked(5_000_000_000);
577 assert_eq!(time.as_nanos(), 5_000_000_000);
578
579 let time = Time::from_scale_unchecked(5000, 1000);
580 assert_eq!(time.as_millis(), 5000);
581 }
582
583 #[test]
584 fn test_as_scale() {
585 let time = Time::from_secs(1).unwrap();
586 assert_eq!(time.as_scale(1000), 1000);
588 assert_eq!(time.as_scale(1), 1);
590 assert_eq!(time.as_scale(1_000_000), 1_000_000);
592 }
593
594 #[test]
595 fn test_convert_to_finer() {
596 type TimeInMillis = Timescale<1_000>;
598 type TimeInMicros = Timescale<1_000_000>;
599
600 let time_millis = TimeInMillis::from_millis(5000).unwrap();
601 let time_micros: TimeInMicros = time_millis.convert().unwrap();
602
603 assert_eq!(time_micros.as_millis(), 5000);
604 assert_eq!(time_micros.as_micros(), 5_000_000);
605 }
606
607 #[test]
608 fn test_convert_to_coarser() {
609 type TimeInMillis = Timescale<1_000>;
611 type TimeInSeconds = Timescale<1>;
612
613 let time_millis = TimeInMillis::from_millis(5000).unwrap();
614 let time_secs: TimeInSeconds = time_millis.convert().unwrap();
615
616 assert_eq!(time_secs.as_secs(), 5);
617 assert_eq!(time_secs.as_millis(), 5000);
618 }
619
620 #[test]
621 fn test_convert_precision_loss() {
622 type TimeInMillis = Timescale<1_000>;
624 type TimeInSeconds = Timescale<1>;
625
626 let time_millis = TimeInMillis::from_millis(1234).unwrap();
627 let time_secs: TimeInSeconds = time_millis.convert().unwrap();
628
629 assert_eq!(time_secs.as_secs(), 1);
631 assert_eq!(time_secs.as_millis(), 1000); }
633
634 #[test]
635 fn test_convert_roundtrip() {
636 type TimeInMillis = Timescale<1_000>;
638 type TimeInMicros = Timescale<1_000_000>;
639
640 let original = TimeInMillis::from_millis(5000).unwrap();
641 let as_micros: TimeInMicros = original.convert().unwrap();
642 let back_to_millis: TimeInMillis = as_micros.convert().unwrap();
643
644 assert_eq!(original.as_millis(), back_to_millis.as_millis());
645 }
646
647 #[test]
648 fn test_convert_same_scale() {
649 type TimeInMillis = Timescale<1_000>;
651
652 let time = TimeInMillis::from_millis(5000).unwrap();
653 let converted: TimeInMillis = time.convert().unwrap();
654
655 assert_eq!(time.as_millis(), converted.as_millis());
656 }
657
658 #[test]
659 fn test_convert_microseconds_to_nanoseconds() {
660 type TimeInMicros = Timescale<1_000_000>;
661 type TimeInNanos = Timescale<1_000_000_000>;
662
663 let time_micros = TimeInMicros::from_micros(5_000_000).unwrap();
664 let time_nanos: TimeInNanos = time_micros.convert().unwrap();
665
666 assert_eq!(time_nanos.as_micros(), 5_000_000);
667 assert_eq!(time_nanos.as_nanos(), 5_000_000_000);
668 }
669
670 #[test]
671 fn test_convert_custom_scales() {
672 type TimeScale60 = Timescale<60>; type TimeScale90 = Timescale<90>; let time60 = TimeScale60::from_scale(120, 60).unwrap(); let time90: TimeScale90 = time60.convert().unwrap();
678
679 assert_eq!(time60.as_secs(), 2);
681 assert_eq!(time90.as_secs(), 2);
682 }
683
684 #[test]
685 fn test_debug_format_units() {
686 let t = Time::from_millis(100000).unwrap();
690 assert_eq!(format!("{:?}", t), "100s");
691
692 let t = Time::from_millis(1000).unwrap();
693 assert_eq!(format!("{:?}", t), "1s");
694
695 let t = Time::from_millis(100).unwrap();
697 assert_eq!(format!("{:?}", t), "100ms");
698
699 let t = Time::from_millis(5500).unwrap();
700 assert_eq!(format!("{:?}", t), "5500ms");
701
702 let t = Time::ZERO;
704 assert_eq!(format!("{:?}", t), "0s");
705
706 type TimeMicros = Timescale<1_000_000>;
708 let t = TimeMicros::from_micros(1500).unwrap();
709 assert_eq!(format!("{:?}", t), "1500µs");
710
711 let t = TimeMicros::from_micros(1000).unwrap();
712 assert_eq!(format!("{:?}", t), "1ms");
713 }
714}