moq_lite/model/
time.rs

1use rand::Rng;
2
3use crate::coding::{Decode, DecodeError, Encode, VarInt};
4
5use std::sync::LazyLock;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8/// A timestamp representing the presentation time in milliseconds.
9///
10/// The underlying implementation supports any scale, but everything uses milliseconds by default.
11pub type Time = Timescale<1_000>;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
14#[error("time overflow")]
15pub struct TimeOverflow;
16
17/// A timestamp representing the presentation time in a given scale. ex. 1000 for milliseconds.
18///
19/// All timestamps within a track are relative, so zero for one track is not zero for another.
20/// Values are constrained to fit within a QUIC VarInt (2^62) so they can be encoded and decoded easily.
21///
22/// This is [std::time::Instant] and [std::time::Duration] merged into one type for simplicity.
23#[derive(Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Timescale<const SCALE: u64>(VarInt);
26
27impl<const SCALE: u64> Timescale<SCALE> {
28	/// The maximum representable instant.
29	pub const MAX: Self = Self(VarInt::MAX);
30
31	/// The minimum representable instant.
32	pub const ZERO: Self = Self(VarInt::ZERO);
33
34	pub const fn new(value: u32) -> Self {
35		Self(VarInt::from_u32(value))
36	}
37
38	pub const fn new_u64(value: u64) -> Result<Self, TimeOverflow> {
39		match VarInt::from_u64(value) {
40			Some(varint) => Ok(Self(varint)),
41			None => Err(TimeOverflow),
42		}
43	}
44
45	/// Convert a number of seconds to a timestamp, returning an error if the timestamp would overflow.
46	pub const fn from_secs(seconds: u64) -> Result<Self, TimeOverflow> {
47		// Not using from_scale because it'll be slightly faster
48		match seconds.checked_mul(SCALE) {
49			Some(value) => Self::new_u64(value),
50			None => Err(TimeOverflow),
51		}
52	}
53
54	pub const fn from_secs_unchecked(seconds: u64) -> Self {
55		match Self::from_secs(seconds) {
56			Ok(time) => time,
57			Err(_) => panic!("time overflow"),
58		}
59	}
60
61	/// Convert a number of milliseconds to a timestamp, returning an error if the timestamp would overflow.
62	pub const fn from_millis(millis: u64) -> Result<Self, TimeOverflow> {
63		Self::from_scale(millis, 1000)
64	}
65
66	pub const fn from_millis_unchecked(millis: u64) -> Self {
67		Self::from_scale_unchecked(millis, 1000)
68	}
69
70	pub const fn from_micros(micros: u64) -> Result<Self, TimeOverflow> {
71		Self::from_scale(micros, 1_000_000)
72	}
73
74	pub const fn from_micros_unchecked(micros: u64) -> Self {
75		Self::from_scale_unchecked(micros, 1_000_000)
76	}
77
78	pub const fn from_nanos(nanos: u64) -> Result<Self, TimeOverflow> {
79		Self::from_scale(nanos, 1_000_000_000)
80	}
81
82	pub const fn from_nanos_unchecked(nanos: u64) -> Self {
83		Self::from_scale_unchecked(nanos, 1_000_000_000)
84	}
85
86	pub const fn from_scale(value: u64, scale: u64) -> Result<Self, TimeOverflow> {
87		match VarInt::from_u128(value as u128 * SCALE as u128 / scale as u128) {
88			Some(varint) => Ok(Self(varint)),
89			None => Err(TimeOverflow),
90		}
91	}
92
93	pub const fn from_scale_u128(value: u128, scale: u64) -> Result<Self, TimeOverflow> {
94		match value.checked_mul(SCALE as u128) {
95			Some(value) => match VarInt::from_u128(value / scale as u128) {
96				Some(varint) => Ok(Self(varint)),
97				None => Err(TimeOverflow),
98			},
99			None => Err(TimeOverflow),
100		}
101	}
102
103	pub const fn from_scale_unchecked(value: u64, scale: u64) -> Self {
104		match Self::from_scale(value, scale) {
105			Ok(time) => time,
106			Err(_) => panic!("time overflow"),
107		}
108	}
109
110	/// Get the timestamp as seconds.
111	pub const fn as_secs(self) -> u64 {
112		self.0.into_inner() / SCALE
113	}
114
115	/// Get the timestamp as milliseconds.
116	//
117	// This returns a u128 to avoid a possible overflow when SCALE < 250
118	pub const fn as_millis(self) -> u128 {
119		self.as_scale(1000)
120	}
121
122	/// Get the timestamp as microseconds.
123	pub const fn as_micros(self) -> u128 {
124		self.as_scale(1_000_000)
125	}
126
127	/// Get the timestamp as nanoseconds.
128	pub const fn as_nanos(self) -> u128 {
129		self.as_scale(1_000_000_000)
130	}
131
132	pub const fn as_scale(self, scale: u64) -> u128 {
133		self.0.into_inner() as u128 * scale as u128 / SCALE as u128
134	}
135
136	/// Get the maximum of two timestamps.
137	pub const fn max(self, other: Self) -> Self {
138		if self.0.into_inner() > other.0.into_inner() {
139			self
140		} else {
141			other
142		}
143	}
144
145	pub const fn checked_add(self, rhs: Self) -> Result<Self, TimeOverflow> {
146		let lhs = self.0.into_inner();
147		let rhs = rhs.0.into_inner();
148		match lhs.checked_add(rhs) {
149			Some(result) => Self::new_u64(result),
150			None => Err(TimeOverflow),
151		}
152	}
153
154	pub const fn checked_sub(self, rhs: Self) -> Result<Self, TimeOverflow> {
155		let lhs = self.0.into_inner();
156		let rhs = rhs.0.into_inner();
157		match lhs.checked_sub(rhs) {
158			Some(result) => Self::new_u64(result),
159			None => Err(TimeOverflow),
160		}
161	}
162
163	pub const fn is_zero(self) -> bool {
164		self.0.into_inner() == 0
165	}
166
167	pub fn now() -> Self {
168		// We use tokio so it can be stubbed for testing.
169		tokio::time::Instant::now().into()
170	}
171
172	/// Convert this timestamp to a different scale.
173	///
174	/// This allows converting between different TimeScale types, for example from milliseconds to microseconds.
175	/// Note that converting to a coarser scale may lose precision due to integer division.
176	pub const fn convert<const NEW_SCALE: u64>(self) -> Result<Timescale<NEW_SCALE>, TimeOverflow> {
177		let value = self.0.into_inner();
178		// Convert from SCALE to NEW_SCALE: value * NEW_SCALE / SCALE
179		match (value as u128).checked_mul(NEW_SCALE as u128) {
180			Some(v) => match v.checked_div(SCALE as u128) {
181				Some(v) => match VarInt::from_u128(v) {
182					Some(varint) => Ok(Timescale(varint)),
183					None => Err(TimeOverflow),
184				},
185				None => Err(TimeOverflow),
186			},
187			None => Err(TimeOverflow),
188		}
189	}
190}
191
192impl<const SCALE: u64> TryFrom<std::time::Duration> for Timescale<SCALE> {
193	type Error = TimeOverflow;
194
195	fn try_from(duration: std::time::Duration) -> Result<Self, Self::Error> {
196		Self::from_scale_u128(duration.as_nanos(), 1_000_000_000)
197	}
198}
199
200impl<const SCALE: u64> From<Timescale<SCALE>> for std::time::Duration {
201	fn from(time: Timescale<SCALE>) -> Self {
202		std::time::Duration::new(time.as_secs(), (time.as_nanos() % 1_000_000_000) as u32)
203	}
204}
205
206impl<const SCALE: u64> std::fmt::Debug for Timescale<SCALE> {
207	#[allow(clippy::manual_is_multiple_of)] // is_multiple_of is unstable in Rust 1.85
208	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209		let nanos = self.as_nanos();
210
211		// Choose the largest unit where we don't need decimal places
212		// Check from largest to smallest unit
213		if nanos % 1_000_000_000 == 0 {
214			write!(f, "{}s", nanos / 1_000_000_000)
215		} else if nanos % 1_000_000 == 0 {
216			write!(f, "{}ms", nanos / 1_000_000)
217		} else if nanos % 1_000 == 0 {
218			write!(f, "{}µs", nanos / 1_000)
219		} else {
220			write!(f, "{}ns", nanos)
221		}
222	}
223}
224
225impl<const SCALE: u64> std::ops::Add for Timescale<SCALE> {
226	type Output = Self;
227
228	fn add(self, rhs: Self) -> Self {
229		self.checked_add(rhs).expect("time overflow")
230	}
231}
232
233impl<const SCALE: u64> std::ops::AddAssign for Timescale<SCALE> {
234	fn add_assign(&mut self, rhs: Self) {
235		*self = *self + rhs;
236	}
237}
238
239impl<const SCALE: u64> std::ops::Sub for Timescale<SCALE> {
240	type Output = Self;
241
242	fn sub(self, rhs: Self) -> Self {
243		self.checked_sub(rhs).expect("time overflow")
244	}
245}
246
247impl<const SCALE: u64> std::ops::SubAssign for Timescale<SCALE> {
248	fn sub_assign(&mut self, rhs: Self) {
249		*self = *self - rhs;
250	}
251}
252
253// There's no zero Instant, so we need to use a reference point.
254static TIME_ANCHOR: LazyLock<(std::time::Instant, SystemTime)> = LazyLock::new(|| {
255	// To deter nerds trying to use timestamp as wall clock time, we subtract a random amount of time from the anchor.
256	// This will make our timestamps appear to be late; just enough to be annoying and obscure our clock drift.
257	// This will also catch bad implementations that assume unrelated broadcasts are synchronized.
258	let jitter = std::time::Duration::from_millis(rand::rng().random_range(0..69_420));
259	(std::time::Instant::now(), SystemTime::now() - jitter)
260});
261
262// Convert an Instant to a Unix timestamp
263impl<const SCALE: u64> From<std::time::Instant> for Timescale<SCALE> {
264	fn from(instant: std::time::Instant) -> Self {
265		let (anchor_instant, anchor_system) = *TIME_ANCHOR;
266
267		// Conver the instant to a SystemTime.
268		let system = match instant.checked_duration_since(anchor_instant) {
269			Some(forward) => anchor_system + forward,
270			None => anchor_system - anchor_instant.duration_since(instant),
271		};
272
273		// Convert the SystemTime to a Unix timestamp in nanoseconds.
274		// We'll then convert that to the desired scale.
275		system
276			.duration_since(UNIX_EPOCH)
277			.expect("dude your clock is earlier than 1970")
278			.try_into()
279			.expect("dude your clock is later than 2116")
280	}
281}
282
283impl<const SCALE: u64> From<tokio::time::Instant> for Timescale<SCALE> {
284	fn from(instant: tokio::time::Instant) -> Self {
285		instant.into_std().into()
286	}
287}
288
289impl<const SCALE: u64, V> Decode<V> for Timescale<SCALE> {
290	fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
291		let v = VarInt::decode(r, version)?;
292		Ok(Self(v))
293	}
294}
295
296impl<const SCALE: u64, V> Encode<V> for Timescale<SCALE> {
297	fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) {
298		self.0.encode(w, version)
299	}
300}
301
302#[cfg(test)]
303mod tests {
304	use super::*;
305
306	#[test]
307	fn test_from_secs() {
308		let time = Time::from_secs(5).unwrap();
309		assert_eq!(time.as_secs(), 5);
310		assert_eq!(time.as_millis(), 5000);
311		assert_eq!(time.as_micros(), 5_000_000);
312		assert_eq!(time.as_nanos(), 5_000_000_000);
313	}
314
315	#[test]
316	fn test_from_millis() {
317		let time = Time::from_millis(5000).unwrap();
318		assert_eq!(time.as_secs(), 5);
319		assert_eq!(time.as_millis(), 5000);
320	}
321
322	#[test]
323	fn test_from_micros() {
324		let time = Time::from_micros(5_000_000).unwrap();
325		assert_eq!(time.as_secs(), 5);
326		assert_eq!(time.as_millis(), 5000);
327		assert_eq!(time.as_micros(), 5_000_000);
328	}
329
330	#[test]
331	fn test_from_nanos() {
332		let time = Time::from_nanos(5_000_000_000).unwrap();
333		assert_eq!(time.as_secs(), 5);
334		assert_eq!(time.as_millis(), 5000);
335		assert_eq!(time.as_micros(), 5_000_000);
336		assert_eq!(time.as_nanos(), 5_000_000_000);
337	}
338
339	#[test]
340	fn test_zero() {
341		let time = Time::ZERO;
342		assert_eq!(time.as_secs(), 0);
343		assert_eq!(time.as_millis(), 0);
344		assert_eq!(time.as_micros(), 0);
345		assert_eq!(time.as_nanos(), 0);
346		assert!(time.is_zero());
347	}
348
349	#[test]
350	fn test_roundtrip_millis() {
351		let values = [0, 1, 100, 1000, 999999, 1_000_000_000];
352		for &val in &values {
353			let time = Time::from_millis(val).unwrap();
354			assert_eq!(time.as_millis(), val as u128);
355		}
356	}
357
358	#[test]
359	fn test_roundtrip_micros() {
360		// Note: values < 1000 will lose precision when converting to milliseconds (SCALE=1000)
361		let values = [0, 1000, 1_000_000, 1_000_000_000];
362		for &val in &values {
363			let time = Time::from_micros(val).unwrap();
364			assert_eq!(time.as_micros(), val as u128);
365		}
366	}
367
368	#[test]
369	fn test_different_scale_seconds() {
370		type TimeInSeconds = Timescale<1>;
371		let time = TimeInSeconds::from_secs(5).unwrap();
372		assert_eq!(time.as_secs(), 5);
373		assert_eq!(time.as_millis(), 5000);
374	}
375
376	#[test]
377	fn test_different_scale_microseconds() {
378		type TimeInMicros = Timescale<1_000_000>;
379		let time = TimeInMicros::from_micros(5_000_000).unwrap();
380		assert_eq!(time.as_secs(), 5);
381		assert_eq!(time.as_micros(), 5_000_000);
382	}
383
384	#[test]
385	fn test_scale_conversion() {
386		// Converting 5000 milliseconds at scale 1000 to scale 1000 (should be identity)
387		let time = Time::from_scale(5000, 1000).unwrap();
388		assert_eq!(time.as_millis(), 5000);
389		assert_eq!(time.as_secs(), 5);
390
391		// Converting 5 seconds at scale 1 to scale 1000
392		let time = Time::from_scale(5, 1).unwrap();
393		assert_eq!(time.as_millis(), 5000);
394		assert_eq!(time.as_secs(), 5);
395	}
396
397	#[test]
398	fn test_add() {
399		let a = Time::from_secs(3).unwrap();
400		let b = Time::from_secs(2).unwrap();
401		let c = a + b;
402		assert_eq!(c.as_secs(), 5);
403		assert_eq!(c.as_millis(), 5000);
404	}
405
406	#[test]
407	fn test_sub() {
408		let a = Time::from_secs(5).unwrap();
409		let b = Time::from_secs(2).unwrap();
410		let c = a - b;
411		assert_eq!(c.as_secs(), 3);
412		assert_eq!(c.as_millis(), 3000);
413	}
414
415	#[test]
416	fn test_checked_add() {
417		let a = Time::from_millis(1000).unwrap();
418		let b = Time::from_millis(2000).unwrap();
419		let c = a.checked_add(b).unwrap();
420		assert_eq!(c.as_millis(), 3000);
421	}
422
423	#[test]
424	fn test_checked_sub() {
425		let a = Time::from_millis(5000).unwrap();
426		let b = Time::from_millis(2000).unwrap();
427		let c = a.checked_sub(b).unwrap();
428		assert_eq!(c.as_millis(), 3000);
429	}
430
431	#[test]
432	fn test_checked_sub_underflow() {
433		let a = Time::from_millis(1000).unwrap();
434		let b = Time::from_millis(2000).unwrap();
435		assert!(a.checked_sub(b).is_err());
436	}
437
438	#[test]
439	fn test_max() {
440		let a = Time::from_secs(5).unwrap();
441		let b = Time::from_secs(10).unwrap();
442		assert_eq!(a.max(b), b);
443		assert_eq!(b.max(a), b);
444	}
445
446	#[test]
447	fn test_duration_conversion() {
448		let duration = std::time::Duration::from_secs(5);
449		let time: Time = duration.try_into().unwrap();
450		assert_eq!(time.as_secs(), 5);
451		assert_eq!(time.as_millis(), 5000);
452
453		let duration_back: std::time::Duration = time.into();
454		assert_eq!(duration_back.as_secs(), 5);
455	}
456
457	#[test]
458	fn test_duration_with_nanos() {
459		let duration = std::time::Duration::new(5, 500_000_000); // 5.5 seconds
460		let time: Time = duration.try_into().unwrap();
461		assert_eq!(time.as_millis(), 5500);
462
463		let duration_back: std::time::Duration = time.into();
464		assert_eq!(duration_back.as_millis(), 5500);
465	}
466
467	#[test]
468	fn test_fractional_conversion() {
469		// Test that 1500 millis = 1.5 seconds
470		let time = Time::from_millis(1500).unwrap();
471		assert_eq!(time.as_secs(), 1); // Integer division
472		assert_eq!(time.as_millis(), 1500);
473		assert_eq!(time.as_micros(), 1_500_000);
474	}
475
476	#[test]
477	fn test_precision_loss() {
478		// When converting from a finer scale to coarser, we lose precision
479		// 1234 micros = 1.234 millis, which rounds down to 1 millisecond internally
480		// When converting back, we get 1000 micros, not the original 1234
481		let time = Time::from_micros(1234).unwrap();
482		assert_eq!(time.as_millis(), 1); // 1234 micros = 1.234 millis, rounds to 1
483		assert_eq!(time.as_micros(), 1000); // Precision lost: 1 milli = 1000 micros
484	}
485
486	#[test]
487	fn test_scale_boundaries() {
488		// Test values near scale boundaries
489		let time = Time::from_millis(999).unwrap();
490		assert_eq!(time.as_secs(), 0);
491		assert_eq!(time.as_millis(), 999);
492
493		let time = Time::from_millis(1000).unwrap();
494		assert_eq!(time.as_secs(), 1);
495		assert_eq!(time.as_millis(), 1000);
496
497		let time = Time::from_millis(1001).unwrap();
498		assert_eq!(time.as_secs(), 1);
499		assert_eq!(time.as_millis(), 1001);
500	}
501
502	#[test]
503	fn test_large_values() {
504		// Test with large but valid values
505		let large_secs = 1_000_000_000u64; // ~31 years
506		let time = Time::from_secs(large_secs).unwrap();
507		assert_eq!(time.as_secs(), large_secs);
508	}
509
510	#[test]
511	fn test_new() {
512		let time = Time::new(5000); // 5000 in the current scale (millis)
513		assert_eq!(time.as_millis(), 5000);
514		assert_eq!(time.as_secs(), 5);
515	}
516
517	#[test]
518	fn test_new_u64() {
519		let time = Time::new_u64(5000).unwrap();
520		assert_eq!(time.as_millis(), 5000);
521	}
522
523	#[test]
524	fn test_ordering() {
525		let a = Time::from_secs(1).unwrap();
526		let b = Time::from_secs(2).unwrap();
527		assert!(a < b);
528		assert!(b > a);
529		assert_eq!(a, a);
530	}
531
532	#[test]
533	fn test_unchecked_variants() {
534		let time = Time::from_secs_unchecked(5);
535		assert_eq!(time.as_secs(), 5);
536
537		let time = Time::from_millis_unchecked(5000);
538		assert_eq!(time.as_millis(), 5000);
539
540		let time = Time::from_micros_unchecked(5_000_000);
541		assert_eq!(time.as_micros(), 5_000_000);
542
543		let time = Time::from_nanos_unchecked(5_000_000_000);
544		assert_eq!(time.as_nanos(), 5_000_000_000);
545
546		let time = Time::from_scale_unchecked(5000, 1000);
547		assert_eq!(time.as_millis(), 5000);
548	}
549
550	#[test]
551	fn test_as_scale() {
552		let time = Time::from_secs(1).unwrap();
553		// 1 second in scale 1000 = 1000
554		assert_eq!(time.as_scale(1000), 1000);
555		// 1 second in scale 1 = 1
556		assert_eq!(time.as_scale(1), 1);
557		// 1 second in scale 1_000_000 = 1_000_000
558		assert_eq!(time.as_scale(1_000_000), 1_000_000);
559	}
560
561	#[test]
562	fn test_convert_to_finer() {
563		// Convert from milliseconds to microseconds (coarser to finer)
564		type TimeInMillis = Timescale<1_000>;
565		type TimeInMicros = Timescale<1_000_000>;
566
567		let time_millis = TimeInMillis::from_millis(5000).unwrap();
568		let time_micros: TimeInMicros = time_millis.convert().unwrap();
569
570		assert_eq!(time_micros.as_millis(), 5000);
571		assert_eq!(time_micros.as_micros(), 5_000_000);
572	}
573
574	#[test]
575	fn test_convert_to_coarser() {
576		// Convert from milliseconds to seconds (finer to coarser)
577		type TimeInMillis = Timescale<1_000>;
578		type TimeInSeconds = Timescale<1>;
579
580		let time_millis = TimeInMillis::from_millis(5000).unwrap();
581		let time_secs: TimeInSeconds = time_millis.convert().unwrap();
582
583		assert_eq!(time_secs.as_secs(), 5);
584		assert_eq!(time_secs.as_millis(), 5000);
585	}
586
587	#[test]
588	fn test_convert_precision_loss() {
589		// Converting 1234 millis to seconds loses precision
590		type TimeInMillis = Timescale<1_000>;
591		type TimeInSeconds = Timescale<1>;
592
593		let time_millis = TimeInMillis::from_millis(1234).unwrap();
594		let time_secs: TimeInSeconds = time_millis.convert().unwrap();
595
596		// 1234 millis = 1.234 seconds, rounds down to 1 second
597		assert_eq!(time_secs.as_secs(), 1);
598		assert_eq!(time_secs.as_millis(), 1000); // Lost 234 millis
599	}
600
601	#[test]
602	fn test_convert_roundtrip() {
603		// Converting to finer and back should preserve value
604		type TimeInMillis = Timescale<1_000>;
605		type TimeInMicros = Timescale<1_000_000>;
606
607		let original = TimeInMillis::from_millis(5000).unwrap();
608		let as_micros: TimeInMicros = original.convert().unwrap();
609		let back_to_millis: TimeInMillis = as_micros.convert().unwrap();
610
611		assert_eq!(original.as_millis(), back_to_millis.as_millis());
612	}
613
614	#[test]
615	fn test_convert_same_scale() {
616		// Converting to the same scale should be identity
617		type TimeInMillis = Timescale<1_000>;
618
619		let time = TimeInMillis::from_millis(5000).unwrap();
620		let converted: TimeInMillis = time.convert().unwrap();
621
622		assert_eq!(time.as_millis(), converted.as_millis());
623	}
624
625	#[test]
626	fn test_convert_microseconds_to_nanoseconds() {
627		type TimeInMicros = Timescale<1_000_000>;
628		type TimeInNanos = Timescale<1_000_000_000>;
629
630		let time_micros = TimeInMicros::from_micros(5_000_000).unwrap();
631		let time_nanos: TimeInNanos = time_micros.convert().unwrap();
632
633		assert_eq!(time_nanos.as_micros(), 5_000_000);
634		assert_eq!(time_nanos.as_nanos(), 5_000_000_000);
635	}
636
637	#[test]
638	fn test_convert_custom_scales() {
639		// Test with unusual custom scales
640		type TimeScale60 = Timescale<60>; // 60Hz
641		type TimeScale90 = Timescale<90>; // 90Hz
642
643		let time60 = TimeScale60::from_scale(120, 60).unwrap(); // 2 seconds at 60Hz
644		let time90: TimeScale90 = time60.convert().unwrap();
645
646		// Both should represent 2 seconds
647		assert_eq!(time60.as_secs(), 2);
648		assert_eq!(time90.as_secs(), 2);
649	}
650
651	#[test]
652	fn test_debug_format_units() {
653		// Test that Debug chooses appropriate units based on value
654
655		// Milliseconds that are clean seconds
656		let t = Time::from_millis(100000).unwrap();
657		assert_eq!(format!("{:?}", t), "100s");
658
659		let t = Time::from_millis(1000).unwrap();
660		assert_eq!(format!("{:?}", t), "1s");
661
662		// Milliseconds that are clean milliseconds
663		let t = Time::from_millis(100).unwrap();
664		assert_eq!(format!("{:?}", t), "100ms");
665
666		let t = Time::from_millis(5500).unwrap();
667		assert_eq!(format!("{:?}", t), "5500ms");
668
669		// Zero
670		let t = Time::ZERO;
671		assert_eq!(format!("{:?}", t), "0s");
672
673		// Test with microsecond-scale time
674		type TimeMicros = Timescale<1_000_000>;
675		let t = TimeMicros::from_micros(1500).unwrap();
676		assert_eq!(format!("{:?}", t), "1500µs");
677
678		let t = TimeMicros::from_micros(1000).unwrap();
679		assert_eq!(format!("{:?}", t), "1ms");
680	}
681}