rabbit-time 0.1.2

Rust implementations of Neralie and Arvelie time formats.
Documentation
use chrono::{NaiveTime, TimeDelta};
use std::fmt::{Display, Formatter};
use std::time::Duration;

/// Represents a duration of time on the [Neralie](https://wiki.xxiivv.com/site/neralie.html) clock.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct NeralieTime {
    /// Number of beats in the duration.
    /// A beat is equivalent to 86.4 seconds.
    /// There are 1000 beats in a day.
    pub beats: i64,
    /// Number of pulses in the duration.
    /// A pulse is 1/1000 of a beat.
    pub pulses: i64,
}
impl From<Duration> for NeralieTime {
    fn from(value: Duration) -> Self {
        let total_beats = value.as_secs_f64() / 86.4;
        let total_pulses = value.as_secs_f64() / (86.4 / 1000.0);
        let beats = total_beats.floor() as i64;
        let pulses = (total_pulses - total_beats.floor() * 1000.0).ceil() as i64;
        Self { beats, pulses }
    }
}
impl From<NeralieTime> for Duration {
    fn from(val: NeralieTime) -> Self {
        Duration::from_secs_f64(
            ((val.beats as f64 + (val.pulses as f64 / 1000.0)) * 86.4).round_ties_even(),
        )
    }
}
impl From<TimeDelta> for NeralieTime {
    fn from(value: TimeDelta) -> Self {
        let total_beats = value.as_seconds_f64() / 86.4;
        let total_pulses = value.as_seconds_f64() / (86.4 / 1000.0);
        let beats = total_beats.floor() as i64;
        let pulses = (total_pulses - total_beats.floor() * 1000.0).ceil() as i64;
        Self { beats, pulses }
    }
}
impl From<NeralieTime> for TimeDelta {
    fn from(value: NeralieTime) -> Self {
        TimeDelta::from_std(Duration::from(value)).unwrap()
    }
}
impl From<NaiveTime> for NeralieTime {
    fn from(value: chrono::NaiveTime) -> Self {
        value.signed_duration_since(NaiveTime::MIN).into()
    }
}
impl From<NeralieTime> for NaiveTime {
    fn from(value: NeralieTime) -> Self {
        let duration = Duration::from(value);
        let total_milli = duration.as_millis();
        let total_sec = duration.as_secs();
        let total_min = total_sec / 60;
        let total_hour = total_min / 60;
        let hour = total_hour as u32;
        let min = (total_min - total_hour * 60) as u32;
        let sec = (total_sec - total_min * 60) as u32;
        let milli = (total_milli - (total_sec as u128 * 1000)) as u32;
        NaiveTime::from_hms_milli_opt(hour, min, sec, milli).unwrap()
    }
}
impl Display for NeralieTime {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:0>3}:{:0>3}", self.beats, self.pulses)
    }
}