tea_time/
time.rs

1use chrono::{NaiveTime, Timelike};
2use tea_error::{TError, TResult};
3
4use crate::convert::*;
5/// Represents a time of day with nanosecond precision.
6///
7/// This struct is a wrapper around an `i64` value representing the number of nanoseconds
8/// since midnight. It provides various methods for creating, manipulating, and converting
9/// time values.
10#[repr(transparent)]
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct Time(pub i64);
14
15impl Time {
16    /// Creates a new `Time` instance from the given number of nanoseconds.
17    #[inline]
18    pub const fn from_i64(nanos: i64) -> Self {
19        Self(nanos)
20    }
21
22    /// Checks if the time is "not a time" (NAT).
23    ///
24    /// Returns `true` if the internal value is `i64::MIN`, which represents an invalid time.
25    #[inline]
26    pub const fn is_nat(&self) -> bool {
27        self.0 == i64::MIN
28    }
29
30    /// Checks if the time is a valid time (not NAT).
31    ///
32    /// Returns `true` if the internal value is not `i64::MIN`.
33    #[inline]
34    pub const fn is_not_nat(&self) -> bool {
35        self.0 != i64::MIN
36    }
37
38    /// Returns a `Time` instance representing "not a time" (NAT).
39    #[inline]
40    pub const fn nat() -> Self {
41        Self(i64::MIN)
42    }
43
44    /// Converts the `Time` instance to its raw `i64` value.
45    #[inline]
46    pub const fn into_i64(self) -> i64 {
47        self.0
48    }
49
50    /// Creates a `Time` instance from a `chrono::NaiveTime`.
51    #[inline]
52    pub fn from_cr(cr: &NaiveTime) -> Self {
53        Self(cr.num_seconds_from_midnight() as i64 * NANOS_PER_SEC + cr.nanosecond() as i64)
54    }
55
56    /// Converts the `Time` instance to a `chrono::NaiveTime`, if valid.
57    #[inline]
58    pub const fn as_cr(&self) -> Option<NaiveTime> {
59        use NANOS_PER_SEC;
60        let secs = self.0 / NANOS_PER_SEC;
61        let nanos = self.0 % NANOS_PER_SEC;
62        NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nanos as u32)
63    }
64
65    /// Creates a `Time` instance from hours, minutes, and seconds.
66    #[inline]
67    pub const fn from_hms(hour: i64, min: i64, sec: i64) -> Self {
68        let secs = hour * SECS_PER_HOUR + min * SECS_PER_MINUTE + sec;
69        let nanos = secs * NANOS_PER_SEC;
70        Self(nanos)
71    }
72
73    /// Creates a `Time` instance from hours, minutes, seconds, and milliseconds.
74    #[inline]
75    pub const fn from_hms_milli(hour: i64, min: i64, sec: i64, milli: i64) -> Self {
76        let mut time = Self::from_hms(hour, min, sec);
77        let nanos = milli * NANOS_PER_MILLI;
78        time.0 += nanos;
79        time
80    }
81
82    /// Creates a `Time` instance from hours, minutes, seconds, and microseconds.
83    #[inline]
84    pub const fn from_hms_micro(hour: i64, min: i64, sec: i64, micro: i64) -> Self {
85        let mut time = Self::from_hms(hour, min, sec);
86        let nanos = micro * NANOS_PER_MICRO;
87        time.0 += nanos;
88        time
89    }
90
91    /// Creates a `Time` instance from hours, minutes, seconds, and nanoseconds.
92    #[inline]
93    pub const fn from_hms_nano(hour: i64, min: i64, sec: i64, nano: i64) -> Self {
94        let mut time = Self::from_hms(hour, min, sec);
95        time.0 += nano;
96        time
97    }
98
99    /// Creates a `Time` instance from the number of seconds since midnight and additional nanoseconds.
100    #[inline]
101    pub const fn from_num_seconds_from_midnight(secs: i64, nano: i64) -> Self {
102        let nanos = secs * NANOS_PER_SEC + nano;
103        Self(nanos)
104    }
105
106    /// Parses a string into a `Time` instance using an optional format string.
107    ///
108    /// If no format is provided, it attempts to parse using the default format.
109    #[inline]
110    pub fn parse(s: &str, fmt: Option<&str>) -> TResult<Self> {
111        let naive_time = if let Some(fmt) = fmt {
112            NaiveTime::parse_from_str(s, fmt)
113        } else {
114            s.parse()
115        }
116        .map_err(|e| TError::ParseError(e.to_string().into()))?;
117        Ok(Time(
118            naive_time.num_seconds_from_midnight() as i64 * NANOS_PER_SEC
119                + naive_time.nanosecond() as i64,
120        ))
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_from_hms() {
130        let time = Time::from_hms(12, 34, 56);
131        assert_eq!(time.0, (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC);
132    }
133
134    #[test]
135    fn test_from_hms_milli() {
136        let time = Time::from_hms_milli(12, 34, 56, 789);
137        assert_eq!(
138            time.0,
139            (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC + 789 * NANOS_PER_MILLI
140        );
141    }
142
143    #[test]
144    fn test_from_hms_micro() {
145        let time = Time::from_hms_micro(12, 34, 56, 789);
146        assert_eq!(
147            time.0,
148            (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC + 789 * NANOS_PER_MICRO
149        );
150    }
151
152    #[test]
153    fn test_from_hms_nano() {
154        let time = Time::from_hms_nano(12, 34, 56, 789);
155        assert_eq!(time.0, (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC + 789);
156    }
157
158    #[test]
159    fn test_from_num_seconds_from_midnight() {
160        let time = Time::from_num_seconds_from_midnight(45296, 789);
161        assert_eq!(time.0, 45296 * NANOS_PER_SEC + 789);
162    }
163
164    #[test]
165    fn test_parse() {
166        let time = Time::parse("12:34:56", None).unwrap();
167        assert_eq!(time.0, (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC);
168
169        let time = Time::parse("12:34:56.789", Some("%H:%M:%S%.3f")).unwrap();
170        assert_eq!(
171            time.0,
172            (12 * 3600 + 34 * 60 + 56) * NANOS_PER_SEC + 789 * NANOS_PER_MILLI
173        );
174
175        assert!(Time::parse("invalid", None).is_err());
176    }
177}