rstime 0.1.0

A zero-dependency Rust time library providing date, time, datetime types with formatting, parsing, Unix timestamps, and clock functionality.
Documentation
//! Time module
//!
//! Provides `Time` type with validation, 12/24-hour conversion,
//! and arithmetic with [`Duration`].

use crate::duration::Duration;
use std::fmt;

/// A time of day with millisecond precision
///
/// Supports validation, 12-hour conversion, and arithmetic with
/// [`Duration`] (with cross-midnight wrapping).
///
/// # Examples
///
/// ```rust
/// use rstime::Time;
///
/// let t = Time::new(14, 5, 9, 37);
/// assert!(t.is_valid());
/// assert_eq!(t.hour, 14);
/// assert_eq!(t.minute, 5);
/// assert_eq!(t.second, 9);
/// assert_eq!(t.millisecond, 37);
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Time {
    /// Hour (0-23)
    pub hour: u8,
    /// Minute (0-59)
    pub minute: u8,
    /// Second (0-59)
    pub second: u8,
    /// Millisecond (0-999)
    pub millisecond: u16,
}

impl Time {
    /// Midnight (00:00:00.000)
    pub const MIDNIGHT: Time = Time {
        hour: 0,
        minute: 0,
        second: 0,
        millisecond: 0,
    };

    /// Noon (12:00:00.000)
    pub const NOON: Time = Time {
        hour: 12,
        minute: 0,
        second: 0,
        millisecond: 0,
    };

    /// Maximum time (23:59:59.999)
    pub const MAX: Time = Time {
        hour: 23,
        minute: 59,
        second: 59,
        millisecond: 999,
    };

    /// Create a new time with millisecond precision
    pub fn new(hour: u8, minute: u8, second: u8, millisecond: u16) -> Self {
        Time {
            hour,
            minute,
            second,
            millisecond,
        }
    }

    /// Create a time from hours, minutes, and seconds (no milliseconds)
    pub fn from_hms(hour: u8, minute: u8, second: u8) -> Self {
        Time {
            hour,
            minute,
            second,
            millisecond: 0,
        }
    }

    /// Check if the time is valid
    ///
    /// ```rust
    /// use rstime::Time;
    ///
    /// assert!(Time::new(14, 5, 9, 0).is_valid());
    /// assert!(!Time::new(24, 0, 0, 0).is_valid());
    /// assert!(!Time::new(0, 60, 0, 0).is_valid());
    /// ```
    pub fn is_valid(&self) -> bool {
        self.hour < 24 && self.minute < 60 && self.second < 60 && self.millisecond < 1000
    }

    /// Total time in seconds (since midnight)
    pub fn total_seconds(&self) -> u32 {
        self.hour as u32 * 3600 + self.minute as u32 * 60 + self.second as u32
    }

    /// Total time in milliseconds (since midnight)
    pub fn total_millis(&self) -> u32 {
        self.total_seconds() * 1000 + self.millisecond as u32
    }

    /// Convert to 12-hour clock representation
    ///
    /// Returns `(hour, is_pm)` where hour is 1-12.
    ///
    /// ```rust
    /// use rstime::Time;
    ///
    /// let t = Time::from_hms(14, 0, 0);
    /// assert_eq!(t.hour12(), (2, true));
    ///
    /// let midnight = Time::MIDNIGHT;
    /// assert_eq!(midnight.hour12(), (12, false));
    /// ```
    pub fn hour12(&self) -> (u8, bool) {
        if self.hour == 0 {
            (12, false)
        } else if self.hour < 12 {
            (self.hour, false)
        } else if self.hour == 12 {
            (12, true)
        } else {
            (self.hour - 12, true)
        }
    }

    /// Returns `true` if the time is PM (hour >= 12)
    pub fn is_pm(&self) -> bool {
        self.hour >= 12
    }
}

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{:02}:{:02}:{:02}",
            self.hour, self.minute, self.second
        )
    }
}

impl std::ops::Add<Duration> for Time {
    type Output = Time;
    /// Add a duration, wrapping around midnight
    ///
    /// ```rust
    /// use rstime::{Time, Duration};
    ///
    /// let t = Time::from_hms(23, 0, 0);
    /// let t2 = t + Duration::HOUR * 2;
    /// ```
    fn add(self, delta: Duration) -> Time {
        let total_ms = self.total_millis() as i64 + delta.total_millis();
        let ms_per_day: i64 = 86_400_000;
        let mut ms = total_ms % ms_per_day;
        if ms < 0 {
            ms += ms_per_day;
        }
        Time {
            hour: (ms / 3_600_000 % 24) as u8,
            minute: (ms / 60_000 % 60) as u8,
            second: (ms / 1000 % 60) as u8,
            millisecond: (ms % 1000) as u16,
        }
    }
}

impl std::ops::Sub<Duration> for Time {
    type Output = Time;
    /// Subtract a duration, wrapping around midnight
    fn sub(self, delta: Duration) -> Time {
        self + Duration::new(-delta.secs, -delta.nanos)
    }
}

impl std::ops::Sub<Time> for Time {
    type Output = Duration;
    /// Calculate the difference between two times
    fn sub(self, rhs: Time) -> Duration {
        let diff_ms =
            self.total_millis() as i64 - rhs.total_millis() as i64;
        Duration::from_millis(diff_ms)
    }
}