extern crate alloc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct Time {
pub sec: i32,
pub nanosec: u32,
}
impl Time {
pub const ZERO: Self = Self { sec: 0, nanosec: 0 };
pub const INVALID: Self = Self {
sec: -1,
nanosec: 0xFFFF_FFFF,
};
pub const INFINITE: Self = Self {
sec: 0x7FFF_FFFF,
nanosec: 0xFFFF_FFFE,
};
#[must_use]
pub const fn is_zero(&self) -> bool {
self.sec == 0 && self.nanosec == 0
}
#[must_use]
pub const fn new(sec: i32, nanosec: u32) -> Self {
Self { sec, nanosec }
}
#[must_use]
pub const fn is_invalid(&self) -> bool {
self.sec == -1 && self.nanosec == 0xFFFF_FFFF
}
#[must_use]
pub const fn is_infinite(&self) -> bool {
self.sec == 0x7FFF_FFFF && self.nanosec == 0xFFFF_FFFE
}
#[must_use]
pub const fn seconds(&self) -> i32 {
self.sec
}
#[must_use]
pub const fn nanoseconds(&self) -> u32 {
self.nanosec
}
#[must_use]
pub fn add_duration(self, d: Duration) -> Self {
let total_ns = u64::from(self.nanosec) + u64::from(d.nanosec);
let extra_sec = (total_ns / 1_000_000_000) as i32;
let nanosec = (total_ns % 1_000_000_000) as u32;
Self {
sec: self.sec.saturating_add(d.sec).saturating_add(extra_sec),
nanosec,
}
}
#[must_use]
pub const fn from_millis(ms: i64) -> Self {
let sec = (ms / 1000) as i32;
let nanosec = ((ms % 1000) * 1_000_000) as u32;
Self { sec, nanosec }
}
#[must_use]
pub const fn as_millis(&self) -> i64 {
(self.sec as i64) * 1000 + (self.nanosec as i64) / 1_000_000
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct Duration {
pub sec: i32,
pub nanosec: u32,
}
impl Duration {
pub const ZERO: Self = Self { sec: 0, nanosec: 0 };
pub const INFINITE: Self = Self {
sec: 0x7FFF_FFFF,
nanosec: 0xFFFF_FFFF,
};
#[must_use]
pub const fn new(sec: i32, nanosec: u32) -> Self {
Self { sec, nanosec }
}
#[must_use]
pub const fn is_infinite(&self) -> bool {
self.sec == 0x7FFF_FFFF && self.nanosec == 0xFFFF_FFFF
}
#[must_use]
pub const fn is_zero(&self) -> bool {
self.sec == 0 && self.nanosec == 0
}
#[must_use]
pub fn to_core(self) -> core::time::Duration {
if self.is_infinite() {
core::time::Duration::MAX
} else if self.sec < 0 {
core::time::Duration::ZERO
} else {
core::time::Duration::new(self.sec as u64, self.nanosec)
}
}
#[must_use]
pub fn from_core(d: core::time::Duration) -> Self {
let sec = i32::try_from(d.as_secs()).unwrap_or(i32::MAX);
Self {
sec,
nanosec: d.subsec_nanos(),
}
}
#[must_use]
pub const fn seconds(&self) -> i32 {
self.sec
}
#[must_use]
pub const fn nanoseconds(&self) -> u32 {
self.nanosec
}
#[must_use]
pub fn add_duration(self, d: Duration) -> Self {
let total_ns = u64::from(self.nanosec) + u64::from(d.nanosec);
let extra_sec = (total_ns / 1_000_000_000) as i32;
let nanosec = (total_ns % 1_000_000_000) as u32;
Self {
sec: self.sec.saturating_add(d.sec).saturating_add(extra_sec),
nanosec,
}
}
#[must_use]
pub const fn from_millis(ms: i64) -> Self {
let sec = (ms / 1000) as i32;
let nanosec = ((ms % 1000) * 1_000_000) as u32;
Self { sec, nanosec }
}
#[must_use]
pub const fn as_millis(&self) -> i64 {
(self.sec as i64) * 1000 + (self.nanosec as i64) / 1_000_000
}
}
#[must_use]
#[cfg(feature = "std")]
pub fn get_current_time() -> Time {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| Time {
sec: i32::try_from(d.as_secs()).unwrap_or(i32::MAX),
nanosec: d.subsec_nanos(),
})
.unwrap_or(Time::INVALID)
}
#[must_use]
#[cfg(not(feature = "std"))]
pub fn get_current_time() -> Time {
Time::INVALID
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn time_zero_sentinel() {
assert!(Time::ZERO.is_zero());
assert!(!Time::ZERO.is_invalid());
assert!(!Time::ZERO.is_infinite());
assert_eq!(Time::ZERO, Time::default());
}
#[test]
fn time_invalid_sentinel() {
assert!(Time::INVALID.is_invalid());
assert!(!Time::INVALID.is_infinite());
let zero = Time::default();
assert!(!zero.is_invalid());
}
#[test]
fn time_infinite_sentinel() {
assert!(Time::INFINITE.is_infinite());
assert!(!Time::INFINITE.is_invalid());
}
#[test]
fn duration_zero_and_infinite_sentinels() {
assert!(Duration::ZERO.is_zero());
assert!(Duration::INFINITE.is_infinite());
assert!(!Duration::INFINITE.is_zero());
}
#[test]
fn duration_to_core_maps_infinite_to_max() {
assert_eq!(Duration::INFINITE.to_core(), core::time::Duration::MAX);
}
#[test]
fn duration_to_core_preserves_value() {
let d = Duration::new(5, 500_000_000);
let c = d.to_core();
assert_eq!(c.as_secs(), 5);
assert_eq!(c.subsec_nanos(), 500_000_000);
}
#[test]
fn duration_from_core_roundtrip() {
let c = core::time::Duration::new(42, 123_456_789);
let d = Duration::from_core(c);
assert_eq!(d.sec, 42);
assert_eq!(d.nanosec, 123_456_789);
assert_eq!(d.to_core(), c);
}
#[test]
fn duration_negative_sec_maps_to_zero() {
let d = Duration::new(-1, 0);
assert_eq!(d.to_core(), core::time::Duration::ZERO);
}
#[cfg(feature = "std")]
#[test]
fn get_current_time_is_recent() {
let t = get_current_time();
assert!(!t.is_invalid());
assert!(t.sec > 1_700_000_000);
}
#[test]
fn time_seconds_and_nanoseconds_accessors() {
let t = Time::new(7, 250_000_000);
assert_eq!(t.seconds(), 7);
assert_eq!(t.nanoseconds(), 250_000_000);
}
#[test]
fn duration_seconds_and_nanoseconds_accessors() {
let d = Duration::new(3, 100_000_000);
assert_eq!(d.seconds(), 3);
assert_eq!(d.nanoseconds(), 100_000_000);
}
#[test]
fn time_add_duration_carries_seconds() {
let t = Time::new(10, 800_000_000);
let inc = t.add_duration(Duration::new(2, 500_000_000));
assert_eq!(inc.seconds(), 13);
assert_eq!(inc.nanoseconds(), 300_000_000);
}
#[test]
fn time_from_and_as_millis_roundtrip() {
let t = Time::from_millis(12_345);
assert_eq!(t.seconds(), 12);
assert_eq!(t.nanoseconds(), 345_000_000);
assert_eq!(t.as_millis(), 12_345);
}
#[test]
fn duration_add_duration_carries_seconds() {
let d = Duration::new(5, 700_000_000);
let inc = d.add_duration(Duration::new(1, 600_000_000));
assert_eq!(inc.seconds(), 7);
assert_eq!(inc.nanoseconds(), 300_000_000);
}
#[test]
fn duration_from_and_as_millis_roundtrip() {
let d = Duration::from_millis(2_500);
assert_eq!(d.seconds(), 2);
assert_eq!(d.nanoseconds(), 500_000_000);
assert_eq!(d.as_millis(), 2_500);
}
}